การทำให้กระบวนการอ่านไฟล์อื่นสำหรับชื่อไฟล์เดียวกัน


9

ฉันมีแอปพลิเคชั่นที่อ่านไฟล์ โทร Let 's มันProcessNameและแฟ้ม~ / .configuration เมื่อprocessnameรันมันจะอ่าน~ / .configurationเสมอและไม่สามารถกำหนดค่าต่างกันได้ นอกจากนี้ยังมีแอปพลิเคชันอื่น ๆ ที่ใช้ "~ / .configuration" ก่อนและหลัง แต่ไม่ใช่ในขณะที่ชื่อกระบวนการกำลังทำงาน

การตัดชื่อกระบวนการในสคริปต์ที่แทนที่เนื้อหาของ~ / .configurationเป็นตัวเลือก แต่เมื่อเร็ว ๆ นี้ฉันมีไฟดับ (ในขณะที่เนื้อหาถูกสับเปลี่ยนออก) ซึ่งฉันได้สูญเสียเนื้อหาก่อนหน้าของไฟล์ดังกล่าวดังนั้นจึงไม่เป็นที่ต้องการ

มีวิธี (อาจใช้บางสิ่งที่เกี่ยวข้องกับการห่างไกลLD_DEBUG=files processname?) เพื่อหลอกให้กระบวนการอ่านเนื้อหาต่าง ๆ เมื่อพยายามอ่านไฟล์ใดไฟล์หนึ่ง? การค้นหาและแทนที่ชื่อไฟล์ในไฟล์เรียกทำงานนั้นเป็นบิตที่รุกรานเกินไป แต่ก็ควรใช้งานได้เช่นกัน

ฉันรู้ว่าเป็นไปได้ที่จะเขียนโมดูลเคอร์เนลที่รับopen()สาย ( https://news.ycombinator.com/item?id=2972958 ) แต่มีวิธีที่ง่ายขึ้นหรือสะอาดขึ้นหรือไม่

แก้ไข: เมื่อค้นหา~ / .configurationในProcessNameปฏิบัติการที่ผมค้นพบว่ามันพยายามที่จะอ่านชื่อไฟล์อื่นขวาก่อนอ่าน~ / .configuration แก้ไขปัญหา.


2
สิ่งนี้สามารถทำได้ผ่านLD_PRELOADหรือ FUSE เช่นเดียวกับปัญหาที่ค่อนข้างคล้ายกันนี้แต่ฉันไม่ทราบว่าการใช้งานที่มีอยู่
Gilles 'ดังนั้นหยุดความชั่วร้าย'

คำตอบ:


6

ใน Linux เวอร์ชันล่าสุดคุณสามารถยกเลิกการแบ่งใช้เนมสเปซได้ นั่นคือคุณสามารถเริ่มต้นกระบวนการที่ดูระบบไฟล์เสมือนต่างกัน (ด้วยระบบไฟล์ที่ติดตั้งแตกต่างกัน)

สามารถทำได้ด้วยchrootแต่unshareปรับให้เหมาะกับกรณีของคุณมากกว่า

เช่นเดียวกับchrootคุณต้องมีสิทธิ์ superuser 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หากจำเป็นต้องเรียกใช้ในฐานะผู้ใช้อื่น


ฉันคิดว่าวิธีแก้ปัญหาของคุณน่าจะดีกว่าของทั้งสองที่ให้มาจนถึงตอนนี้ (และให้สิ่งที่ OP ทำหลังจากนั้นการเปลี่ยนเส้นทางตามเวลาหรือผลลัพธ์ของกระบวนการตรวจจับดูเหมือนว่าไม่เหมาะกับฉัน) แต่ฉันคิดว่าพวกเขาต้องการไฟล์เดียว เพื่อแสดงที่แตกต่างกัน ดังนั้นพวกเขาอาจต้องติดตั้งที่อื่นและใช้ symlink นับจำนวนจุดเชื่อมต่อที่แตกต่างกันเพื่อทำหน้าที่เป็นจุดเปลี่ยนเส้นทางที่แท้จริง
Bratchley

1
@JoelDavis คุณสามารถผูกไฟล์ใดก็ได้ไม่เพียงแค่สร้างไดเรกทอรี
Stéphane Chazelas

TIL มีการควบคุมความปลอดภัยด้วยหรือไม่? ฉันลองใช้ไดเรกทอรีย่อยที่ฉันอยู่ (เชื่อมโยงจาก / etc / fstab) และส่งคืน "ไม่ใช่ไดเรกทอรี" แต่ฉันทำสิ่งเดียวกันภายใต้/testและทำงานได้โดยไม่มีปัญหา
Bratchley

ที่จริงแล้วฉันเห็นความแตกต่างฉันทำมันในไดเรกทอรีเป็นครั้งแรกและไฟล์ถัดไป ฉันสมมติว่ามันจะเปลี่ยนเส้นทาง / แก้ไข VFS ตามความเหมาะสม Anywho ขอบคุณสำหรับของเล่นใหม่
Bratchley

3

ลิงค์อ่อน

สร้างไฟล์กำหนดค่าสองไฟล์และชี้ไปที่หนึ่งในนั้นด้วยซอฟต์ลิงค์ส่วนใหญ่ แต่เปลี่ยนซอฟต์ลิงค์ให้ชี้ไปที่อีกไฟล์หนึ่งเมื่อแอพพิเศษกำลังทำงาน

(ฉันรู้ว่านี่เป็นแฮ็คที่น่ากลัว แต่น่าเชื่อถือกว่าการเปลี่ยนเนื้อหาไฟล์เล็กน้อย)

หรือจัดการ $ HOME

ในสคริปต์ที่เริ่มต้นกระบวนการที่น่ารำคาญให้ตั้งค่า $ HOME เป็นสิ่งภายใต้ไดเรกทอรี $ HOME ปกติแล้วแอปของคุณควรใช้ไฟล์กำหนดค่าที่อยู่ที่นั่น (ทดสอบแล้วและใช้งานได้กับคำสั่งเชลล์พื้นฐาน ~ ขยายเป็น $ HOME)

ขึ้นอยู่กับกระบวนการอื่นการเปลี่ยนแปลง $ HOME อาจมีผลที่ไม่ตั้งใจ (เช่นไฟล์เอาต์พุตอาจสิ้นสุดในตำแหน่งที่ไม่ถูกต้อง)


1

คุณสามารถทำได้โดยใช้เคล็ดลับ LD_PRELOAD ต่อไปนี้เป็นการนำไปใช้งานที่แมปเส้นทางที่ขึ้นต้นด้วยคำนำหน้าเฉพาะไปยังตำแหน่งอื่น รหัสยัง GitHub

ตัวอย่างเช่นคุณสามารถปลอมไฟล์ที่มีอยู่/etc/โดยไม่ต้องรูท สิ่งนี้จำเป็นสำหรับไคลเอนต์cloudที่ปฏิเสธที่จะทำงานเมื่อ/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);
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.