Baca / tulis file dalam modul kernel Linux

99

Saya tahu semua diskusi tentang mengapa seseorang tidak boleh membaca / menulis file dari kernel, melainkan bagaimana menggunakan / proc atau netlink untuk melakukan itu. Saya tetap ingin membaca / menulis. Saya juga membaca Driving Me Nuts - Things You Never Should Do in the Kernel .

Namun, masalahnya adalah 2.6.30 tidak dapat mengekspor sys_read(). Melainkan dibungkus SYSCALL_DEFINE3. Jadi jika saya menggunakannya di modul saya, saya mendapatkan peringatan berikut:

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

Jelas insmodtidak dapat memuat modul karena penautan tidak terjadi dengan benar.

Pertanyaan:

  • Bagaimana membaca / menulis dalam kernel setelah 2.6.22 (di mana sys_read()/ sys_open()tidak diekspor)?
  • Secara umum, bagaimana menggunakan panggilan sistem yang dibungkus dalam makro SYSCALL_DEFINEn()dari dalam kernel?
Methos
sumber

Jawaban:

122

Anda harus menyadari bahwa Anda harus menghindari file I / O dari dalam kernel Linux jika memungkinkan. Ide utamanya adalah untuk "melangkah lebih dalam" dan memanggil fungsi level VFS alih-alih penangan syscall secara langsung:

Termasuk:

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

Membuka file (mirip dengan membuka):

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

Tutup file (mirip dengan tutup):

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

Membaca data dari file (mirip dengan 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;
}   

Menulis data ke file (mirip dengan 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;
}

Sinkronisasi mengubah file (mirip dengan fsync):

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

[Sunting] Awalnya, saya mengusulkan menggunakan file_fsync, yang hilang di versi kernel yang lebih baru. Terima kasih kepada pria malang yang menyarankan perubahan, tetapi perubahannya ditolak. Pengeditan ditolak sebelum saya dapat memeriksanya.

dmeister
sumber
2
Terima kasih. Saya berpikir untuk melakukan hal serupa dengan mereplikasi fungsi sys_read / sys_open. Tapi ini sangat membantu. Penasaran, apakah ada cara untuk menggunakan panggilan sistem yang dideklarasikan menggunakan SYSCALL_DEFINE?
Methos
5
Saya mencoba kode ini di kernel 2.6.30 (Ubuntu 9.04) dan membaca file tersebut membuat sistem crash. Ada yang mengalami masalah yang sama?
Enrico Detoma
@Enri Detoma? Oh wow. Adakah cara agar Anda bisa memberi saya modul yang Anda gunakan? Belum pernah melihat itu sebelumnya?
dmeister
2
Itu segera menimbulkan pertanyaan "mengapa Anda melakukan tarian FS itu, btw", yang dijawab dengan cukup baik di sini: linuxjournal.com/node/8110/print di bawah bagian "Memperbaiki Ruang Alamat".
PypeBros
@dmeister, Objek Tidak Ditemukan untuk tautan ur fungsi tingkat VFS
sree
20

Sejak kernel Linux versi 4.14, vfs_readdan vfs_writefungsi tidak lagi diekspor untuk digunakan dalam modul. Sebagai gantinya, fungsi khusus untuk akses file kernel disediakan:

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

Juga, filp_opentidak lagi menerima string ruang pengguna, sehingga dapat digunakan untuk akses kernel secara langsung (tanpa menari dengan set_fs).

Tsyvarev
sumber