ハードリンク


Rev.1を表示中。最新版はこちら

ハードリンクはlinkシステムコールからpath検索をAT_FDCWD(カレントパスからの検索)でlinkaシステムコールをコールします。oldnameがリンク元、newnameがリンク先となります。

user_path_at()でリンク元のstruct pathを、user_path_create()でリンク先のpathを作成し、そのstruct pathを取得します。struct pathはdentry/inode属性を有しています。なお、リンク先のpathは、linkコールバックの関係上、ディレクトリとして作成されます。

if (old_path.mnt != new_path.mnt)でリンク元/リンク先が同じファイルシステムでないとエラーです。ハードリンクは異なるファイルシステムに作成することはできません。

mnt_want_write()は、リンク先が書込み可でマウントされているかチェックし、vfs_link()でハードリンクを作成します。
SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname)
{
       return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
}

SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
               int, newdfd, const char __user *, newname, int, flags)
{
       struct dentry *new_dentry;
       struct path old_path, new_path;
       int how = 0;
       int error;

       if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
               return -EINVAL;

       if (flags & AT_EMPTY_PATH) {
               if (!capable(CAP_DAC_READ_SEARCH))
                       return -ENOENT;
               how = LOOKUP_EMPTY;
       }

       if (flags & AT_SYMLINK_FOLLOW)
               how |= LOOKUP_FOLLOW;

       error = user_path_at(olddfd, oldname, how, &old_path);
       if (error)
               return error;

       new_dentry = user_path_create(newdfd, newname, &new_path, 0);
       error = PTR_ERR(new_dentry);
       if (IS_ERR(new_dentry))
               goto out;

       error = -EXDEV;
       if (old_path.mnt != new_path.mnt)
               goto out_dput;
       error = mnt_want_write(new_path.mnt);
       if (error)
               goto out_dput;
       error = security_path_link(old_path.dentry, &new_path, new_dentry);
       if (error)
               goto out_drop_write;
       error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry);
out_drop_write:
       mnt_drop_write(new_path.mnt);
out_dput:
       dput(new_dentry);
       mutex_unlock(&new_path.dentry->d_inode->i_mutex);
       path_put(&new_path);
out:
       path_put(&old_path);

       return error;
}
old_dentryはリンク元、dir/new_dentryはリンク先情報です。再度inodeレベルで書込み有無/ファイルシステムが同じかのチェックを行っています。冗長的かと思いますが、たぶんlinkatシステムコールを介さないで、カーネル内から直接vfs_link()をコールするケースを想定してかと思います。

リンク元が、APPEND(追加のみ)/IMMUTABLE(変更不可)、そしてディレクトリだとエラーです。OKだとディレクトリコールバックのlinkをコールします。
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
       struct inode *inode = old_dentry->d_inode;
       int error;

       if (!inode)
               return -ENOENT;

       error = may_create(dir, new_dentry);
       if (error)
               return error;

       if (dir->i_sb != inode->i_sb)
               return -EXDEV;

       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
               return -EPERM;
       if (!dir->i_op->link)
               return -EPERM;
       if (S_ISDIR(inode->i_mode))
               return -EPERM;

       error = security_inode_link(old_dentry, dir, new_dentry);
       if (error)
               return 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);
       if (!error)
               fsnotify_link(dir, inode, new_dentry);
       return error;
}
ハードリンクの実装はファイルシステム依存です。ここではramfsについては、ハードリンクはsimple_link()がコールされます。
static const struct inode_operations ramfs_dir_inode_operations = {
       .create         = ramfs_create,
       .lookup         = simple_lookup,
       .link           = simple_link,
       .unlink         = simple_unlink,
       .symlink        = ramfs_symlink,
       .mkdir          = ramfs_mkdir,
       .rmdir          = simple_rmdir,
       .mknod          = ramfs_mknod,
       .rename         = simple_rename,
};
old_dentry(リンク元)のタイムスタンプが更新され(故にIMMUTABLEであってはなりません。)、d_instantiate()から_d_instantiate()をコールし、dentry->d_inode = inodeとする事で、リンク元inodeをリンク先inodeとし(故に同じファイルシステムでなければなりません。)ハードリンクを構築します。従ってハードリンクは、inodeレベルではリンク先もリンク元もありません。同等な物です。

inc_nlink()は、 inode->__i_nlink++とし、かかるinodeのファイルを削除する時、ディレクトリのdentry(ファイル名)は削除されますが、かかるファイルのブロックデータも削除するかどうか、inode->__i_nlink==0かによって決定され、1つでもハードリンクが有している限り、ファイルのデータブロックが削除されることはありません。
int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
       struct inode *inode = old_dentry->d_inode;

       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
       inc_nlink(inode);
       ihold(inode);
       dget(dentry);
       d_instantiate(dentry, inode);
       return 0;
}

static void __d_instantiate(struct dentry *dentry, struct inode *inode)
{
       spin_lock(&dentry->d_lock);
       if (inode) {
               if (unlikely(IS_AUTOMOUNT(inode)))
                       dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
               list_add(&dentry->d_alias, &inode->i_dentry);
       }
       dentry->d_inode = inode;
       dentry_rcuwalk_barrier(dentry);
       spin_unlock(&dentry->d_lock);
       fsnotify_d_instantiate(dentry, inode);
}

追記

リンク元がディレクトリだと、なぜダメなのでしょうか・・・?


最終更新 2015/03/01 18:01:54 - north
(2015/03/01 18:01:54 作成)


検索

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