Thomas Gleixner | 328970d | 2019-05-24 12:04:05 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
Masahiro Yamada | fa60ce2 | 2021-05-06 18:06:44 -0700 | [diff] [blame] | 2 | /* |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 3 | * symlink.c - operations for configfs symlinks. |
| 4 | * |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 5 | * Based on sysfs: |
| 6 | * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel |
| 7 | * |
| 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> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 14 | #include <linux/slab.h> |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 15 | |
| 16 | #include <linux/configfs.h> |
| 17 | #include "configfs_internal.h" |
| 18 | |
Louis Rilling | 9a73d78 | 2008-06-20 14:09:22 +0200 | [diff] [blame] | 19 | /* Protects attachments of new symlinks */ |
| 20 | DEFINE_MUTEX(configfs_symlink_mutex); |
| 21 | |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 22 | static 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 | |
| 30 | static 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 | |
| 41 | static 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; |
Guenter Roeck | 1823342 | 2018-07-01 13:56:54 -0700 | [diff] [blame] | 51 | memcpy(buffer + length, config_item_name(p), cur); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 52 | *(buffer + --length) = '/'; |
| 53 | } |
| 54 | } |
| 55 | |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 56 | static 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 | |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 77 | static int create_link(struct config_item *parent_item, |
Joel Becker | e7515d0 | 2006-03-10 11:42:30 -0800 | [diff] [blame] | 78 | struct config_item *item, |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 79 | struct dentry *dentry) |
| 80 | { |
| 81 | struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 82 | char *body; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 83 | int ret; |
| 84 | |
Louis Rilling | 2a109f2 | 2008-07-04 16:56:05 +0200 | [diff] [blame] | 85 | if (!configfs_dirent_is_ready(target_sd)) |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 86 | return -ENOENT; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 87 | |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 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); |
Honggang Li | e2f238f | 2019-11-04 20:43:22 +0800 | [diff] [blame] | 102 | ret = configfs_get_target_path(parent_item, item, body); |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 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 | } |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 113 | return ret; |
| 114 | } |
| 115 | |
| 116 | |
Al Viro | 421748e | 2008-08-02 01:04:36 -0400 | [diff] [blame] | 117 | static int get_target(const char *symname, struct path *path, |
Al Viro | b7c177f | 2012-03-17 16:24:54 -0400 | [diff] [blame] | 118 | struct config_item **target, struct super_block *sb) |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 119 | { |
| 120 | int ret; |
| 121 | |
Al Viro | 421748e | 2008-08-02 01:04:36 -0400 | [diff] [blame] | 122 | ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 123 | if (!ret) { |
Al Viro | b7c177f | 2012-03-17 16:24:54 -0400 | [diff] [blame] | 124 | if (path->dentry->d_sb == sb) { |
Al Viro | 421748e | 2008-08-02 01:04:36 -0400 | [diff] [blame] | 125 | *target = configfs_get_config_item(path->dentry); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 126 | if (!*target) { |
| 127 | ret = -ENOENT; |
Al Viro | 421748e | 2008-08-02 01:04:36 -0400 | [diff] [blame] | 128 | path_put(path); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 129 | } |
Al Viro | 9b6e310 | 2010-01-13 22:10:57 -0500 | [diff] [blame] | 130 | } else { |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 131 | ret = -EPERM; |
Al Viro | 9b6e310 | 2010-01-13 22:10:57 -0500 | [diff] [blame] | 132 | path_put(path); |
| 133 | } |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | return ret; |
| 137 | } |
| 138 | |
| 139 | |
Christian Brauner | 549c729 | 2021-01-21 14:19:43 +0100 | [diff] [blame] | 140 | int configfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, |
| 141 | struct dentry *dentry, const char *symname) |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 142 | { |
| 143 | int ret; |
Al Viro | 421748e | 2008-08-02 01:04:36 -0400 | [diff] [blame] | 144 | struct path path; |
Louis Rilling | 2a109f2 | 2008-07-04 16:56:05 +0200 | [diff] [blame] | 145 | struct configfs_dirent *sd; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 146 | struct config_item *parent_item; |
Subrata Modak | 3c48f23 | 2009-04-19 01:10:03 +0530 | [diff] [blame] | 147 | struct config_item *target_item = NULL; |
Bhumika Goyal | aa29358 | 2017-10-16 17:18:40 +0200 | [diff] [blame] | 148 | const struct config_item_type *type; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 149 | |
Louis Rilling | 2a109f2 | 2008-07-04 16:56:05 +0200 | [diff] [blame] | 150 | sd = dentry->d_parent->d_fsdata; |
| 151 | /* |
| 152 | * Fake invisibility if dir belongs to a group/default groups hierarchy |
| 153 | * being attached |
| 154 | */ |
Louis Rilling | 2a109f2 | 2008-07-04 16:56:05 +0200 | [diff] [blame] | 155 | if (!configfs_dirent_is_ready(sd)) |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 156 | return -ENOENT; |
Louis Rilling | 2a109f2 | 2008-07-04 16:56:05 +0200 | [diff] [blame] | 157 | |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 158 | parent_item = configfs_get_config_item(dentry->d_parent); |
| 159 | type = parent_item->ci_type; |
| 160 | |
Louis Rilling | 2a109f2 | 2008-07-04 16:56:05 +0200 | [diff] [blame] | 161 | ret = -EPERM; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 162 | if (!type || !type->ct_item_ops || |
| 163 | !type->ct_item_ops->allow_link) |
| 164 | goto out_put; |
| 165 | |
Al Viro | 351e5d8 | 2019-08-03 11:51:18 -0400 | [diff] [blame] | 166 | /* |
| 167 | * This is really sick. What they wanted was a hybrid of |
| 168 | * link(2) and symlink(2) - they wanted the target resolved |
| 169 | * at syscall time (as link(2) would've done), be a directory |
| 170 | * (which link(2) would've refused to do) *AND* be a deep |
| 171 | * fucking magic, making the target busy from rmdir POV. |
| 172 | * symlink(2) is nothing of that sort, and the locking it |
| 173 | * gets matches the normal symlink(2) semantics. Without |
| 174 | * attempts to resolve the target (which might very well |
| 175 | * not even exist yet) done prior to locking the parent |
| 176 | * directory. This perversion, OTOH, needs to resolve |
| 177 | * the target, which would lead to obvious deadlocks if |
| 178 | * attempted with any directories locked. |
| 179 | * |
| 180 | * Unfortunately, that garbage is userland ABI and we should've |
| 181 | * said "no" back in 2005. Too late now, so we get to |
| 182 | * play very ugly games with locking. |
| 183 | * |
| 184 | * Try *ANYTHING* of that sort in new code, and you will |
| 185 | * really regret it. Just ask yourself - what could a BOFH |
| 186 | * 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); |
Al Viro | b7c177f | 2012-03-17 16:24:54 -0400 | [diff] [blame] | 191 | ret = get_target(symname, &path, &target_item, dentry->d_sb); |
Al Viro | 351e5d8 | 2019-08-03 11:51:18 -0400 | [diff] [blame] | 192 | inode_lock(dir); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 193 | if (ret) |
| 194 | goto out_put; |
| 195 | |
Al Viro | 351e5d8 | 2019-08-03 11:51:18 -0400 | [diff] [blame] | 196 | if (dentry->d_inode || d_unhashed(dentry)) |
| 197 | ret = -EEXIST; |
| 198 | else |
Christian Brauner | 47291ba | 2021-01-21 14:19:24 +0100 | [diff] [blame] | 199 | ret = inode_permission(&init_user_ns, dir, |
| 200 | MAY_WRITE | MAY_EXEC); |
Al Viro | 351e5d8 | 2019-08-03 11:51:18 -0400 | [diff] [blame] | 201 | if (!ret) |
| 202 | ret = type->ct_item_ops->allow_link(parent_item, target_item); |
Louis Rilling | e752065 | 2008-06-12 17:26:47 +0200 | [diff] [blame] | 203 | if (!ret) { |
Louis Rilling | 9a73d78 | 2008-06-20 14:09:22 +0200 | [diff] [blame] | 204 | mutex_lock(&configfs_symlink_mutex); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 205 | ret = create_link(parent_item, target_item, dentry); |
Louis Rilling | 9a73d78 | 2008-06-20 14:09:22 +0200 | [diff] [blame] | 206 | mutex_unlock(&configfs_symlink_mutex); |
Louis Rilling | e752065 | 2008-06-12 17:26:47 +0200 | [diff] [blame] | 207 | if (ret && type->ct_item_ops->drop_link) |
| 208 | type->ct_item_ops->drop_link(parent_item, |
| 209 | target_item); |
| 210 | } |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 211 | |
| 212 | config_item_put(target_item); |
Al Viro | 421748e | 2008-08-02 01:04:36 -0400 | [diff] [blame] | 213 | path_put(&path); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 214 | |
| 215 | out_put: |
| 216 | config_item_put(parent_item); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 217 | return ret; |
| 218 | } |
| 219 | |
| 220 | int configfs_unlink(struct inode *dir, struct dentry *dentry) |
| 221 | { |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 222 | struct configfs_dirent *sd = dentry->d_fsdata, *target_sd; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 223 | struct config_item *parent_item; |
Bhumika Goyal | aa29358 | 2017-10-16 17:18:40 +0200 | [diff] [blame] | 224 | const struct config_item_type *type; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 225 | int ret; |
| 226 | |
| 227 | ret = -EPERM; /* What lack-of-symlink returns */ |
| 228 | if (!(sd->s_type & CONFIGFS_ITEM_LINK)) |
| 229 | goto out; |
| 230 | |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 231 | target_sd = sd->s_element; |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 232 | |
| 233 | parent_item = configfs_get_config_item(dentry->d_parent); |
| 234 | type = parent_item->ci_type; |
| 235 | |
Louis Rilling | 6f61076 | 2008-06-16 19:00:58 +0200 | [diff] [blame] | 236 | spin_lock(&configfs_dirent_lock); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 237 | list_del_init(&sd->s_sibling); |
Louis Rilling | 6f61076 | 2008-06-16 19:00:58 +0200 | [diff] [blame] | 238 | spin_unlock(&configfs_dirent_lock); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 239 | configfs_drop_dentry(sd, dentry->d_parent); |
| 240 | dput(dentry); |
| 241 | configfs_put(sd); |
| 242 | |
| 243 | /* |
| 244 | * drop_link() must be called before |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 245 | * decrementing target's ->s_links, so that the order of |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 246 | * 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, |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 251 | target_sd->s_element); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 252 | |
Louis Rilling | 5301a77 | 2008-06-16 19:00:59 +0200 | [diff] [blame] | 253 | spin_lock(&configfs_dirent_lock); |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 254 | target_sd->s_links--; |
Louis Rilling | 5301a77 | 2008-06-16 19:00:59 +0200 | [diff] [blame] | 255 | spin_unlock(&configfs_dirent_lock); |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 256 | configfs_put(target_sd); |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 257 | |
| 258 | config_item_put(parent_item); |
| 259 | |
| 260 | ret = 0; |
| 261 | |
| 262 | out: |
| 263 | return ret; |
| 264 | } |
| 265 | |
Arjan van de Ven | 754661f | 2007-02-12 00:55:38 -0800 | [diff] [blame] | 266 | const struct inode_operations configfs_symlink_inode_operations = { |
Al Viro | e9c03af | 2019-09-11 09:00:01 +0200 | [diff] [blame] | 267 | .get_link = simple_get_link, |
Joel Becker | 3d0f89b | 2006-01-25 13:31:07 -0800 | [diff] [blame] | 268 | .setattr = configfs_setattr, |
Joel Becker | 7063fbf | 2005-12-15 14:29:43 -0800 | [diff] [blame] | 269 | }; |
| 270 | |