อ่าน / เขียนไฟล์ภายในโมดูลเคอร์เนล Linux


101

ฉันรู้การอภิปรายทั้งหมดเกี่ยวกับสาเหตุที่ไม่ควรอ่าน / เขียนไฟล์จากเคอร์เนลแทนที่จะใช้/ procหรือnetlinkเพื่อทำเช่นนั้น ฉันต้องการอ่าน / เขียนต่อไป ฉันได้อ่าน Driving Me Nuts - สิ่งที่คุณไม่ควรทำในเคอร์เนลด้วย

แต่ปัญหาก็คือว่า 2.6.30 sys_read()ไม่ได้ส่งออก SYSCALL_DEFINE3แต่มันอยู่ในห่อ ดังนั้นหากฉันใช้มันในโมดูลของฉันฉันจะได้รับคำเตือนต่อไปนี้:

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

เห็นได้ชัดว่าinsmodไม่สามารถโหลดโมดูลได้เนื่องจากการเชื่อมโยงเกิดขึ้นไม่ถูกต้อง

คำถาม:

  • วิธีอ่าน / เขียนภายในเคอร์เนลหลังจาก 2.6.22 (โดยที่ sys_read() / sys_open()ไม่ถูกส่งออก)
  • โดยทั่วไปจะใช้การเรียกระบบที่ห่อด้วยมาโครSYSCALL_DEFINEn()จากภายในเคอร์เนลได้อย่างไร?

คำตอบ:


124

คุณควรทราบว่าคุณควรหลีกเลี่ยงไฟล์ I / O จากภายในเคอร์เนล Linux เมื่อเป็นไปได้ แนวคิดหลักคือไป "ลึกระดับหนึ่ง" และเรียกใช้ฟังก์ชันระดับ VFSแทนตัวจัดการ syscall โดยตรง:

รวมถึง:

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

การเปิดไฟล์ (คล้ายกับการเปิด):

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

ปิดไฟล์ (คล้ายกับปิด):

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

การอ่านข้อมูลจากไฟล์ (คล้ายกับ 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;
}   

การเขียนข้อมูลลงในไฟล์ (คล้ายกับ 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;
}

การซิงค์เปลี่ยนไฟล์ (คล้ายกับ fsync):

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

[แก้ไข] เดิมทีฉันเสนอให้ใช้ file_fsync ซึ่งหายไปในเวอร์ชันเคอร์เนลที่ใหม่กว่า ต้องขอบคุณชายผู้น่าสงสารที่แนะนำการเปลี่ยนแปลง แต่การเปลี่ยนแปลงถูกปฏิเสธ การแก้ไขถูกปฏิเสธก่อนที่ฉันจะตรวจสอบได้


2
ขอขอบคุณ. ฉันคิดว่าจะทำบางอย่างที่คล้ายกันโดยจำลองฟังก์ชัน sys_read / sys_open แต่นี่เป็นความช่วยเหลือที่ดี ความอยากรู้มีวิธีใดบ้างในการใช้การเรียกระบบที่ประกาศโดยใช้ SYSCALL_DEFINE
Methos

5
ฉันลองใช้รหัสนี้ในเคอร์เนล 2.6.30 (Ubuntu 9.04) และการอ่านไฟล์ขัดข้องระบบ ใครประสบปัญหาเดียวกัน?
Enrico Detoma

@Enrico Detoma? โอ้ว้าว. มีวิธีใดบ้างที่คุณสามารถให้โมดูลที่คุณใช้แก่ฉันได้? ไม่เคยเห็นมาก่อน?
dmeister

2
นั่นทำให้เกิดคำถามขึ้นทันทีว่า "ทำไมคุณถึงเต้น FS แบบนั้น btw" ซึ่งมีคำตอบค่อนข้างดีที่นี่: linuxjournal.com/node/8110/printภายใต้ส่วน "Fixing the Address Space"
PypeBros

@dmeister วัตถุไม่พบสำหรับการเชื่อมโยง ur VFS ฟังก์ชั่นระดับ
sree

24

ตั้งแต่เคอร์เนล Linux เวอร์ชัน 4.14 vfs_readและvfs_writeฟังก์ชันต่างๆจะไม่ถูกเอ็กซ์พอร์ตเพื่อใช้ในโมดูลอีกต่อไป แต่จะมีฟังก์ชั่นเฉพาะสำหรับการเข้าถึงไฟล์ของเคอร์เนล:

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

นอกจากนี้filp_openไม่ยอมรับสตริงพื้นที่ผู้ใช้อีกต่อไปดังนั้นจึงสามารถใช้สำหรับการเข้าถึงเคอร์เนลได้โดยตรง (โดยไม่ต้องเต้นด้วยset_fs)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.