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 31 #include <linux/configfs.h> 32 #include "configfs_internal.h" 33 34 /* Protects attachments of new symlinks */ 35 DEFINE_MUTEX(configfs_symlink_mutex); 36 37 static int item_depth(struct config_item * item) 38 { 39 struct config_item * p = item; 40 int depth = 0; 41 do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p)); 42 return depth; 43 } 44 45 static int item_path_length(struct config_item * item) 46 { 47 struct config_item * p = item; 48 int length = 1; 49 do { 50 length += strlen(config_item_name(p)) + 1; 51 p = p->ci_parent; 52 } while (p && !configfs_is_root(p)); 53 return length; 54 } 55 56 static void fill_item_path(struct config_item * item, char * buffer, int length) 57 { 58 struct config_item * p; 59 60 --length; 61 for (p = item; p && !configfs_is_root(p); p = p->ci_parent) { 62 int cur = strlen(config_item_name(p)); 63 64 /* back up enough to print this bus id with '/' */ 65 length -= cur; 66 strncpy(buffer + length,config_item_name(p),cur); 67 *(buffer + --length) = '/'; 68 } 69 } 70 71 static int create_link(struct config_item *parent_item, 72 struct config_item *item, 73 struct dentry *dentry) 74 { 75 struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; 76 struct configfs_symlink *sl; 77 int ret; 78 79 ret = -ENOENT; 80 if (!configfs_dirent_is_ready(target_sd)) 81 goto out; 82 ret = -ENOMEM; 83 sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); 84 if (sl) { 85 sl->sl_target = config_item_get(item); 86 spin_lock(&configfs_dirent_lock); 87 if (target_sd->s_type & CONFIGFS_USET_DROPPING) { 88 spin_unlock(&configfs_dirent_lock); 89 config_item_put(item); 90 kfree(sl); 91 return -ENOENT; 92 } 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) 113 { 114 int ret; 115 116 ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path); 117 if (!ret) { 118 if (path->dentry->d_sb == configfs_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 } 127 128 return ret; 129 } 130 131 132 int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) 133 { 134 int ret; 135 struct path path; 136 struct configfs_dirent *sd; 137 struct config_item *parent_item; 138 struct config_item *target_item = NULL; 139 struct config_item_type *type; 140 141 ret = -EPERM; /* What lack-of-symlink returns */ 142 if (dentry->d_parent == configfs_sb->s_root) 143 goto out; 144 145 sd = dentry->d_parent->d_fsdata; 146 /* 147 * Fake invisibility if dir belongs to a group/default groups hierarchy 148 * being attached 149 */ 150 ret = -ENOENT; 151 if (!configfs_dirent_is_ready(sd)) 152 goto out; 153 154 parent_item = configfs_get_config_item(dentry->d_parent); 155 type = parent_item->ci_type; 156 157 ret = -EPERM; 158 if (!type || !type->ct_item_ops || 159 !type->ct_item_ops->allow_link) 160 goto out_put; 161 162 ret = get_target(symname, &path, &target_item); 163 if (ret) 164 goto out_put; 165 166 ret = type->ct_item_ops->allow_link(parent_item, target_item); 167 if (!ret) { 168 mutex_lock(&configfs_symlink_mutex); 169 ret = create_link(parent_item, target_item, dentry); 170 mutex_unlock(&configfs_symlink_mutex); 171 if (ret && type->ct_item_ops->drop_link) 172 type->ct_item_ops->drop_link(parent_item, 173 target_item); 174 } 175 176 config_item_put(target_item); 177 path_put(&path); 178 179 out_put: 180 config_item_put(parent_item); 181 182 out: 183 return ret; 184 } 185 186 int configfs_unlink(struct inode *dir, struct dentry *dentry) 187 { 188 struct configfs_dirent *sd = dentry->d_fsdata; 189 struct configfs_symlink *sl; 190 struct config_item *parent_item; 191 struct config_item_type *type; 192 int ret; 193 194 ret = -EPERM; /* What lack-of-symlink returns */ 195 if (!(sd->s_type & CONFIGFS_ITEM_LINK)) 196 goto out; 197 198 BUG_ON(dentry->d_parent == configfs_sb->s_root); 199 200 sl = sd->s_element; 201 202 parent_item = configfs_get_config_item(dentry->d_parent); 203 type = parent_item->ci_type; 204 205 spin_lock(&configfs_dirent_lock); 206 list_del_init(&sd->s_sibling); 207 spin_unlock(&configfs_dirent_lock); 208 configfs_drop_dentry(sd, dentry->d_parent); 209 dput(dentry); 210 configfs_put(sd); 211 212 /* 213 * drop_link() must be called before 214 * list_del_init(&sl->sl_list), so that the order of 215 * drop_link(this, target) and drop_item(target) is preserved. 216 */ 217 if (type && type->ct_item_ops && 218 type->ct_item_ops->drop_link) 219 type->ct_item_ops->drop_link(parent_item, 220 sl->sl_target); 221 222 spin_lock(&configfs_dirent_lock); 223 list_del_init(&sl->sl_list); 224 spin_unlock(&configfs_dirent_lock); 225 226 /* Put reference from create_link() */ 227 config_item_put(sl->sl_target); 228 kfree(sl); 229 230 config_item_put(parent_item); 231 232 ret = 0; 233 234 out: 235 return ret; 236 } 237 238 static int configfs_get_target_path(struct config_item * item, struct config_item * target, 239 char *path) 240 { 241 char * s; 242 int depth, size; 243 244 depth = item_depth(item); 245 size = item_path_length(target) + depth * 3 - 1; 246 if (size > PATH_MAX) 247 return -ENAMETOOLONG; 248 249 pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size); 250 251 for (s = path; depth--; s += 3) 252 strcpy(s,"../"); 253 254 fill_item_path(target, path, size); 255 pr_debug("%s: path = '%s'\n", __func__, path); 256 257 return 0; 258 } 259 260 static int configfs_getlink(struct dentry *dentry, char * path) 261 { 262 struct config_item *item, *target_item; 263 int error = 0; 264 265 item = configfs_get_config_item(dentry->d_parent); 266 if (!item) 267 return -EINVAL; 268 269 target_item = configfs_get_config_item(dentry); 270 if (!target_item) { 271 config_item_put(item); 272 return -EINVAL; 273 } 274 275 down_read(&configfs_rename_sem); 276 error = configfs_get_target_path(item, target_item, path); 277 up_read(&configfs_rename_sem); 278 279 config_item_put(item); 280 config_item_put(target_item); 281 return error; 282 283 } 284 285 static void *configfs_follow_link(struct dentry *dentry, struct nameidata *nd) 286 { 287 int error = -ENOMEM; 288 unsigned long page = get_zeroed_page(GFP_KERNEL); 289 290 if (page) { 291 error = configfs_getlink(dentry, (char *)page); 292 if (!error) { 293 nd_set_link(nd, (char *)page); 294 return (void *)page; 295 } 296 } 297 298 nd_set_link(nd, ERR_PTR(error)); 299 return NULL; 300 } 301 302 static void configfs_put_link(struct dentry *dentry, struct nameidata *nd, 303 void *cookie) 304 { 305 if (cookie) { 306 unsigned long page = (unsigned long)cookie; 307 free_page(page); 308 } 309 } 310 311 const struct inode_operations configfs_symlink_inode_operations = { 312 .follow_link = configfs_follow_link, 313 .readlink = generic_readlink, 314 .put_link = configfs_put_link, 315 .setattr = configfs_setattr, 316 }; 317 318