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




