Leer / escribir archivos dentro de un módulo del kernel de Linux

99

Conozco todas las discusiones sobre por qué uno no debería leer / escribir archivos desde el kernel, sino cómo usar / proc o netlink para hacer eso. Quiero leer / escribir de todos modos. También he leído Driving Me Nuts: Cosas que nunca debes hacer en el núcleo .

Sin embargo, el problema es que 2.6.30 no se exporta sys_read(). Más bien está envuelto SYSCALL_DEFINE3. Entonces, si lo uso en mi módulo, recibo las siguientes advertencias:

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

Obviamente insmodno se puede cargar el módulo porque la vinculación no ocurre correctamente.

Preguntas:

  • ¿Cómo leer / escribir dentro del kernel después de 2.6.22 (donde sys_read()/ sys_open()no se exportan)?
  • En general, ¿cómo usar las llamadas al sistema envueltas en macro SYSCALL_DEFINEn()desde dentro del kernel?
Methos
fuente

Respuestas:

122

Debe tener en cuenta que debe evitar la E / S de archivos desde el kernel de Linux cuando sea posible. La idea principal es ir "un nivel más profundo" y llamar a las funciones de nivel VFS en lugar del controlador syscall directamente:

Incluye:

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

Abrir un archivo (similar 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;
}

Cerrar un archivo (similar a cerrar):

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

Lectura de datos de un archivo (similar a 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;
}   

Escribir datos en un archivo (similar 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;
}

La sincronización cambia un archivo (similar a fsync):

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

[Editar] Originalmente, propuse usar file_fsync, que ya no está en las versiones más recientes del kernel. Gracias al pobre que sugirió el cambio, pero cuyo cambio fue rechazado. La edición fue rechazada antes de que pudiera revisarla.

dmeister
fuente
2
Gracias. Estaba pensando en hacer algo similar replicando la funcionalidad sys_read / sys_open. Pero esto es de gran ayuda. Una curiosidad, ¿hay alguna forma de usar las llamadas al sistema declaradas usando SYSCALL_DEFINE?
Methos
5
Probé este código en el kernel 2.6.30 (Ubuntu 9.04) y la lectura del archivo bloquea el sistema. ¿Alguien experimentó el mismo problema?
Enrico Detoma
@Enrico Detoma? Oh wow. ¿Hay alguna forma de que me puedas dar el módulo que usaste? ¿Nunca has visto eso antes?
dmeister
2
Eso inmediatamente plantea la pregunta de "por qué estás haciendo ese baile de FS, por cierto", que se responde bastante bien aquí: linuxjournal.com/node/8110/print en la sección "Arreglando el espacio de direcciones".
PypeBros
@dmeister, Objeto no encontrado para las funciones de nivel VFS de tu enlace
sree
19

Desde la versión 4.14 del núcleo de Linux, vfs_ready vfs_writefunciones están ya no se exportan para su uso en módulos. En cambio, se proporcionan funciones exclusivamente para el acceso a archivos del 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);

Además, filp_openya no acepta cadenas de espacio de usuario, por lo que se puede usar para acceder al kernel directamente (sin bailar con set_fs).

Tsyvarev
fuente