1 /* -*- mode: c; c-basic-offset: 8; -*- 2 * vim: noexpandtab sw=8 ts=8 sts=0: 3 * 4 * symlink.c - operations for configfs symlinks. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public 17 * License along with this program; if not, write to the 18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 021110-1307, USA. 20 * 21 * Based on sysfs: 22 * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel 23 * 24 * configfs Copyright (C) 2005 Oracle. All rights reserved. 25 */ 26 27 #include <linux/fs.h> 28 #include <linux/module.h> 29 #include <linux/namei.h> 30 #include <linux/slab.h> 31 32 #include <linux/configfs.h> 33 #include "configfs_internal.h" 34 35 /* Protects attachments of new symlinks */ 36 DEFINE_MUTEX(configfs_symlink_mutex); 37 38 static int item_depth(struct config_item * item) 39 { 40 struct config_item * p = item; 41 int depth = 0; 42 do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p)); 43 return depth; 44 } 45 46 static int item_path_length(struct config_item * item) 47 { 48 struct config_item * p = item; 49 int length = 1; 50 do { 51 length += strlen(config_item_name(p)) + 1; 52 p = p->ci_parent; 53 } while (p && !configfs_is_root(p)); 54 return length; 55 } 56 57 static void fill_item_path(struct config_item * item, char * buffer, int length) 58 { 59 struct config_item * p; 60 61 --length; 62 for (p = item; p && !configfs_is_root(p); p = p->ci_parent) { 63 int cur = strlen(config_item_name(p)); 64 65 /* back up enough to print this bus id with '/' */ 66 length -= cur; 67 strncpy(buffer + length,config_item_name(p),cur); 68 *(buffer + --length) = '/'; 69 } 70 } 71 72 static int create_link(struct config_item *parent_item, 73 struct config_item *item, 74 struct dentry *dentry) 75 { 76 struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; 77 struct configfs_symlink *sl; 78 int ret; 79 80 ret = -ENOENT; 81 if (!configfs_dirent_is_ready(target_sd)) 82 goto out; 83 ret = -ENOMEM; 84 sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); 85 if (sl) { 86 spin_lock(&configfs_dirent_lock); 87 if (target_sd->s_type & CONFIGFS_USET_DROPPING) { 88 spin_unlock(&configfs_dirent_lock); 89 kfree(sl); 90 return -ENOENT; 91 } 92 sl->sl_target = config_item_get(item); 93 list_add(&sl->sl_list, &target_sd->s_links); 94 spin_unlock(&configfs_dirent_lock); 95 ret = configfs_create_link(sl, parent_item->ci_dentry, 96 dentry); 97 if (ret) { 98 spin_lock(&configfs_dirent_lock); 99 list_del_init(&sl->sl_list); 100 spin_unlock(&configfs_dirent_lock); 101 config_item_put(item); 102 kfree(sl); 103 } 104 } 105 106 out: 107 return ret; 108 } 109 110 111 static int get_target(const char *symname, struct path *path, 112 struct config_item **target, struct super_block *sb) 113 { 114 int ret; 115 116 ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path); 117 if (!ret) { 118 if (path->dentry->d_sb == sb) { 119 *target = configfs_get_config_item(path->dentry); 120 if (!*target) { 121 ret = -ENOENT; 122 path_put(path); 123 } 124 } else { 125 ret = -EPERM; 126 path_put(path); 127 } 128 } 129 130 return ret; 131 } 132 133 134 int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) 135 { 136 int ret; 137 struct path path; 138 struct configfs_dirent *sd; 139 struct config_item *parent_item; 140 struct config_item *target_item = NULL; 141 const struct config_item_type *type; 142 143 sd = dentry->d_parent->d_fsdata; 144 /* 145 * Fake invisibility if dir belongs to a group/default groups hierarchy 146 * being attached 147 */ 148 ret = -ENOENT; 149 if (!configfs_dirent_is_ready(sd)) 150 goto out; 151 152 parent_item = configfs_get_config_item(dentry->d_parent); 153 type = parent_item->ci_type; 154 155 ret = -EPERM; 156 if (!type || !type->ct_item_ops || 157 !type->ct_item_ops->allow_link) 158 goto out_put; 159 160 ret = get_target(symname, &path, &target_item, dentry->d_sb); 161 if (ret) 162 goto out_put; 163 164 ret = type->ct_item_ops->allow_link(parent_item, target_item); 165 if (!ret) { 166 mutex_lock(&configfs_symlink_mutex); 167 ret = create_link(parent_item, target_item, dentry); 168 mutex_unlock(&configfs_symlink_mutex); 169 if (ret && type->ct_item_ops->drop_link) 170 type->ct_item_ops->drop_link(parent_item, 171 target_item); 172 } 173 174 config_item_put(target_item); 175 path_put(&path); 176 177 out_put: 178 config_item_put(parent_item); 179 180 out: 181 return ret; 182 } 183 184 int configfs_unlink(struct inode *dir, struct dentry *dentry) 185 { 186 struct configfs_dirent *sd = dentry->d_fsdata; 187 struct configfs_symlink *sl; 188 struct config_item *parent_item; 189 const struct config_item_type *type; 190 int ret; 191 192 ret = -EPERM; /* What lack-of-symlink returns */ 193 if (!(sd->s_type & CONFIGFS_ITEM_LINK)) 194 goto out; 195 196 sl = sd->s_element; 197 198 parent_item = configfs_get_config_item(dentry->d_parent); 199 type = parent_item->ci_type; 200 201 spin_lock(&configfs_dirent_lock); 202 list_del_init(&sd->s_sibling); 203 spin_unlock(&configfs_dirent_lock); 204 configfs_drop_dentry(sd, dentry->d_parent); 205 dput(dentry); 206 configfs_put(sd); 207 208 /* 209 * drop_link() must be called before 210 * list_del_init(&sl->sl_list), so that the order of 211 * drop_link(this, target) and drop_item(target) is preserved. 212 */ 213 if (type && type->ct_item_ops && 214 type->ct_item_ops->drop_link) 215 type->ct_item_ops->drop_link(parent_item, 216 sl->sl_target); 217 218 spin_lock(&configfs_dirent_lock); 219 list_del_init(&sl->sl_list); 220 spin_unlock(&configfs_dirent_lock); 221 222 /* Put reference from create_link() */ 223 config_item_put(sl->sl_target); 224 kfree(sl); 225 226 config_item_put(parent_item); 227 228 ret = 0; 229 230 out: 231 return ret; 232 } 233 234 static int configfs_get_target_path(struct config_item * item, struct config_item * target, 235 char *path) 236 { 237 char * s; 238 int depth, size; 239 240 depth = item_depth(item); 241 size = item_path_length(target) + depth * 3 - 1; 242 if (size > PATH_MAX) 243 return -ENAMETOOLONG; 244 245 pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size); 246 247 for (s = path; depth--; s += 3) 248 strcpy(s,"../"); 249 250 fill_item_path(target, path, size); 251 pr_debug("%s: path = '%s'\n", __func__, path); 252 253 return 0; 254 } 255 256 static int configfs_getlink(struct dentry *dentry, char * path) 257 { 258 struct config_item *item, *target_item; 259 int error = 0; 260 261 item = configfs_get_config_item(dentry->d_parent); 262 if (!item) 263 return -EINVAL; 264 265 target_item = configfs_get_config_item(dentry); 266 if (!target_item) { 267 config_item_put(item); 268 return -EINVAL; 269 } 270 271 down_read(&configfs_rename_sem); 272 error = configfs_get_target_path(item, target_item, path); 273 up_read(&configfs_rename_sem); 274 275 config_item_put(item); 276 config_item_put(target_item); 277 return error; 278 279 } 280 281 static const char *configfs_get_link(struct dentry *dentry, 282 struct inode *inode, 283 struct delayed_call *done) 284 { 285 char *body; 286 int error; 287 288 if (!dentry) 289 return ERR_PTR(-ECHILD); 290 291 body = kzalloc(PAGE_SIZE, GFP_KERNEL); 292 if (!body) 293 return ERR_PTR(-ENOMEM); 294 295 error = configfs_getlink(dentry, body); 296 if (!error) { 297 set_delayed_call(done, kfree_link, body); 298 return body; 299 } 300 301 kfree(body); 302 return ERR_PTR(error); 303 } 304 305 const struct inode_operations configfs_symlink_inode_operations = { 306 .get_link = configfs_get_link, 307 .setattr = configfs_setattr, 308 }; 309 310