Здійснення процесу зчитування іншого файлу для того ж імені файлу


9

У мене є програма, яка читає файл. Назвемо це ім'я процесу та файл ~ / .configuration . Коли ім'я процесу запущено, воно завжди зчитує ~ / .configuration і не може бути налаштовано інакше. Є й інші програми , які покладаються на «~ / .configuration», до і після, але не в той час як ProcessName працює.

Обгортання імені процесу в скрипті, який замінює вміст ~ / .configuration - це варіант, але у мене нещодавно був відключений живлення (в той час як вміст було замінено), де я втратив попередній вміст згаданого файлу, тому це не бажано.

Чи існує спосіб (можливо, використовувати щось віддалене, пов’язане з LD_DEBUG=files processname?) Для того, щоб обдурити процес читання різного вмісту, коли він намагається прочитати певний файл? Пошук і заміна імені файлу у виконуваному файлі трохи надто інвазивний, але також повинен працювати.

Я знаю, що можна написати модуль ядра, який приймає open()виклик ( https://news.ycombinator.com/item?id=2972958 ), але чи є простіший чи чистіший спосіб?

EDIT: Під час пошуку ~ / .configuration у виконаному імені процесу я виявив, що він намагався прочитати інше ім’я файлу безпосередньо перед читанням ~ / .configuration . Проблема вирішена.


2
Це можна зробити через LD_PRELOADабо FUSE, як у цій дещо подібній проблемі , але я не знаю жодної існуючої реалізації.
Жил "ТАК - перестань бути злим"

Відповіді:


6

В останніх версіях Linux ви можете скасувати доступ до простору імен для кріплення . Тобто ви можете запускати процеси, які переглядають віртуальну файлову систему по-різному (файлові системи монтуються по-різному).

Це також можна зробити chroot, але unshareце більше адаптується до вашого випадку.

Мовляв chroot, вам потрібен надрукований користувач на unshareпросторі імен.

Отже, скажіть, у вас є ~/.configurationі ~/.configuration-for-that-cmdфайли.

Ви можете запустити процес, для якого ~/.configurationнасправді є прив'язка ~/.configuration-for-that-cmdтам, і виконувати that-cmdтам.

подібно до:

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

that-cmdі всі його нащадкові процеси побачать інше ~/.configuration.

that-cmdвище буде запускатися як root, використовувати, sudo -u another-user that-cmdякщо потрібно запустити як інший користувач .


Я думаю, що ваше рішення є, мабуть, кращим з двох даних, що надані до цього часу (і з огляду на те, що відбувається в ОП, перенаправлення на основі часу або результатів процесу виявлення здається мені непростим), але я думаю, що вони хочуть одного файлу показувати по-різному. Таким чином, вони, ймовірно, повинні були б встановити його в іншому місці і використовувати симпосилання, розраховуючи на різні точки монтажу, щоб діяти як фактична точка перенаправлення.
Братчлі

1
@JoelDavis, ви можете прив’язати-змонтувати будь-який файл, не лише каталог.
Стефан Шазелас

TIL. Чи є в цьому контролі безпеки? Я спробував це за допомогою підкаталогу, де я знаходився (прив'язування з / etc / fstab), і він повернув "Не каталог", але я зробив майже те саме, що під цим, /testі він працював без проблем.
Братчлі

Насправді, нм я бачу різницю, я зробив це в каталозі вперше, а в наступний файл. Я припускав, що він буде просто перенаправляти / змінювати VFS у відповідних випадках. Все, дякую за нову іграшку.
Братчлі

3

М'які посилання.

Створіть два конфігураційні файли та вкажіть на один з них м'яким посиланням більшу частину часу, але змініть м'яке посилання, щоб вказати на інше, коли працює спеціальний додаток.

(Я знаю, що це жахливий злом, але це трохи надійніше, ніж зміна вмісту файлів).

Або маніпулюйте $ HOME.

У скрипті, який запускає роздратований процес, встановіть, що $ HOME є чимось у звичайній папці $ HOME, і тоді ваша програма повинна використовувати конфігураційний файл, розташований там (перевірений і працює для основних команд оболонки, ~ розширюється до $ HOME).

Залежно від того, що ще робить процес, зміна $ HOME може мати непередбачувані наслідки (тобто вихідні файли можуть опинитися в неправильному місці).


1

Це можна зробити за допомогою фокусу LD_PRELOAD . Ось реалізація, яка відображає шляхи, починаючи з певного префікса до іншого місця. Код також є на github .

Наприклад, ви можете підробити існування файлу, /etc/не використовуючи root. Це було необхідно для клієнта owncloud, який відмовляється працювати, коли файл /etc/ownCloud/sync-exclude.listне існує.

Він працює, заміняючи функції open()та open64()функції для відображення однієї директорії в іншу, наприклад, всі open()виклики, на які /etc/ownCloud/...можна перенаправити /home/user1/.etc/ownCloud/....

Просто відрегулюйте path_map, потім скомпілюйте та запустіть програму з попередньо завантаженою lib:

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

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

Вихідний код 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);
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.