Hacer que un proceso lea un archivo diferente para el mismo nombre de archivo

9

Tengo una aplicación que lee un archivo. Llamémoslo nombre de proceso y el archivo ~ / .configuration . Cuando se ejecuta processname , siempre lee ~ / .configuration y no se puede configurar de manera diferente. También existen otras aplicaciones que se basan en "~ / .configuration", antes y después, pero no mientras ProcessName se está ejecutando.

Envoltura ProcessName en una secuencia de comandos que sustituye el contenido de ~ / .configuration es una opción, pero recientemente he tenido un corte de energía (mientras que los contenidos fueron cambiados), donde perdí el contenido anterior de dicho fichero, así que esto no es deseable.

¿Hay alguna manera (tal vez usando algo relacionado con algo distante LD_DEBUG=files processname) para engañar a un proceso a leer diferentes contenidos cuando intenta leer un archivo específico? Buscar y reemplazar el nombre del archivo en el ejecutable es un poco invasivo, pero debería funcionar también.

Sé que es posible escribir un módulo de kernel que se haga cargo de la open()llamada ( https://news.ycombinator.com/item?id=2972958 ), pero ¿hay alguna forma más simple o más limpia?

EDITAR: Al buscar ~ / .configuration en el ejecutable del nombre del proceso , descubrí que intentaba leer otro nombre de archivo justo antes de leer ~ / .configuration . Problema resuelto.

Alejandro
fuente
2
Esto podría hacerse a través de LD_PRELOADo FUSE, como con este problema algo similar , pero no conozco ninguna implementación existente.
Gilles 'SO- deja de ser malvado'

Respuestas:

6

En versiones recientes de Linux, puede dejar de compartir el espacio de nombres de montaje . Es decir, puede iniciar procesos que vean el sistema de archivos virtual de manera diferente (con sistemas de archivos montados de manera diferente).

Eso también se puede hacer chroot, pero unshareestá más adaptado a su caso.

Al igual que chroot, necesita superusuario privilegiado para unshareel espacio de nombres de montaje.

Entonces, digamos que tienes ~/.configurationy ~/.configuration-for-that-cmdarchivos.

Puede iniciar un proceso para el cual ~/.configurationes realmente un montaje de enlace ~/.configuration-for-that-cmdallí, y ejecutar that-cmdallí.

me gusta:

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

that-cmdy todos sus procesos descendientes verán un diferente ~/.configuration.

that-cmdanterior se ejecutará como root, use sudo -u another-user that-cmdsi necesita ejecutarse como otro usuario .

Stéphane Chazelas
fuente
Creo que su solución es probablemente la mejor de las dos dadas hasta ahora (y dado lo que busca el OP, redirigir según el tiempo o los resultados de un proceso de detección me parece dudoso), pero creo que quieren un solo archivo para aparecer de manera diferente. Por lo tanto, probablemente tendrían que montarlo en otro lugar y usar un enlace simbólico, contando con los diferentes puntos de montaje para actuar como el punto real de redireccionamiento.
Bratchley
1
@JoelDavis, puede enlazar y montar cualquier archivo, no solo los de directorio.
Stéphane Chazelas
TIL Sin embargo, ¿hay controles de seguridad con eso? Lo intenté usando un subdirectorio en el que estaba (enlace desde / etc / fstab) y me devolvió "No es un directorio", pero hice casi lo mismo /testy funcionó sin problemas.
Bratchley
En realidad, nm puedo ver la diferencia, lo hice en un directorio la primera vez y en un archivo la siguiente. Supuse que solo redirigiría / modificaría el VFS según corresponda. De todos modos, gracias por el nuevo juguete.
Bratchley
3

Enlaces blandos.

Cree dos archivos de configuración y apunte a uno de ellos con un enlace suave la mayor parte del tiempo, pero cambie el enlace suave para que apunte al otro cuando se esté ejecutando la aplicación especial.

(Sé que este es un truco horrible, pero es un poco más confiable que cambiar el contenido del archivo).

O manipule $ HOME.

En el script que inicia el proceso molesto, configure $ HOME como algo bajo el directorio normal $ HOME, y su aplicación debería usar el archivo de configuración ubicado allí (probado y funciona para comandos básicos de shell, ~ se expande a $ HOME).

Dependiendo de qué más haga el proceso, cambiar $ HOME puede tener consecuencias no deseadas (es decir, los archivos de salida pueden terminar en el lugar equivocado).

OchoBitTony
fuente
1

Puedes hacer esto usando el truco LD_PRELOAD . Aquí hay una implementación que asigna rutas que comienzan con un prefijo específico a otra ubicación. El código también está en github .

Por ejemplo, podría fingir la existencia de un archivo /etc/sin ser root. Esto era necesario para el cliente owncloud que se niega a trabajar cuando el archivo /etc/ownCloud/sync-exclude.listno existe.

Funciona anulando las funciones open()y open64()para asignar un directorio a otro, por ejemplo, todas las open()llamadas a /etc/ownCloud/...se pueden redirigir a /home/user1/.etc/ownCloud/....

Simplemente ajuste el path_map, luego compile y ejecute su programa con la biblioteca precargada:

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

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

Código fuente de 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
fuente