ハードリンク
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); }