Sprawienie, by proces odczytał inny plik dla tej samej nazwy pliku

9

Mam aplikację, która czyta plik. Nazwijmy to nazwa procesu i plik ~ / .configuracja . Po uruchomieniu nazwa procesu zawsze odczytuje konfigurację ~ / .configuracja i nie może być skonfigurowana inaczej. Istnieją również inne aplikacje, które polegają na „~ / .configuration”, przed i po, ale nie podczas działania nazwy procesu .

Zawijanie nazwy procesu w skrypcie, który zastępuje zawartość ~ / .configuracja, jest opcją, ale ostatnio miałem awarię zasilania (podczas gdy zawartość została zamieniona), w której straciłem poprzednią zawartość tego pliku, więc nie jest to pożądane.

Czy istnieje sposób (być może użycie czegoś odległego LD_DEBUG=files processname?), Aby oszukać proces czytania różnych treści podczas próby odczytania określonego pliku? Wyszukiwanie i zastępowanie nazwy pliku w pliku wykonywalnym jest nieco zbyt inwazyjne, ale powinno również działać.

Wiem, że można napisać moduł jądra, który przejmuje open()połączenie ( https://news.ycombinator.com/item?id=2972958 ), ale czy istnieje prostszy czy czystszy sposób?

EDYCJA: Podczas wyszukiwania ~ / .configuracji w pliku wykonywalnym nazwy procesu odkryłem, że próbował odczytać inną nazwę pliku tuż przed odczytaniem ~ / .configuracji . Problem rozwiązany.

Alexander
źródło
2
Można to zrobić za pomocą LD_PRELOADlub FUSE, jak w przypadku tego nieco podobnego problemu , ale nie znam żadnej istniejącej implementacji.
Gilles „SO- przestań być zły”

Odpowiedzi:

6

W ostatnich wersjach Linuksa, można cofnąć udostępnianie nazw zamontować . Oznacza to, że można uruchomić procesy, które inaczej wyświetlają wirtualny system plików (z odmiennie zamontowanymi systemami plików).

Można to również zrobić chroot, ale unsharejest bardziej dostosowane do twojej sprawy.

Jak chroottrzeba superużytkownika uprzywilejowane do unshareprzestrzeni nazw Górze.

Powiedzmy, że masz ~/.configurationi ~/.configuration-for-that-cmdpliki.

Możesz rozpocząć proces, dla którego ~/.configurationjest to naprawdę bind-mount ~/.configuration-for-that-cmd, i wykonać that-cmdtam.

lubić:

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

that-cmdi wszystkie jego procesy potomne będą wyglądały inaczej ~/.configuration.

that-cmdpowyżej będzie działać jako root, użyj, sudo -u another-user that-cmdjeśli trzeba uruchomić jako inny użytkownik .

Stéphane Chazelas
źródło
Myślę, że twoje rozwiązanie jest prawdopodobnie lepsze z dwóch podanych do tej pory (i biorąc pod uwagę cel OP, przekierowanie na podstawie czasu lub wyników procesu wykrywania wydaje mi się niewygodne), ale myślę, że chcą jednego pliku pokazać się inaczej. Prawdopodobnie musieliby zamontować go gdzie indziej i użyć dowiązania symbolicznego, licząc na różne punkty montowania, które będą działały jako rzeczywisty punkt przekierowania.
Bratchley,
1
@JoelDavis, możesz podłączyć dowolny plik, nie tylko katalogowy.
Stéphane Chazelas
TIL Czy są z tym jednak kontrole bezpieczeństwa? Wypróbowałem to, używając podkatalogu, w którym się znajdowałem (wiązanie z / etc / fstab) i zwróciło „Not a directory”, ale zrobiłem prawie to samo /testi działało bez problemu.
Bratchley,
Właściwie nm widzę różnicę, zrobiłem to do katalogu za pierwszym razem, a plik za następnym razem. Zakładałem, że odpowiednio przekieruje / zmodyfikuje VFS. W każdym razie dzięki za nową zabawkę.
Bratchley,
3

Miękkie linki.

Utwórz dwa pliki konfiguracji i wskaż jeden z nich miękkim łączem przez większość czasu, ale zmień miękkie łącze, aby wskazywało na inny, gdy uruchomiona jest specjalna aplikacja.

(Wiem, że to okropny hack, ale jest nieco bardziej niezawodny niż zmiana zawartości pliku).

Lub manipuluj $ HOME.

W skrypcie, który rozpoczyna denerwujący proces, ustaw $ HOME na coś w zwykłym katalogu $ HOME, a twoja aplikacja powinna następnie użyć pliku konfiguracyjnego znajdującego się tam (przetestowany i działa dla podstawowych poleceń powłoki, ~ rozwija się do $ HOME).

W zależności od tego, co jeszcze robi ten proces, zmiana $ HOME może mieć niezamierzone konsekwencje (np. Pliki wyjściowe mogą skończyć w niewłaściwym miejscu).

EightBitTony
źródło
1

Możesz to zrobić za pomocą sztuczki LD_PRELOAD . Oto implementacja, która mapuje ścieżki zaczynające się od określonego prefiksu do innej lokalizacji. Kod znajduje się również na github .

Na przykład, możesz sfałszować istnienie pliku /etc/bez rootowania. Było to konieczne dla klienta owncloud, który odmawia działania, gdy plik /etc/ownCloud/sync-exclude.listnie istnieje.

Działa poprzez zastąpienie funkcji open()i, open64()aby zamapować jeden katalog do drugiego, na przykład wszystkie open()wywołania, do /etc/ownCloud/...których można przekierować /home/user1/.etc/ownCloud/....

Po prostu dostosuj path_map, a następnie skompiluj i uruchom swój program ze wstępnie załadowaną biblioteką lib:

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

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

Kod źródłowy 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);
    }
}
Fritz
źródło