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

9

У меня есть приложение, которое читает файл. Давайте назовем это имя процесса и файл ~ / .configuration . Когда имя процесса запускается, оно всегда читает ~ / .configuration и не может быть настроено иначе. Существуют также другие приложения, которые используют «~ / .configuration» до и после, но не во время выполнения имени процесса .

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

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

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

РЕДАКТИРОВАТЬ: При поиске ~ / .configuration в исполняемом файле с именем процесса я обнаружил, что он пытался прочитать другое имя файла прямо перед чтением ~ / .configuration . Задача решена.

Александр
источник
2
Это может быть сделано через LD_PRELOADили FUSE, как с этой несколько похожей проблемой , но я не знаю ни одной существующей реализации.
Жиль "ТАК ... перестать быть злым"

Ответы:

6

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

Это также можно сделать chroot, но unshareболее адаптировано к вашему случаю.

Например chroot, вам нужен привилегированный пользователь, привилегированный для unshareпространства имен mount.

Итак, скажем, у вас есть ~/.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 может иметь непредвиденные последствия (т. Е. Выходные файлы могут оказаться не в том месте).

EightBitTony
источник
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);
    }
}
ломаться
источник