1 /* 2 * fs/sysfs/symlink.c - sysfs symlink implementation 3 * 4 * Copyright (c) 2001-3 Patrick Mochel 5 * Copyright (c) 2007 SUSE Linux Products GmbH 6 * Copyright (c) 2007 Tejun Heo <teheo@suse.de> 7 * 8 * This file is released under the GPLv2. 9 * 10 * Please see Documentation/filesystems/sysfs.txt for more information. 11 */ 12 13 #include <linux/fs.h> 14 #include <linux/mount.h> 15 #include <linux/module.h> 16 #include <linux/kobject.h> 17 #include <linux/namei.h> 18 #include <linux/mutex.h> 19 #include <linux/security.h> 20 21 #include "sysfs.h" 22 23 static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, 24 const char *name, int warn) 25 { 26 struct sysfs_dirent *parent_sd = NULL; 27 struct sysfs_dirent *target_sd = NULL; 28 struct sysfs_dirent *sd = NULL; 29 struct sysfs_addrm_cxt acxt; 30 int error; 31 32 BUG_ON(!name); 33 34 if (!kobj) 35 parent_sd = &sysfs_root; 36 else 37 parent_sd = kobj->sd; 38 39 error = -EFAULT; 40 if (!parent_sd) 41 goto out_put; 42 43 /* target->sd can go away beneath us but is protected with 44 * sysfs_assoc_lock. Fetch target_sd from it. 45 */ 46 spin_lock(&sysfs_assoc_lock); 47 if (target->sd) 48 target_sd = sysfs_get(target->sd); 49 spin_unlock(&sysfs_assoc_lock); 50 51 error = -ENOENT; 52 if (!target_sd) 53 goto out_put; 54 55 error = -ENOMEM; 56 sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); 57 if (!sd) 58 goto out_put; 59 60 sd->s_symlink.target_sd = target_sd; 61 target_sd = NULL; /* reference is now owned by the symlink */ 62 63 sysfs_addrm_start(&acxt, parent_sd); 64 if (warn) 65 error = sysfs_add_one(&acxt, sd); 66 else 67 error = __sysfs_add_one(&acxt, sd); 68 sysfs_addrm_finish(&acxt); 69 70 if (error) 71 goto out_put; 72 73 return 0; 74 75 out_put: 76 sysfs_put(target_sd); 77 sysfs_put(sd); 78 return error; 79 } 80 81 /** 82 * sysfs_create_link - create symlink between two objects. 83 * @kobj: object whose directory we're creating the link in. 84 * @target: object we're pointing to. 85 * @name: name of the symlink. 86 */ 87 int sysfs_create_link(struct kobject *kobj, struct kobject *target, 88 const char *name) 89 { 90 return sysfs_do_create_link(kobj, target, name, 1); 91 } 92 93 /** 94 * sysfs_create_link_nowarn - create symlink between two objects. 95 * @kobj: object whose directory we're creating the link in. 96 * @target: object we're pointing to. 97 * @name: name of the symlink. 98 * 99 * This function does the same as sysf_create_link(), but it 100 * doesn't warn if the link already exists. 101 */ 102 int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, 103 const char *name) 104 { 105 return sysfs_do_create_link(kobj, target, name, 0); 106 } 107 108 /** 109 * sysfs_remove_link - remove symlink in object's directory. 110 * @kobj: object we're acting for. 111 * @name: name of the symlink to remove. 112 */ 113 114 void sysfs_remove_link(struct kobject * kobj, const char * name) 115 { 116 struct sysfs_dirent *parent_sd = NULL; 117 118 if (!kobj) 119 parent_sd = &sysfs_root; 120 else 121 parent_sd = kobj->sd; 122 123 sysfs_hash_and_remove(parent_sd, name); 124 } 125 126 static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, 127 struct sysfs_dirent *target_sd, char *path) 128 { 129 struct sysfs_dirent *base, *sd; 130 char *s = path; 131 int len = 0; 132 133 /* go up to the root, stop at the base */ 134 base = parent_sd; 135 while (base->s_parent) { 136 sd = target_sd->s_parent; 137 while (sd->s_parent && base != sd) 138 sd = sd->s_parent; 139 140 if (base == sd) 141 break; 142 143 strcpy(s, "../"); 144 s += 3; 145 base = base->s_parent; 146 } 147 148 /* determine end of target string for reverse fillup */ 149 sd = target_sd; 150 while (sd->s_parent && sd != base) { 151 len += strlen(sd->s_name) + 1; 152 sd = sd->s_parent; 153 } 154 155 /* check limits */ 156 if (len < 2) 157 return -EINVAL; 158 len--; 159 if ((s - path) + len > PATH_MAX) 160 return -ENAMETOOLONG; 161 162 /* reverse fillup of target string from target to base */ 163 sd = target_sd; 164 while (sd->s_parent && sd != base) { 165 int slen = strlen(sd->s_name); 166 167 len -= slen; 168 strncpy(s + len, sd->s_name, slen); 169 if (len) 170 s[--len] = '/'; 171 172 sd = sd->s_parent; 173 } 174 175 return 0; 176 } 177 178 static int sysfs_getlink(struct dentry *dentry, char * path) 179 { 180 struct sysfs_dirent *sd = dentry->d_fsdata; 181 struct sysfs_dirent *parent_sd = sd->s_parent; 182 struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; 183 int error; 184 185 mutex_lock(&sysfs_mutex); 186 error = sysfs_get_target_path(parent_sd, target_sd, path); 187 mutex_unlock(&sysfs_mutex); 188 189 return error; 190 } 191 192 static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 193 { 194 int error = -ENOMEM; 195 unsigned long page = get_zeroed_page(GFP_KERNEL); 196 if (page) { 197 error = sysfs_getlink(dentry, (char *) page); 198 if (error < 0) 199 free_page((unsigned long)page); 200 } 201 nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); 202 return NULL; 203 } 204 205 static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) 206 { 207 char *page = nd_get_link(nd); 208 if (!IS_ERR(page)) 209 free_page((unsigned long)page); 210 } 211 212 const struct inode_operations sysfs_symlink_inode_operations = { 213 .setxattr = sysfs_setxattr, 214 .readlink = generic_readlink, 215 .follow_link = sysfs_follow_link, 216 .put_link = sysfs_put_link, 217 .setattr = sysfs_setattr, 218 .getattr = sysfs_getattr, 219 .permission = sysfs_permission, 220 }; 221 222 223 EXPORT_SYMBOL_GPL(sysfs_create_link); 224 EXPORT_SYMBOL_GPL(sysfs_remove_link); 225