diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2019-09-11 09:00:01 +0200 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2019-09-11 12:46:14 +0200 |
commit | e9c03af21cc7e5723d4f1e90fe45d2cdccb70dc7 (patch) | |
tree | a6daaac424c8aa5a827de19a2b21ea9a8636de04 /fs/configfs/symlink.c | |
parent | 2743c515a1239bb96028bddafef87c0a486f4361 (diff) |
configfs: calculate the symlink target only once
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/configfs/symlink.c')
-rw-r--r-- | fs/configfs/symlink.c | 164 |
1 files changed, 54 insertions, 110 deletions
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index f3881e4caedd..dc5dbf6a81d7 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -55,41 +55,63 @@ static void fill_item_path(struct config_item * item, char * buffer, int length) } } +static int configfs_get_target_path(struct config_item *item, + struct config_item *target, char *path) +{ + int depth, size; + char *s; + + depth = item_depth(item); + size = item_path_length(target) + depth * 3 - 1; + if (size > PATH_MAX) + return -ENAMETOOLONG; + + pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size); + + for (s = path; depth--; s += 3) + strcpy(s,"../"); + + fill_item_path(target, path, size); + pr_debug("%s: path = '%s'\n", __func__, path); + return 0; +} + static int create_link(struct config_item *parent_item, struct config_item *item, struct dentry *dentry) { struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; - struct configfs_symlink *sl; + char *body; int ret; - ret = -ENOENT; if (!configfs_dirent_is_ready(target_sd)) - goto out; - ret = -ENOMEM; - sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); - if (sl) { + return -ENOENT; + + body = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!body) + return -ENOMEM; + + configfs_get(target_sd); + spin_lock(&configfs_dirent_lock); + if (target_sd->s_type & CONFIGFS_USET_DROPPING) { + spin_unlock(&configfs_dirent_lock); + configfs_put(target_sd); + kfree(body); + return -ENOENT; + } + target_sd->s_links++; + spin_unlock(&configfs_dirent_lock); + ret = configfs_get_target_path(item, item, body); + if (!ret) + ret = configfs_create_link(target_sd, parent_item->ci_dentry, + dentry, body); + if (ret) { spin_lock(&configfs_dirent_lock); - if (target_sd->s_type & CONFIGFS_USET_DROPPING) { - spin_unlock(&configfs_dirent_lock); - kfree(sl); - return -ENOENT; - } - sl->sl_target = config_item_get(item); - list_add(&sl->sl_list, &target_sd->s_links); + target_sd->s_links--; spin_unlock(&configfs_dirent_lock); - ret = configfs_create_link(sl, parent_item->ci_dentry, - dentry); - if (ret) { - spin_lock(&configfs_dirent_lock); - list_del_init(&sl->sl_list); - spin_unlock(&configfs_dirent_lock); - config_item_put(item); - kfree(sl); - } + configfs_put(target_sd); + kfree(body); } - -out: return ret; } @@ -131,9 +153,8 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna * Fake invisibility if dir belongs to a group/default groups hierarchy * being attached */ - ret = -ENOENT; if (!configfs_dirent_is_ready(sd)) - goto out; + return -ENOENT; parent_item = configfs_get_config_item(dentry->d_parent); type = parent_item->ci_type; @@ -193,15 +214,12 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna out_put: config_item_put(parent_item); - -out: return ret; } int configfs_unlink(struct inode *dir, struct dentry *dentry) { - struct configfs_dirent *sd = dentry->d_fsdata; - struct configfs_symlink *sl; + struct configfs_dirent *sd = dentry->d_fsdata, *target_sd; struct config_item *parent_item; const struct config_item_type *type; int ret; @@ -210,7 +228,7 @@ int configfs_unlink(struct inode *dir, struct dentry *dentry) if (!(sd->s_type & CONFIGFS_ITEM_LINK)) goto out; - sl = sd->s_element; + target_sd = sd->s_element; parent_item = configfs_get_config_item(dentry->d_parent); type = parent_item->ci_type; @@ -224,21 +242,18 @@ int configfs_unlink(struct inode *dir, struct dentry *dentry) /* * drop_link() must be called before - * list_del_init(&sl->sl_list), so that the order of + * decrementing target's ->s_links, so that the order of * drop_link(this, target) and drop_item(target) is preserved. */ if (type && type->ct_item_ops && type->ct_item_ops->drop_link) type->ct_item_ops->drop_link(parent_item, - sl->sl_target); + target_sd->s_element); spin_lock(&configfs_dirent_lock); - list_del_init(&sl->sl_list); + target_sd->s_links--; spin_unlock(&configfs_dirent_lock); - - /* Put reference from create_link() */ - config_item_put(sl->sl_target); - kfree(sl); + configfs_put(target_sd); config_item_put(parent_item); @@ -248,79 +263,8 @@ out: return ret; } -static int configfs_get_target_path(struct config_item * item, struct config_item * target, - char *path) -{ - char * s; - int depth, size; - - depth = item_depth(item); - size = item_path_length(target) + depth * 3 - 1; - if (size > PATH_MAX) - return -ENAMETOOLONG; - - pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size); - - for (s = path; depth--; s += 3) - strcpy(s,"../"); - - fill_item_path(target, path, size); - pr_debug("%s: path = '%s'\n", __func__, path); - - return 0; -} - -static int configfs_getlink(struct dentry *dentry, char * path) -{ - struct config_item *item, *target_item; - int error = 0; - - item = configfs_get_config_item(dentry->d_parent); - if (!item) - return -EINVAL; - - target_item = configfs_get_config_item(dentry); - if (!target_item) { - config_item_put(item); - return -EINVAL; - } - - down_read(&configfs_rename_sem); - error = configfs_get_target_path(item, target_item, path); - up_read(&configfs_rename_sem); - - config_item_put(item); - config_item_put(target_item); - return error; - -} - -static const char *configfs_get_link(struct dentry *dentry, - struct inode *inode, - struct delayed_call *done) -{ - char *body; - int error; - - if (!dentry) - return ERR_PTR(-ECHILD); - - body = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!body) - return ERR_PTR(-ENOMEM); - - error = configfs_getlink(dentry, body); - if (!error) { - set_delayed_call(done, kfree_link, body); - return body; - } - - kfree(body); - return ERR_PTR(error); -} - const struct inode_operations configfs_symlink_inode_operations = { - .get_link = configfs_get_link, + .get_link = simple_get_link, .setattr = configfs_setattr, }; |