linux.3.3.8のvfsmount
動作環境はfedora16のlinux.3.3.8-1で、記事の内容はlinux.2.6.0です。mount実装の検証のためLKMを作成したのですが、どうもうまくいきません。そこでカーネルソースもlinux-3.3.8に更新しました。で、やはりその実装は変更されておりました。
linux.2.6.0では、vfsmount構造体内にかかる全ての情報を有していて、しかもLKMからの参照も可能となっています。しかしlinux.3.3.8-1のvfsmount構造体内は、動作に必要なdentry/super_block/mnt_flagsとし、LIST_HEADを初めとするそれ以外の管理情報は、mount構造体内に設定されるようになっています。また、マウント情報としても新たに追加項目もあり、LIST_HEADに関しては、共有/スレーブ/有効期限等の管理のためのリストヘッドが追加されています。しかも、LKMからこのmount構造体を参照することはできなくなっています。(たぶんセキュリティ故のことだと思いますが。)
linux.2.6.0のvfsmount
linux.2.6.0では、vfsmount構造体内にかかる全ての情報を有していて、しかもLKMからの参照も可能となっています。しかしlinux.3.3.8-1のvfsmount構造体内は、動作に必要なdentry/super_block/mnt_flagsとし、LIST_HEADを初めとするそれ以外の管理情報は、mount構造体内に設定されるようになっています。また、マウント情報としても新たに追加項目もあり、LIST_HEADに関しては、共有/スレーブ/有効期限等の管理のためのリストヘッドが追加されています。しかも、LKMからこのmount構造体を参照することはできなくなっています。(たぶんセキュリティ故のことだと思いますが。)
linux.2.6.0のvfsmount
struct vfsmount { struct list_head mnt_hash; struct vfsmount *mnt_parent; /* fs we are mounted on */ struct dentry *mnt_mountpoint; /* dentry of mountpoint */ struct dentry *mnt_root; /* root of the mounted tree */ struct super_block *mnt_sb; /* pointer to superblock */ struct list_head mnt_mounts; /* list of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ atomic_t mnt_count; int mnt_flags; char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; };linux.3.3.8-1のvfsmount
struct vfsmount { struct dentry *mnt_root; /* root of the mounted tree */ struct super_block *mnt_sb; /* pointer to superblock */ int mnt_flags; }; struct mount { struct list_head mnt_hash; struct mount *mnt_parent; struct dentry *mnt_mountpoint; struct vfsmount mnt; int mnt_count; int mnt_writers; struct list_head mnt_mounts; /* list of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ struct list_head mnt_instance; /* mount instance on sb->s_mounts */ const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ struct list_head mnt_share; /* circular list of shared mounts */ struct list_head mnt_slave_list;/* list of slave mounts */ struct list_head mnt_slave; /* slave list entry */ struct mount *mnt_master; /* slave is on master->mnt_slave_list */ struct mnt_namespace *mnt_ns; /* containing namespace */ int mnt_id; /* mount identifier */ int mnt_group_id; /* peer group identifier */ int mnt_expiry_mark; /* true if marked for expiry */ int mnt_pinned; int mnt_ghosts; };マウント処理におけるカーネルの実装は、vfsmountでreal_mount()をコールする事で行っています。 vfsmoun構造体/mount構造体は別々に有していると書きましたが、vfsmoun構造体の実態は、mount構造体のmntメンバーになり、list_headの処理で使われるcontainer_of()にて、mount構造体を取得できるわけです。
static inline struct mount *real_mount(struct vfsmount *mnt) { return container_of(mnt, struct mount, mnt); }LKMからmnt構造体を取得するサンプルです。先に書いたようにmount構造他および掛かるマクロ/関数を使用する事はできません。ここでは、struct mount/real_mount()をカーネルソースからのLKM内に埋め込む事で実現しました。
#include <linux/kernel.h> #include <linux/namei.h> #include <linux/mnt_namespace.h> #include <linux/mount.h> struct mount { struct list_head mnt_hash; struct mount *mnt_parent; struct dentry *mnt_mountpoint; struct vfsmount mnt; int mnt_count; int mnt_writers; struct list_head mnt_mounts; /* list of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ struct list_head mnt_instance; /* mount instance on sb->s_mounts */ const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ struct list_head mnt_share; /* circular list of shared mounts */ struct list_head mnt_slave_list;/* list of slave mounts */ struct list_head mnt_slave; /* slave list entry */ struct mount *mnt_master; /* slave is on master->mnt_slave_list */ struct mnt_namespace *mnt_ns; /* containing namespace */ int mnt_id; /* mount identifier */ int mnt_group_id; /* peer group identifier */ int mnt_expiry_mark; /* true if marked for expiry */ int mnt_pinned; int mnt_ghosts; }; static inline struct mount *real_mount(struct vfsmount *mnt) { return container_of(mnt, struct mount, mnt); } void get_mnt(char *pathname) { struct path path; struct mount *mnt; int err; err = kern_path(pathname, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path); if (err) { printk("err\n"); } else { mnt = real_mount(path.mnt); printk("%s\n", mnt->mnt_devname); } } int init_module(void) { get_mnt("/"); get_mnt("/sys"); get_mnt("/proc"); return 0; } void cleanup_module(void) { }
[root@localhost lkm]# insmod mnt_get.ko [root@localhost lkm]# dmesg : : [28732.966463] pcnet32 0000:02:01.0: eth0: link down [28771.005229] pcnet32 0000:02:01.0: eth0: link up [29651.255533] /dev/mapper/VolGroup-lv_root [29651.255552] sysfs [29651.255557] prockern_path()のフラグLOOKUP_FOLLOWはシンボリックリンクの有効、LOOKUP_AUTOMOUNTは動的なマウント(この時dentryのd_automountコールバック関数がコールされ、所定のマウント処理が行われるようです。)を有効とする。との設定のようです。
補足
サンプルのように、理屈の上ではカーネルソースからLKMで何でもできるわけですが、mount構造体にダミーのメンバーを入れ、オリジナル(?)なカーネルを作れば、サンプルのようにmount構造体を参照する事はできなくなります。追記
LKMの実装においても、module_initマクロによるセクションへ配置することなく、init_moduleアドレスで__NR_init_moduleでシステムコールをコールすることで、insmodできるようになっています。#define __NR_init_module 128 #define __NR_delete_module 129