Ler / gravar arquivos em um módulo do kernel Linux

98

Eu conheço todas as discussões sobre por que não se deve ler / escrever arquivos do kernel, em vez de como usar / proc ou netlink para fazer isso. Quero ler / escrever mesmo assim. Eu também li o que me deixa maluco - coisas que você nunca deve fazer no kernel .

No entanto, o problema é que 2.6.30 não exporta sys_read(). Em vez disso, está embrulhado SYSCALL_DEFINE3. Portanto, se eu usá-lo em meu módulo, recebo os seguintes avisos:

WARNING: "sys_read" [xxx.ko] undefined!
WARNING: "sys_open" [xxx.ko] undefined!

Obviamente, insmodnão é possível carregar o módulo porque a vinculação não ocorre corretamente.

Questões:

  • Como ler / escrever dentro do kernel após 2.6.22 (onde sys_read()/ sys_open()não são exportados)?
  • Em geral, como usar chamadas de sistema agrupadas em macro SYSCALL_DEFINEn()de dentro do kernel?
Methos
fonte

Respostas:

121

Você deve estar ciente de que deve evitar E / S de arquivo de dentro do kernel do Linux quando possível. A ideia principal é ir "um nível mais fundo" e chamar funções de nível VFS em vez do manipulador syscall diretamente:

Inclui:

#include <linux/fs.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/buffer_head.h>

Abrindo um arquivo (semelhante a abrir):

struct file *file_open(const char *path, int flags, int rights) 
{
    struct file *filp = NULL;
    mm_segment_t oldfs;
    int err = 0;

    oldfs = get_fs();
    set_fs(get_ds());
    filp = filp_open(path, flags, rights);
    set_fs(oldfs);
    if (IS_ERR(filp)) {
        err = PTR_ERR(filp);
        return NULL;
    }
    return filp;
}

Fechar um arquivo (semelhante a fechar):

void file_close(struct file *file) 
{
    filp_close(file, NULL);
}

Lendo dados de um arquivo (semelhante ao pread):

int file_read(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size) 
{
    mm_segment_t oldfs;
    int ret;

    oldfs = get_fs();
    set_fs(get_ds());

    ret = vfs_read(file, data, size, &offset);

    set_fs(oldfs);
    return ret;
}   

Gravando dados em um arquivo (semelhante a pwrite):

int file_write(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size) 
{
    mm_segment_t oldfs;
    int ret;

    oldfs = get_fs();
    set_fs(get_ds());

    ret = vfs_write(file, data, size, &offset);

    set_fs(oldfs);
    return ret;
}

A sincronização altera um arquivo (semelhante ao fsync):

int file_sync(struct file *file) 
{
    vfs_fsync(file, 0);
    return 0;
}

[Editar] Originalmente, propus usar file_fsync, que não existe nas versões mais recentes do kernel. Obrigado ao coitado que sugeriu a mudança, mas cuja mudança foi rejeitada. A edição foi rejeitada antes que eu pudesse revisá-la.

mestre
fonte
2
Obrigado. Eu estava pensando em fazer algo semelhante, replicando a funcionalidade sys_read / sys_open. Mas isso é uma grande ajuda. Uma curiosidade, existe alguma maneira de usar chamadas de sistema declaradas usando SYSCALL_DEFINE?
Methos
5
Eu tentei esse código no kernel 2.6.30 (Ubuntu 9.04) e ler o arquivo travou o sistema. Alguém teve o mesmo problema?
Enrico Detoma,
@Enrico Detoma? Oh, uau. Existe alguma maneira de você me passar o módulo que você usou? Nunca viu isso antes?
dmeister
2
Isso imediatamente levanta a questão de "por que você está fazendo aquela dança FS, btw", que é respondida muito bem aqui: linuxjournal.com/node/8110/print na seção "Corrigindo o espaço de endereço".
PypeBros
@dmeister, Object Not Found for ur link VFS level functions
sree
18

Desde a versão 4.14 do kernel do Linux, vfs_reade vfs_writeas funções são já exportados para uso em módulos. Em vez disso, são fornecidas funções exclusivamente para acesso a arquivos do kernel:

# Read the file from the kernel space.
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);

# Write the file from the kernel space.
ssize_t kernel_write(struct file *file, const void *buf, size_t count,
            loff_t *pos);

Além disso, filp_opennão aceita mais string de espaço do usuário, portanto, pode ser usado para acesso ao kernel diretamente (sem dançar com set_fs).

Tsyvarev
fonte