読者です 読者をやめる 読者になる 読者になる

作成したファイル(struct dentry *)の読み書き用バッファをプロセス等固有に持たせるめも

linux kernel

たとえば、debugfsのディレクトリとファイルをプロセス毎に作って、ファイルの中身はプロセスごとに変えたいってことをしたい場合にどうするかというメモです。

foobarという機能がdebugfsのルートディレクトリにディレクトリを作成して、piyoというプロセスはfoobar/piyo、poyoならfoobar/poyoとディレクトリができます。で、ファイルはfileという名前で固定されているけど、内容は個々に違う感じです。

/sys/kernel/debugfs/foobar --- /piyo/file
                           |-- /poyo/file

piyo/、poyo/は任意のタイミングで作成できて、foobarはstart_main()の時に初期化の関数を呼んで作るものとします。

foobar用に1つだけファイルを作成するならバッファは一つで良いのでファイルのdentryはこんな感じで作れます。

// ファイルの読み書き操作の関数設定
static struct file_operations foobar_file_fops = {
    .read = fooabr_file_read,
    .write = foobar_file_write,
};

// /sys/kernel/debugfs/foobarのdentry
static struct dentry *foobar_dir;

// fileの読み書き用バッファ
static char file_buf[64];

// どこかの関数でファイルの作成
foobar_file = debugfs_create_file("file", 0644, foobar_dir, file_buf, &foobar_file_fops);

今回やりたいのは、file_bufをstaticにしないで、プロセスとかインスタンスごとに変えるってことです。

ファイルの読み書き時にはfile構造体は受け取れます。なので、ここから固有のデータにアクセスするのが良いですね。

ssize_t foobar_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
ssize_t foobar_file_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);

この時にfile構造体からfile_dentry()を使ってdentry構造体を取得できるんですが、ここでcontainer_ofマクロを使ってもポインタが違うので、正しいデータの取得ができません。よって、別の手段を取る必要があります。

で、ここで使えるのがinode構造体のi_private変数です。この変数にデータを入れるのってNamespacesのnsfs.cでも使ってる方法です。file構造体からinodeの取得はfile_inode()でできます。

今作りかけのやつですけど、実際に書くとこんな感じになります。

struct kstub {
    const char *old_name;
    struct module *module;

    struct klp_func funcs[2];
    struct klp_object objs[2];
    struct klp_patch patch;
    
    struct dentry *this_stub_dir;
    struct dentry *cmd_file;
    char cmd_file_buf[32];
};

static struct dentry *kstub_debugfs_dir;

static inline struct kstub *kstub_get_kstub_from_file(struct file *filp)
{
    return file_inode(filp)->i_private;
}

static ssize_t kstub_cmd_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
    struct kstub *kstub = kstub_get_kstub_from_file(filp);

    return simple_read_from_buffer(buf, len, ppos, kstub->cmd_file_buf, sizeof(kstub->cmd_file_buf));
}

static ssize_t kstub_cmd_file_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
    struct kstub *kstub = kstub_get_kstub_from_file(filp);
    int copy_len = len >= sizeof(kstub->cmd_file_buf) ? sizeof(kstub->cmd_file_buf) : len;

    memset(kstub->cmd_file_buf, 0x0, sizeof(kstub->cmd_file_buf));
    return simple_write_to_buffer(kstub->cmd_file_buf, copy_len, ppos, buf, len);
}

static struct file_operations kstub_cmd_file_fops = {
    .read = kstub_cmd_file_read,
    .write = kstub_cmd_file_write,
};

static int kstub_create_cmd_file(struct kstub *kstub) 
{
    int err = 0;

    kstub_cmd_file_fops.owner = kstub->module;
    kstub->cmd_file = debugfs_create_file(KSTUB_DEBUG_CMD_FILE, 0644, kstub->this_stub_dir, kstub->cmd_file_buf, &kstub_cmd_file_fops);
    
    err = kstub_check_debugfs_ops_result(kstub->cmd_file);
    if (err) 
        return err;

    kstub->cmd_file->d_inode->i_private = kstub;

    return err;
}

これでやりたかったことができます( ´∀`)bグッ!

[root@kerntest kstub]# tree
.
├── cmdline_proc_show
│   └── cmd
└── version_proc_show
    └── cmd

2 directories, 2 files
[root@kerntest kstub]# echo foobar > cmdline_proc_show/cmd
[root@kerntest kstub]# echo hogehoge > version_proc_show/cmd
[root@kerntest kstub]# cat cmdline_proc_show/cmd version_proc_show/cmd
foobar
hogehoge

( ´ー`)フゥー...

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版