Tạo một quá trình đọc một tệp khác nhau cho cùng một tên tệp


9

Tôi có một ứng dụng đọc một tập tin. Hãy gọi nó là processname và tệp ~ / .configuration . Khi processname chạy, nó luôn đọc ~ / .configuration và không thể được cấu hình khác. Ngoài ra còn có các ứng dụng khác dựa vào "~ / .configuration", trước và sau, nhưng không phải trong khi processname đang chạy.

Gói tên quy trình trong tập lệnh thay thế nội dung của ~ / .configuration là một tùy chọn, nhưng gần đây tôi bị mất điện (trong khi nội dung bị tráo đổi), trong đó tôi bị mất nội dung trước đó của tệp đã nói, vì vậy điều này không được mong muốn.

Có cách nào (có lẽ sử dụng một cái gì đó liên quan đến xa LD_DEBUG=files processname?) Để đánh lừa một quá trình đọc các nội dung khác nhau khi nó cố đọc một tệp cụ thể? Tìm kiếm và thay thế tên tệp trong tệp thực thi là một chút quá xâm lấn, nhưng cũng nên hoạt động.

Tôi biết có thể viết mô-đun hạt nhân đảm nhận open()cuộc gọi ( https://news.ycombinator.com/item?id=2972958 ), nhưng có cách nào đơn giản hơn hoặc sạch hơn không?

EDIT: Khi tìm kiếm ~ / .configuration trong tệp thực thi processname tôi phát hiện ra rằng nó đã cố đọc tên tệp khác ngay trước khi đọc ~ / .configuration . Vấn đề được giải quyết.


2
Điều này có thể được thực hiện thông qua LD_PRELOADhoặc FUSE, giống như với vấn đề hơi tương tự này , nhưng tôi không biết bất kỳ triển khai nào hiện có.
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


6

Trong các phiên bản gần đây của Linux, bạn có thể hủy bỏ không gian tên mount . Nghĩa là, bạn có thể bắt đầu các quy trình xem hệ thống tệp ảo khác nhau (với các hệ thống tệp được gắn khác nhau).

Điều đó cũng có thể được thực hiện với chroot, nhưng unsharephù hợp hơn với trường hợp của bạn.

Giống như chroot, bạn cần siêu người dùng unshaređược đặt riêng vào không gian tên mount.

Vì vậy, nói rằng bạn có ~/.configuration~/.configuration-for-that-cmdcác tập tin.

Bạn có thể bắt đầu một quá trình ~/.configurationthực sự gắn kết ~/.configuration-for-that-cmdtrong đó và thực hiện that-cmdở đó.

giống:

sudo unshare -m sh -c "
   mount --bind '$HOME/.configuration-for-that-cmd' \
                '$HOME/.configuration' &&
     exec that-cmd"

that-cmdvà tất cả các quá trình hậu duệ của nó sẽ thấy một sự khác biệt ~/.configuration.

that-cmdở trên sẽ chạy như root, sử dụng sudo -u another-user that-cmdnếu nó cần chạy như một người dùng khác .


Tôi nghĩ rằng giải pháp của bạn có lẽ là tốt hơn cả hai được đưa ra cho đến nay (và đưa ra những gì OP đang theo sau, chuyển hướng dựa trên thời gian hoặc kết quả của quá trình phát hiện có vẻ iffy đối với tôi), nhưng tôi nghĩ họ muốn một tệp duy nhất để hiển thị khác nhau. Vì vậy, họ có thể phải gắn kết nó ở nơi khác và sử dụng một liên kết tượng trưng, ​​dựa trên các điểm gắn khác nhau để hoạt động như điểm chuyển hướng thực tế.
Bratchley

1
@JoelDavis, bạn có thể gắn kết bất kỳ tệp nào, không chỉ các thư mục.
Stéphane Chazelas

GẠCH Có kiểm soát an ninh với điều đó, mặc dù? Tôi đã thử nó bằng thư mục con nơi tôi đang ở (ràng buộc từ / etc / fstab) và nó trả về "Không phải là một thư mục" nhưng tôi đã làm khá nhiều thứ tương tự /testvà nó hoạt động mà không gặp vấn đề gì.
Bratchley

Trên thực tế, tôi có thể thấy sự khác biệt, lần đầu tiên tôi đã làm nó vào một thư mục và một tệp. Tôi đã giả định rằng nó sẽ chỉ chuyển hướng / sửa đổi VFS khi thích hợp. Dù sao, cảm ơn cho đồ chơi mới.
Bratchley

3

Liên kết mềm.

Tạo hai tệp cấu hình và trỏ đến một trong số chúng bằng một liên kết mềm hầu hết thời gian, nhưng thay đổi liên kết mềm để trỏ đến một tệp khác khi ứng dụng đặc biệt đang chạy.

(Tôi biết đây là một vụ hack khủng khiếp, nhưng nó đáng tin cậy hơn một chút so với việc thay đổi nội dung tệp).

Hoặc, thao túng $ HOME.

Trong tập lệnh bắt đầu quá trình gây phiền nhiễu, hãy đặt $ HOME thành một cái gì đó trong thư mục $ HOME thông thường và ứng dụng của bạn sẽ sử dụng tệp cấu hình nằm ở đó (đã thử nghiệm và hoạt động cho các lệnh shell cơ bản, ~ mở rộng thành $ HOME).

Tùy thuộc vào những gì khác mà quá trình thực hiện, việc thay đổi $ HOME có thể có những hậu quả không lường trước được (tức là các tệp đầu ra có thể kết thúc ở sai vị trí).


1

Bạn có thể làm điều này bằng cách sử dụng thủ thuật LD_PRELOAD . Đây là một triển khai ánh xạ các đường dẫn bắt đầu bằng một tiền tố cụ thể đến một vị trí khác. Mã này cũng có trên github .

Ví dụ: bạn có thể giả mạo sự tồn tại của một tệp /etc/mà không cần root. Này là cần thiết cho khách hàng owncloud mà từ chối làm việc khi các tập tin /etc/ownCloud/sync-exclude.listkhông tồn tại.

Nó hoạt động bằng cách ghi đè các hàm open()và các open64()hàm để ánh xạ một thư mục này sang thư mục khác, ví dụ như tất cả các open()lệnh gọi /etc/ownCloud/...có thể được chuyển hướng đến /home/user1/.etc/ownCloud/....

Chỉ cần điều chỉnh path_map, sau đó biên dịch và chạy chương trình của bạn với lib được tải sẵn:

gcc -std=c99 -Wall -shared -fPIC path-mapping.c -o path-mapping.so -ldl

LD_PRELOAD=/path/to/my/path-mapping.so someprogram

Mã nguồn của path-mapping.c:

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdarg.h>
#include <malloc.h>

// List of path pairs. Paths beginning with the first item will be
// translated by replacing the matching part with the second item.
static const char *path_map[][2] = {
    { "/etc/ownCloud/", "/home/user1/.etc/ownCloud/" },
};

__thread char *buffer = NULL;
__thread int buffer_size = -1;

typedef FILE* (*orig_fopen_func_type)(const char *path, const char *mode);
typedef int (*orig_open_func_type)(const char *pathname, int flags, ...);

static int starts_with(const char *str, const char *prefix) {
    return (strncmp(prefix, str, strlen(prefix)) == 0);
}

static char *get_buffer(int min_size) {
    int step = 63;
    if (min_size < 1) {
        min_size = 1;
    }
    if (min_size > buffer_size) {
        if (buffer != NULL) {
            free(buffer);
            buffer = NULL;
            buffer_size = -1;
        }
        buffer = malloc(min_size + step);
        if (buffer != NULL) {
            buffer_size = min_size + step;
        }
    }
    return buffer;
}

static const char *fix_path(const char *path)
{
    int count = (sizeof path_map) / (sizeof *path_map); // Array length
    for (int i = 0; i < count; i++) {
        const char *prefix = path_map[i][0];
        const char *replace = path_map[i][1];
        if (starts_with(path, prefix)) {
            const char *rest = path + strlen(prefix);
            char *new_path = get_buffer(strlen(path) + strlen(replace) - strlen(prefix));
            strcpy(new_path, replace);
            strcat(new_path, rest);
            printf("Mapped Path: %s  ==>  %s\n", path, new_path);
            return new_path;
        }
    }
    return path;
}


int open(const char *pathname, int flags, ...)
{
    const char *new_path = fix_path(pathname);

    orig_open_func_type orig_func;
    orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open");

    // If O_CREAT is used to create a file, the file access mode must be given.
    if (flags & O_CREAT) {
        va_list args;
        va_start(args, flags);
        int mode = va_arg(args, int);
        va_end(args);
        return orig_func(new_path, flags, mode);
    } else {
        return orig_func(new_path, flags);
    }
}

int open64(const char *pathname, int flags, ...)
{
    const char *new_path = fix_path(pathname);

    orig_open_func_type orig_func;
    orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open64");

    // If O_CREAT is used to create a file, the file access mode must be given.
    if (flags & O_CREAT) {
        va_list args;
        va_start(args, flags);
        int mode = va_arg(args, int);
        va_end(args);
        return orig_func(new_path, flags, mode);
    } else {
        return orig_func(new_path, flags);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.