271 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * symlink.c - operations for configfs symlinks.4 *5 * Based on sysfs:6 * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel7 *8 * configfs Copyright (C) 2005 Oracle. All rights reserved.9 */10 11#include <linux/fs.h>12#include <linux/module.h>13#include <linux/namei.h>14#include <linux/slab.h>15 16#include <linux/configfs.h>17#include "configfs_internal.h"18 19/* Protects attachments of new symlinks */20DEFINE_MUTEX(configfs_symlink_mutex);21 22static int item_depth(struct config_item * item)23{24 struct config_item * p = item;25 int depth = 0;26 do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p));27 return depth;28}29 30static int item_path_length(struct config_item * item)31{32 struct config_item * p = item;33 int length = 1;34 do {35 length += strlen(config_item_name(p)) + 1;36 p = p->ci_parent;37 } while (p && !configfs_is_root(p));38 return length;39}40 41static void fill_item_path(struct config_item * item, char * buffer, int length)42{43 struct config_item * p;44 45 --length;46 for (p = item; p && !configfs_is_root(p); p = p->ci_parent) {47 int cur = strlen(config_item_name(p));48 49 /* back up enough to print this bus id with '/' */50 length -= cur;51 memcpy(buffer + length, config_item_name(p), cur);52 *(buffer + --length) = '/';53 }54}55 56static int configfs_get_target_path(struct config_item *item,57 struct config_item *target, char *path)58{59 int depth, size;60 char *s;61 62 depth = item_depth(item);63 size = item_path_length(target) + depth * 3 - 1;64 if (size > PATH_MAX)65 return -ENAMETOOLONG;66 67 pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size);68 69 for (s = path; depth--; s += 3)70 strcpy(s,"../");71 72 fill_item_path(target, path, size);73 pr_debug("%s: path = '%s'\n", __func__, path);74 return 0;75}76 77static int create_link(struct config_item *parent_item,78 struct config_item *item,79 struct dentry *dentry)80{81 struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata;82 char *body;83 int ret;84 85 if (!configfs_dirent_is_ready(target_sd))86 return -ENOENT;87 88 body = kzalloc(PAGE_SIZE, GFP_KERNEL);89 if (!body)90 return -ENOMEM;91 92 configfs_get(target_sd);93 spin_lock(&configfs_dirent_lock);94 if (target_sd->s_type & CONFIGFS_USET_DROPPING) {95 spin_unlock(&configfs_dirent_lock);96 configfs_put(target_sd);97 kfree(body);98 return -ENOENT;99 }100 target_sd->s_links++;101 spin_unlock(&configfs_dirent_lock);102 ret = configfs_get_target_path(parent_item, item, body);103 if (!ret)104 ret = configfs_create_link(target_sd, parent_item->ci_dentry,105 dentry, body);106 if (ret) {107 spin_lock(&configfs_dirent_lock);108 target_sd->s_links--;109 spin_unlock(&configfs_dirent_lock);110 configfs_put(target_sd);111 kfree(body);112 }113 return ret;114}115 116 117static int get_target(const char *symname, struct path *path,118 struct config_item **target, struct super_block *sb)119{120 int ret;121 122 ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path);123 if (!ret) {124 if (path->dentry->d_sb == sb) {125 *target = configfs_get_config_item(path->dentry);126 if (!*target) {127 ret = -ENOENT;128 path_put(path);129 }130 } else {131 ret = -EPERM;132 path_put(path);133 }134 }135 136 return ret;137}138 139 140int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir,141 struct dentry *dentry, const char *symname)142{143 int ret;144 struct path path;145 struct configfs_dirent *sd;146 struct config_item *parent_item;147 struct config_item *target_item = NULL;148 const struct config_item_type *type;149 150 sd = dentry->d_parent->d_fsdata;151 /*152 * Fake invisibility if dir belongs to a group/default groups hierarchy153 * being attached154 */155 if (!configfs_dirent_is_ready(sd))156 return -ENOENT;157 158 parent_item = configfs_get_config_item(dentry->d_parent);159 type = parent_item->ci_type;160 161 ret = -EPERM;162 if (!type || !type->ct_item_ops ||163 !type->ct_item_ops->allow_link)164 goto out_put;165 166 /*167 * This is really sick. What they wanted was a hybrid of168 * link(2) and symlink(2) - they wanted the target resolved169 * at syscall time (as link(2) would've done), be a directory170 * (which link(2) would've refused to do) *AND* be a deep171 * fucking magic, making the target busy from rmdir POV.172 * symlink(2) is nothing of that sort, and the locking it173 * gets matches the normal symlink(2) semantics. Without174 * attempts to resolve the target (which might very well175 * not even exist yet) done prior to locking the parent176 * directory. This perversion, OTOH, needs to resolve177 * the target, which would lead to obvious deadlocks if178 * attempted with any directories locked.179 *180 * Unfortunately, that garbage is userland ABI and we should've181 * said "no" back in 2005. Too late now, so we get to182 * play very ugly games with locking.183 *184 * Try *ANYTHING* of that sort in new code, and you will185 * really regret it. Just ask yourself - what could a BOFH186 * do to me and do I want to find it out first-hand?187 *188 * AV, a thoroughly annoyed bastard.189 */190 inode_unlock(dir);191 ret = get_target(symname, &path, &target_item, dentry->d_sb);192 inode_lock(dir);193 if (ret)194 goto out_put;195 196 if (dentry->d_inode || d_unhashed(dentry))197 ret = -EEXIST;198 else199 ret = inode_permission(&nop_mnt_idmap, dir,200 MAY_WRITE | MAY_EXEC);201 if (!ret)202 ret = type->ct_item_ops->allow_link(parent_item, target_item);203 if (!ret) {204 mutex_lock(&configfs_symlink_mutex);205 ret = create_link(parent_item, target_item, dentry);206 mutex_unlock(&configfs_symlink_mutex);207 if (ret && type->ct_item_ops->drop_link)208 type->ct_item_ops->drop_link(parent_item,209 target_item);210 }211 212 config_item_put(target_item);213 path_put(&path);214 215out_put:216 config_item_put(parent_item);217 return ret;218}219 220int configfs_unlink(struct inode *dir, struct dentry *dentry)221{222 struct configfs_dirent *sd = dentry->d_fsdata, *target_sd;223 struct config_item *parent_item;224 const struct config_item_type *type;225 int ret;226 227 ret = -EPERM; /* What lack-of-symlink returns */228 if (!(sd->s_type & CONFIGFS_ITEM_LINK))229 goto out;230 231 target_sd = sd->s_element;232 233 parent_item = configfs_get_config_item(dentry->d_parent);234 type = parent_item->ci_type;235 236 spin_lock(&configfs_dirent_lock);237 list_del_init(&sd->s_sibling);238 spin_unlock(&configfs_dirent_lock);239 configfs_drop_dentry(sd, dentry->d_parent);240 dput(dentry);241 configfs_put(sd);242 243 /*244 * drop_link() must be called before245 * decrementing target's ->s_links, so that the order of246 * drop_link(this, target) and drop_item(target) is preserved.247 */248 if (type && type->ct_item_ops &&249 type->ct_item_ops->drop_link)250 type->ct_item_ops->drop_link(parent_item,251 target_sd->s_element);252 253 spin_lock(&configfs_dirent_lock);254 target_sd->s_links--;255 spin_unlock(&configfs_dirent_lock);256 configfs_put(target_sd);257 258 config_item_put(parent_item);259 260 ret = 0;261 262out:263 return ret;264}265 266const struct inode_operations configfs_symlink_inode_operations = {267 .get_link = simple_get_link,268 .setattr = configfs_setattr,269};270 271