Einen Prozess dazu bringen, eine andere Datei für denselben Dateinamen zu lesen

9

Ich habe eine Anwendung, die eine Datei liest. Nennen wir es Prozessname und die Datei ~ / .configuration . Wenn der Prozessname ausgeführt wird , lautet er immer ~ / .configuration und kann nicht anders konfiguriert werden. Es gibt auch andere Anwendungen, die vor und nach "~ / .configuration" auf "~ / .configuration" angewiesen sind, jedoch nicht, während der Prozessname ausgeführt wird.

Wrapping Process in ein Skript, das den Inhalt von ~ / .configuration ersetzt, ist eine Option, aber ich hatte kürzlich einen Stromausfall (während der Inhalt ausgetauscht wurde), bei dem ich den vorherigen Inhalt dieser Datei verloren habe, sodass dies nicht wünschenswert ist.

Gibt es einen Weg (vielleicht mit etwas in der Ferne verwandt LD_DEBUG=files processname tun?), Einen Prozess zum Lesen verschiedener Inhalte zu verleiten, wenn er versucht, eine bestimmte Datei zu lesen? Das Suchen und Ersetzen des Dateinamens in der ausführbaren Datei ist etwas zu invasiv, sollte aber auch funktionieren.

Ich weiß, dass es möglich ist, ein Kernelmodul zu schreiben, das den open()Aufruf übernimmt ( https://news.ycombinator.com/item?id=2972958 ), aber gibt es einen einfacheren oder saubereren Weg?

BEARBEITEN: Bei der Suche nach ~ / .configuration in der ausführbaren Datei des Prozessnamens habe ich festgestellt, dass kurz vor dem Lesen von ~ / .configuration versucht wurde, einen anderen Dateinamen zu lesen . Problem gelöst.

Alexander
quelle
2
Dies könnte über LD_PRELOADoder FUSE erfolgen, wie bei diesem etwas ähnlichen Problem , aber ich kenne keine vorhandene Implementierung.
Gilles 'SO - hör auf böse zu sein'

Antworten:

6

In neueren Linux-Versionen können Sie den Mount-Namespace freigeben . Das heißt, Sie können Prozesse starten, die das virtuelle Dateisystem unterschiedlich anzeigen (wobei Dateisysteme unterschiedlich bereitgestellt werden).

Das kann auch gemacht werden chroot, ist aber unsharebesser auf Ihren Fall abgestimmt.

Wie chrootmüssen Sie Superuser priviledged unshareNamensraum montieren.

Sagen wir also, Sie haben ~/.configurationund ~/.configuration-for-that-cmdDateien.

Sie können dort einen Prozess starten, für den ~/.configurationes sich tatsächlich um einen Bind-Mount handelt ~/.configuration-for-that-cmd, und dort ausführen that-cmd.

mögen:

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

that-cmdund alle seine Nachkommenprozesse werden einen anderen sehen ~/.configuration.

that-cmdoben wird ausgeführt als root, verwenden sudo -u another-user that-cmdSie , wenn es als ein anderer Benutzer ausgeführt werden muss .

Stéphane Chazelas
quelle
Ich denke, Ihre Lösung ist wahrscheinlich die bessere der beiden, die bisher angegeben wurden (und wenn man bedenkt, wonach das OP strebt, erscheint mir die Umleitung basierend auf der Zeit oder den Ergebnissen eines Erkennungsprozesses zweifelhaft), aber ich denke, sie wollen eine einzige Datei anders auftauchen. Sie müssten es also wahrscheinlich an anderer Stelle einbinden und einen Symlink verwenden, wobei sie auf die unterschiedlichen Einhängepunkte zählen, um als eigentlicher Umleitungspunkt zu fungieren.
Bratchley
1
@JoelDavis, Sie können jede Datei binden, nicht nur Verzeichnisdateien.
Stéphane Chazelas
Bis. Gibt es damit jedoch Sicherheitskontrollen? Ich habe es mit einem Unterverzeichnis versucht, in dem ich mich befand (Bindung von / etc / fstab) und es gab "Kein Verzeichnis" zurück, aber ich habe so ziemlich das Gleiche unter gemacht /testund es hat ohne Probleme funktioniert.
Bratchley
Eigentlich kann ich den Unterschied sehen, ich habe es beim ersten Mal in einem Verzeichnis und beim nächsten Mal in einer Datei gemacht. Ich ging davon aus, dass das VFS nur entsprechend umgeleitet / geändert werden würde. Trotzdem danke für das neue Spielzeug.
Bratchley
3

Softlinks.

Erstellen Sie zwei Konfigurationsdateien und zeigen Sie die meiste Zeit mit einem Softlink auf eine davon. Ändern Sie den Softlink jedoch so, dass er auf die andere zeigt, wenn die spezielle App ausgeführt wird.

(Ich weiß, dass dies ein schrecklicher Hack ist, aber er ist etwas zuverlässiger als das Ändern des Dateiinhalts).

Oder manipulieren Sie $ HOME.

Stellen Sie in dem Skript, das den nervigen Prozess startet, $ HOME so ein, dass es sich im regulären $ HOME-Verzeichnis befindet, und Ihre App sollte dann die dort befindliche Konfigurationsdatei verwenden (getestet und funktioniert für grundlegende Shell-Befehle, ~ wird auf $ HOME erweitert).

Je nachdem, was der Prozess sonst noch tut, kann das Ändern von $ HOME unbeabsichtigte Folgen haben (dh Ausgabedateien landen möglicherweise am falschen Ort).

EightBitTony
quelle
1

Sie können dies mit dem Trick LD_PRELOAD tun . Hier ist eine Implementierung, die Pfade, die mit einem bestimmten Präfix beginnen, einem anderen Speicherort zuordnet. Der Code ist auch auf Github .

Sie können beispielsweise die Existenz einer Datei vortäuschen, /etc/ohne root zu sein. Dies war für den owncloud-Client erforderlich, der sich weigert zu arbeiten, wenn die Datei /etc/ownCloud/sync-exclude.listnicht vorhanden ist.

Es funktioniert, indem die Funktionen open()und überschrieben open64()werden, um ein Verzeichnis einem anderen zuzuordnen. Beispielsweise könnten alle open()Aufrufe an /etc/ownCloud/...umgeleitet werden /home/user1/.etc/ownCloud/....

Passen Sie einfach das an path_map, kompilieren Sie Ihr Programm und führen Sie es mit der vorinstallierten Bibliothek aus:

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

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

Quellcode von 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
quelle