ハードリンク(ディレクトリ)


ディレクトリをリンクできません。物理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  hoge2
lkmをinsmodします。エラーはrmmodの手間を省くため、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]# 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]# 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]#

補足

リンク元のディレクトリ配下に、リンク先の親ディレクトリをリンクしているリンクを有していた場合、ファイル検索で回帰的になってしまいます。path検索ではリンクによる無限回帰の処理は施されていますが、カーネル機能としてよろしくありません。それを避けるためには、リンク元のディレクトリ配下全てに、そのようなリンクがないかチェックする雑多な処理が必要となります。

以上、これらの事を踏まえてまで、あえてディレクトリのリンク実装というのは、実務上の効用を推し量ると、あまりにも意味がないということでしょう・・・。


最終更新 2015/03/07 02:14:10 - north
(2015/03/06 22:18:32 作成)


検索

アクセス数
3697955
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。