ハードリンク(ディレクトリ)
Rev.2を表示中。最新版はこちら。
ディレクトリをリンクできません。物理inodeを共通する実装からすると、何故ディレクトリがリンクできないのか?と検証しました。下記サンプルは、vfs_link()の処理でリンク元がディレクトリかどうかのチェックを含め、雑多なエラーチェックをカットし、ディレクトリをハードリンクできるようにしたlkmです。リンク元が/home/kitamura/dirlink/old、リンク先が/home/kitamura/dirlink/neweとなります。#include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/string.h> #include <linux/mman.h> #include <linux/fs.h> #include <linux/namei.h> #include <linux/swap.h> MODULE_DESCRIPTION("link_dir"); unsigned long get_useraddr(void) { unsigned long addr; addr = do_mmap(NULL, 0, 4012, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0); return addr; } int hoge_vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct inode *inode = old_dentry->d_inode; int error; mutex_lock(&inode->i_mutex); if (inode->i_nlink == 0) error = -ENOENT; else error = dir->i_op->link(old_dentry, dir, new_dentry); mutex_unlock(&inode->i_mutex); return error; } int hoge_linkat(const char __user * oldname, const char __user * newname) { struct dentry *new_dentry; struct path old_path, new_path; int error; error = user_path_at(AT_FDCWD, oldname, 0, &old_path); if (error) { printk("err:user_path_at\n"); return error; } new_dentry = user_path_create(AT_FDCWD, newname, &new_path, 0); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto out; error = -EXDEV; error = hoge_vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry); if (error) { printk("err:vfs_link\n"); } dput(new_dentry); mutex_unlock(&new_path.dentry->d_inode->i_mutex); path_put(&new_path); out: path_put(&old_path); return error; } static int __init link_dir_init(void) { int err; char __user *oldname, *newname; struct mm_struct *mm = current->mm; oldname = (char __user *)get_useraddr(); newname = (char __user *)get_useraddr(); strcpy(oldname, "/home/kitamura/dirlink/old"); strcpy(newname, "/home/kitamura/dirlink/new"); err = hoge_linkat((const char __user *)oldname, (const char __user *)newname); if (err) { printk("err\n"); } do_munmap(mm, (unsigned long)oldname, 4012); do_munmap(mm, (unsigned long)newname, 4012); return -1; } MODULE_LICENSE("GPL"); module_init(link_dir_init);
検証
/home/kitamura/dirlink/oldリンク元のディレクトリ作成し、配下にhoge1/hoge2ファイルを作成します。[root@localhost kitamura]# mkdir dirlink [root@localhost kitamura]# mkdir dirlink/old [root@localhost kitamura]# echo 12345 > ./dirlink/old/hoge1 [root@localhost kitamura]# echo 67890 > ./dirlink/old/hoge2 [root@localhost kitamura]# ls dirlink/old/ hoge1 hoge2lkmをinsmodします。エラーはdelmodの手間を省くため、link_dir.koをカーネルに登録させないためmodule_init()の帰り値を-1としているからです。
ハードリンク/home/kitamura/dirlink/new作成され、配下のhoge1/hoge2も参照可能となっています。結論としてディレクトリも問題なくリンク可能と言う事です
[root@localhost lkm]# insmod link_dir.ko insmod: error inserting 'link_dir.ko': -1 Operation not permitted [root@localhost kitamura]# ls dirlink/ new old [root@localhost kitamura]# ls dirlink/new hoge1 hoge2 [root@localhost kitamura]# cat dirlink/new/hoge1 12345 [root@localhost kitamura]# cat dirlink/new/hoge2 67890リンク先/home/kitamura/dirlink/newを削除してみます。ハードリンクはリンク元が影響されません。確かに/home/kitamura/dirlink/oldは問題ないですが、配下のhoge1/hoge2は削除されています。ディレクトリのリンクの場合、配下のファイル群全ても参照カウンタをインクリメントする必要があるわけです。
[root@localhost kitamura]# rm -r dirlink/new rm: descend into directory `dirlink/new'? y rm: remove regular file `dirlink/new/hoge1'? y rm: remove regular file `dirlink/new/hoge2'? y rm: remove directory `dirlink/new'? y [root@localhost kitamura]# ls dirlink/ old [root@localhost kitamura]# ls dirlink/old/ [root@localhost kitamura]#
ramfs下でリンクした場合です。結論としてramfsはディレクトリをリンクはできますが、配下のファイルは参照されません。
ramfsはキャッシュdentryで、vfsを構築しており、ディレクトリをリンクした場合、かかる配下のdentryも、キャッシュ上のdentryを処理しなければなりません。物理デバイスの場合、ディレクトリinodeが更新されると、キャッシュdentryが削除され、改めて読み込まれますが、ramfsはvfsとしての実デバイスそのもが存在しません。
[root@localhost kitamura]# mount -t ramfs none dirlink/ [root@localhost kitamura]# ls dirlink/ [root@localhost kitamura]# mkdir dirlink/old [root@localhost kitamura]# echo 12345 > ./dirlink/old/hoge1 [root@localhost kitamura]# echo 67890 > ./dirlink/old/hoge2 [root@localhost kitamura]# ls ./dirlink/ old [root@localhost kitamura]# ls ./dirlink/ new old [root@localhost kitamura]# cat ./dirlink/old/hoge1 12345 [root@localhost kitamura]# cat ./dirlink/old/hoge2 67890 [root@localhost lkm]# insmod link_dir.ko insmod: error inserting 'link_dir.ko': -1 Operation not permitted [root@localhost kitamura]# ls ./dirlink/ new old [root@localhost kitamura]# ls ./dirlink/new/ [root@localhost kitamura]#