シンボリックリンク
Rev.1を表示中。最新版はこちら。
シンボリックリンクは、symlink/symlinkatシステムコールからvfs_symlink()をコールし、ハードリンクのようにリンク先inodeを共有するのでなく、新規にシンボリックリンクとしてのinodeを取得します。また、リンク元がディレクトリかどうかの/ファイルシステムが同じかどうかのチェックは行いません。
SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname)
{
return sys_symlinkat(oldname, AT_FDCWD, newname);
}
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
int, newdfd, const char __user *, newname)
{
int error;
char *from;
struct dentry *dentry;
struct path path;
from = getname(oldname);
if (IS_ERR(from))
return PTR_ERR(from);
dentry = user_path_create(newdfd, newname, &path, 0);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_putname;
error = mnt_want_write(path.mnt);
if (error)
goto out_dput;
error = security_path_symlink(&path, dentry, from);
if (error)
goto out_drop_write;
error = vfs_symlink(path.dentry->d_inode, dentry, from);
out_drop_write:
mnt_drop_write(path.mnt);
out_dput:
dput(dentry);
mutex_unlock(&path.dentry->d_inode->i_mutex);
path_put(&path);
out_putname:
putname(from);
return error;
}
vfs_symlink()でディレクトリinode(リンク先の親ディレクトリ)の.symlinkコールバックをコールします。dirはリンク親ディレクトリ、dentryはリンクファイル、oldnameはリンク先ファイルです。
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,
};
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
{
int error = may_create(dir, dentry);
if (error)
return error;
if (!dir->i_op->symlink)
return -EPERM;
error = security_inode_symlink(dir, dentry, oldname);
if (error)
return error;
error = dir->i_op->symlink(dir, dentry, oldname);
if (!error)
fsnotify_create(dir, dentry);
return error;
}
ramfs_symlink()はramfsの.symlinkコールバックです。ramfs_get_inode()でramfsスーパブロックからinodeをシンボリックリンクとして取得します。この時inodeコールバックはpage_symlink_inode_operationsが設定されます。page_symlink()はファイルキャッシュのinodeアドレスマップにリンク先ファイルパスを設定します。実ファイルのファイルシステムだとリンク先親ディレクトリへに書き込みとなります。リンク参照はこのinodeマップを取得します。
const struct inode_operations page_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.put_link = page_put_link,
};
static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
{
struct inode *inode;
int error = -ENOSPC;
inode = ramfs_get_inode(dir->i_sb, dir, S_IFLNK|S_IRWXUGO, 0);
if (inode) {
int l = strlen(symname)+1;
error = page_symlink(inode, symname, l);
if (!error) {
d_instantiate(dentry, inode);
dget(dentry);
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
} else
iput(inode);
}
return error;
}
generic_readlinkはリンク先パスを取得します。*buffer引数が__userは、readlinkシステムコールからコールされます。なお実際の取得は、.follow_linkで行います。
int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
struct nameidata nd;
void *cookie;
int res;
nd.depth = 0;
cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
if (IS_ERR(cookie))
return PTR_ERR(cookie);
res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
if (dentry->d_inode->i_op->put_link)
dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
return res;
}
int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
{
int len;
len = PTR_ERR(link);
if (IS_ERR(link))
goto out;
len = strlen(link);
if (len > (unsigned) buflen)
len = buflen;
if (copy_to_user(buffer, link, len))
len = -EFAULT;
out:
return len;
}
.follow_linkコールバックでリンクパスを取得します。page_follow_link_light()はramfsのリンクパスで、はinode->i_mappingのaddress_spacから取得します。そのpathをnd->saved_names[nd->depth] = pathとし、呼び出し元はnd->saved_names[nd->depth]を次の検索pathとして処理します。nd->saved_names[nd->depth]として処理するのは、多重リンクでリカーシブルの実装となっているからです。
void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
{
struct page *page = NULL;
nd_set_link(nd, page_getlink(dentry, &page));
return page;
}
static char *page_getlink(struct dentry * dentry, struct page **ppage)
{
char *kaddr;
struct page *page;
struct address_space *mapping = dentry->d_inode->i_mapping;
page = read_mapping_page(mapping, 0, NULL);
if (IS_ERR(page))
return (char*)page;
*ppage = page;
kaddr = kmap(page);
nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
return kaddr;
}
static inline void nd_set_link(struct nameidata *nd, char *path)
{
nd->saved_names[nd->depth] = path;
}
static inline void nd_terminate_link(void *name, size_t len, size_t maxlen)
{
((char *) name)[min(len, maxlen)] = '\0';
}





