Lire / écrire des fichiers dans un module de noyau Linux

99

Je connais toutes les discussions sur les raisons pour lesquelles il ne faut pas lire / écrire des fichiers à partir du noyau, mais comment utiliser / proc ou netlink pour le faire. Je veux quand même lire / écrire. J'ai aussi lu Driving Me Nuts - Ce que vous ne devriez jamais faire dans le noyau .

Cependant, le problème est que 2.6.30 n'exporte pas sys_read(). C'est plutôt enveloppé SYSCALL_DEFINE3. Donc, si je l'utilise dans mon module, j'obtiens les avertissements suivants:

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

Impossible de insmodcharger le module car la liaison ne se fait pas correctement.

Des questions:

  • Comment lire / écrire dans le noyau après 2.6.22 (où sys_read()/ sys_open()ne sont pas exportés)?
  • En général, comment utiliser les appels système enveloppés dans une macro à SYSCALL_DEFINEn()partir du noyau?
Methos
la source

Réponses:

122

Vous devez être conscient que vous devez éviter les E / S de fichiers depuis le noyau Linux lorsque cela est possible. L'idée principale est d'aller "d'un niveau plus profond" et d'appeler directement les fonctions de niveau VFS au lieu du gestionnaire d'appel système:

Comprend:

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

Ouverture d'un fichier (similaire à ouvrir):

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;
}

Fermer un fichier (similaire à fermer):

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

Lecture des données d'un fichier (similaire à la lecture préalable):

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;
}   

Ecrire des données dans un fichier (similaire à 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 synchronisation modifie un fichier (similaire à fsync):

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

[Modifier] À l'origine, j'ai proposé d'utiliser file_fsync, qui a disparu dans les nouvelles versions du noyau. Merci au pauvre type qui a suggéré le changement, mais dont le changement a été rejeté. La modification a été rejetée avant que je puisse l'examiner.

démister
la source
2
Je vous remercie. Je pensais faire quelque chose de similaire en répliquant la fonctionnalité sys_read / sys_open. Mais c'est une aide précieuse. Une curiosité, existe-t-il un moyen d'utiliser les appels système déclarés en utilisant SYSCALL_DEFINE?
Methos le
5
J'ai essayé ce code dans le noyau 2.6.30 (Ubuntu 9.04) et la lecture du fichier plante le système. Quelqu'un a-t-il rencontré le même problème?
Enrico Detoma
@Enrico Detoma? Oh wow. Est-ce que vous pouvez me donner le module que vous avez utilisé? Jamais vu ça avant?
dmeister
2
Cela soulève immédiatement la question de "pourquoi faites-vous cette danse FS, btw", à laquelle on répond assez bien ici: linuxjournal.com/node/8110/print dans la section "Fixing the Address Space".
PypeBros
@dmeister, objet introuvable pour les fonctions de niveau VFS de lien ur
sree
19

Depuis la version 4.14 du noyau Linux, vfs_readet les vfs_writefonctions sont pas exportées plus pour une utilisation dans des modules. Au lieu de cela, des fonctions exclusivement pour l'accès aux fichiers du noyau sont fournies:

# 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);

De filp_openplus, n'accepte plus la chaîne d'espace utilisateur, elle peut donc être utilisée pour l'accès au noyau directement (sans danse avec set_fs).

Tsyvarev
la source