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