Fazendo um processo ler um arquivo diferente para o mesmo nome de arquivo

9

Eu tenho um aplicativo que lê um arquivo. Vamos chamá-lo processname e o arquivo ~ / .configuration . Quando processname é executado, ele sempre lê ~ / .configuration e não pode ser configurado de maneira diferente. Também existem outros aplicativos que contam com "~ / .configuration", antes e depois, mas não enquanto o nome do processo está em execução.

Embrulhar processname em um script que substitui o conteúdo de ~ / .configuration é uma opção, mas recentemente tive uma queda de energia (enquanto o conteúdo foi trocado), onde perdi o conteúdo anterior do arquivo, portanto, isso não é desejável.

Existe uma maneira (talvez usando algo relacionado à distância LD_DEBUG=files processname?) Para enganar um processo para ler conteúdos diferentes quando ele tenta ler um arquivo específico? Pesquisando e substituindo o nome do arquivo no executável é um pouco invasivo, mas também deve funcionar.

Eu sei que é possível escrever um módulo do kernel que assume a open()chamada ( https://news.ycombinator.com/item?id=2972958 ), mas existe uma maneira mais simples ou mais limpa?

EDIT: Ao procurar ~ / .configuration na processname executável eu descobri que ele tentou ler um outro direito filename antes de ler ~ / .configuration . Problema resolvido.

Alexander
fonte
2
Isso pode ser feito via LD_PRELOADou FUSE, como com esse problema um pouco semelhante , mas não conheço nenhuma implementação existente.
Gilles 'SO- stop be evil'

Respostas:

6

Nas versões recentes do Linux, você pode cancelar o compartilhamento do espaço para nome da montagem . Ou seja, você pode iniciar processos que visualizam o sistema de arquivos virtual de maneira diferente (com sistemas de arquivos montados de maneira diferente).

Isso também pode ser feito chroot, mas unshareé mais adaptado ao seu caso.

Assim chroot, você precisa de um superusuário privilegiado unshareno espaço para nome da montagem.

Então, diga que você tem ~/.configuratione ~/.configuration-for-that-cmdarquivos.

Você pode iniciar um processo para o qual ~/.configurationé realmente uma montagem de ligação ~/.configuration-for-that-cmdlá e executar that-cmdlá.

gostar:

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

that-cmde todos os seus processos descendentes terão uma visão diferente ~/.configuration.

that-cmdacima será executado como root, use sudo -u another-user that-cmdse precisar ser executado como outro usuário .

Stéphane Chazelas
fonte
Acho que sua solução é provavelmente a melhor das duas fornecidas até agora (e considerando o que o OP é depois, o redirecionamento com base no tempo ou nos resultados de um processo de detecção me parece duvidoso), mas acho que eles desejam um único arquivo para aparecer de forma diferente. Portanto, eles provavelmente teriam que montá-lo em outro lugar e usar um link simbólico, contando com os diferentes pontos de montagem para atuar como o ponto real de redirecionamento.
Bratchley
1
@JoelDavis, você pode montar qualquer arquivo, não apenas os de diretório.
Stéphane Chazelas
TIL. Existem controles de segurança com isso? Eu tentei usando um subdiretório em que estava (ligação de / etc / fstab) e ele retornou "Não é um diretório", mas fiz praticamente a mesma coisa /teste funcionou sem problemas.
precisa saber é o seguinte
Na verdade, nm eu ​​posso ver a diferença, eu fiz isso em um diretório pela primeira vez e um arquivo na seguinte. Eu estava assumindo que seria apenas redirecionar / modificar o VFS conforme apropriado. Anywho, obrigado pelo novo brinquedo.
Bratchley
3

Links flexíveis.

Crie dois arquivos de configuração e aponte para um deles com um link flexível na maioria das vezes, mas altere o link flexível para apontar para o outro quando o aplicativo especial estiver em execução.

(Eu sei que esse é um truque horrível, mas é um pouco mais confiável do que alterar o conteúdo do arquivo).

Ou manipule $ HOME.

No script que inicia o processo irritante, defina $ HOME como algo no diretório $ HOME normal, e seu aplicativo deve usar o arquivo de configuração localizado lá (testado e funciona com comandos básicos do shell, ~ expande para $ HOME).

Dependendo do que mais o processo faz, a alteração de $ HOME pode ter consequências indesejadas (ou seja, os arquivos de saída podem acabar no lugar errado).

EightBitTony
fonte
1

Você pode fazer isso usando o truque LD_PRELOAD . Aqui está uma implementação que mapeia os caminhos que começam com um prefixo específico para outro local. O código também está no github .

Por exemplo, você pode falsificar a existência de um arquivo /etc/sem ser root. Isso foi necessário para o cliente owncloud que se recusa a trabalhar quando o arquivo /etc/ownCloud/sync-exclude.listnão existe.

Ele funciona substituindo as funções open()e open64()para mapear um diretório para outro, por exemplo, todas as open()chamadas para as quais /etc/ownCloud/...podem ser redirecionadas /home/user1/.etc/ownCloud/....

Basta ajustar path_map, compilar e executar o seu programa com a lib pré-carregada:

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 fonte 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
fonte