Membuat proses membaca file yang berbeda untuk nama file yang sama

9

Saya punya aplikasi yang membaca file. Sebut saja processname dan file ~ / .configuration . Ketika processname berjalan, ia selalu membaca ~ / .configuration dan tidak dapat dikonfigurasi secara berbeda. Ada juga aplikasi lain yang mengandalkan "~ / .configuration", sebelum dan sesudah, tetapi tidak ketika processname berjalan.

Membungkus processname dalam sebuah skrip yang menggantikan konten ~ / .configuration adalah sebuah pilihan, tetapi saya baru-baru ini mengalami pemadaman listrik (sementara isinya ditukar), di mana saya kehilangan isi file yang sebelumnya dikatakan, jadi ini tidak diinginkan.

Apakah ada cara (mungkin menggunakan sesuatu yang berhubungan dengan jauh LD_DEBUG=files processname?) Untuk mengelabui proses membaca konten yang berbeda ketika mencoba membaca file tertentu? Mencari dan mengganti nama file di executable agak terlalu invasif, tetapi harus bekerja juga.

Saya tahu mungkin untuk menulis modul kernel yang mengambil alih open()panggilan ( https://news.ycombinator.com/item?id=2972958 ), tetapi apakah ada cara yang lebih sederhana atau lebih bersih?

EDIT: Ketika mencari ~ / .configuration di executable processname saya menemukan bahwa ia mencoba membaca nama file lain tepat sebelum membaca ~ / .configuration . Masalah terpecahkan.

Alexander
sumber
2
Ini bisa dilakukan melalui LD_PRELOADatau SEKERING, seperti dengan masalah yang agak mirip ini , tapi saya tidak tahu implementasi yang ada.
Gilles 'SO- berhenti bersikap jahat'

Jawaban:

6

Dalam versi terbaru dari Linux, Anda dapat urungkan yang me-mount namespace . Artinya, Anda dapat memulai proses yang melihat sistem file virtual secara berbeda (dengan sistem file yang dipasang berbeda).

Itu juga bisa dilakukan dengan chroot, tetapi unsharelebih disesuaikan dengan kasus Anda.

Seperti chroot, Anda perlu superuser yang diprioritaskan ke unsharemount namespace.

Jadi, katakan Anda punya ~/.configurationdan ~/.configuration-for-that-cmdfile.

Anda dapat memulai proses yang ~/.configurationsebenarnya merupakan bind-mount ~/.configuration-for-that-cmddi sana, dan jalankan that-cmddi sana.

Suka:

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

that-cmddan semua proses turunannya akan melihat perbedaan ~/.configuration.

that-cmddi atas akan berjalan sebagai root, gunakan sudo -u another-user that-cmdjika perlu dijalankan sebagai pengguna lain .

Stéphane Chazelas
sumber
Saya pikir solusi Anda mungkin lebih baik dari keduanya yang diberikan sejauh ini (dan mengingat apa yang OP lakukan setelah itu, pengarahan ulang berdasarkan waktu atau hasil dari proses pendeteksian tampaknya rapuh bagi saya), tapi saya pikir mereka menginginkan satu file untuk tampil berbeda. Jadi mereka mungkin harus memasangnya di tempat lain dan menggunakan symlink, mengandalkan titik mount yang berbeda untuk bertindak sebagai titik pengalihan yang sebenarnya.
Bratchley
1
@ JoelDavis, Anda dapat mengikat file apa pun, tidak hanya yang direktori.
Stéphane Chazelas
TIL. Apakah ada kontrol keamanan dengan itu? Saya mencoba menggunakan subdirektori di mana saya berada (mengikat dari / etc / fstab) dan mengembalikan "Bukan direktori" tapi saya melakukan hal yang hampir sama /testdan bekerja tanpa masalah.
Bratchley
Sebenarnya, nm saya bisa melihat perbedaannya, saya melakukannya pada direktori pertama kali dan file berikutnya. Saya berasumsi bahwa itu hanya akan mengarahkan ulang / memodifikasi VFS yang sesuai. Siapa pun, terima kasih untuk mainan baru itu.
Bratchley
3

Tautan lunak.

Buat dua file konfigurasi, dan arahkan ke salah satu dari mereka dengan tautan lunak sebagian besar waktu, tetapi ubah tautan lunak untuk menunjuk ke yang lain ketika aplikasi khusus sedang berjalan.

(Saya tahu ini adalah peretasan yang mengerikan, tetapi ini sedikit lebih dapat diandalkan daripada mengubah isi file).

Atau, memanipulasi $ HOME.

Dalam skrip yang memulai proses menjengkelkan, tetapkan $ HOME untuk menjadi sesuatu di bawah direktori $ HOME biasa, dan aplikasi Anda kemudian harus menggunakan file konfigurasi yang terletak di sana (diuji, dan berfungsi untuk perintah shell dasar, ~ diperluas ke $ HOME).

Bergantung pada apa lagi prosesnya, mengubah $ HOME mungkin memiliki konsekuensi yang tidak diinginkan (yaitu file output mungkin berakhir di tempat yang salah).

EightBitTony
sumber
1

Anda dapat melakukan ini menggunakan trik LD_PRELOAD . Berikut ini adalah implementasi yang memetakan jalur yang dimulai dengan awalan spesifik ke lokasi lain. Kode ini juga ada di github .

Misalnya, Anda bisa memalsukan keberadaan file /etc/tanpa menjadi root. Ini diperlukan untuk klien owncloud yang menolak untuk bekerja ketika file /etc/ownCloud/sync-exclude.listtidak ada.

Ini bekerja dengan mengesampingkan open()dan open64()fungsi untuk memetakan satu direktori ke direktori lain, misalnya semua open()panggilan untuk /etc/ownCloud/...dapat dialihkan ke /home/user1/.etc/ownCloud/....

Cukup sesuaikan path_map, lalu kompilasi dan jalankan program Anda dengan lib yang dimuat sebelumnya:

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

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

Kode sumber 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
sumber