1 /* 2 * symlink.c - operations for sysfs symlinks. 3 */ 4 5 #include <linux/fs.h> 6 #include <linux/module.h> 7 #include <linux/kobject.h> 8 #include <linux/namei.h> 9 10 #include "sysfs.h" 11 12 static int object_depth(struct kobject * kobj) 13 { 14 struct kobject * p = kobj; 15 int depth = 0; 16 do { depth++; } while ((p = p->parent)); 17 return depth; 18 } 19 20 static int object_path_length(struct kobject * kobj) 21 { 22 struct kobject * p = kobj; 23 int length = 1; 24 do { 25 length += strlen(kobject_name(p)) + 1; 26 p = p->parent; 27 } while (p); 28 return length; 29 } 30 31 static void fill_object_path(struct kobject * kobj, char * buffer, int length) 32 { 33 struct kobject * p; 34 35 --length; 36 for (p = kobj; p; p = p->parent) { 37 int cur = strlen(kobject_name(p)); 38 39 /* back up enough to print this bus id with '/' */ 40 length -= cur; 41 strncpy(buffer + length,kobject_name(p),cur); 42 *(buffer + --length) = '/'; 43 } 44 } 45 46 static int sysfs_add_link(struct dentry * parent, char * name, struct kobject * target) 47 { 48 struct sysfs_dirent * parent_sd = parent->d_fsdata; 49 struct sysfs_symlink * sl; 50 int error = 0; 51 52 error = -ENOMEM; 53 sl = kmalloc(sizeof(*sl), GFP_KERNEL); 54 if (!sl) 55 goto exit1; 56 57 sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); 58 if (!sl->link_name) 59 goto exit2; 60 61 strcpy(sl->link_name, name); 62 sl->target_kobj = kobject_get(target); 63 64 error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, 65 SYSFS_KOBJ_LINK); 66 if (!error) 67 return 0; 68 69 kfree(sl->link_name); 70 exit2: 71 kfree(sl); 72 exit1: 73 return error; 74 } 75 76 /** 77 * sysfs_create_link - create symlink between two objects. 78 * @kobj: object whose directory we're creating the link in. 79 * @target: object we're pointing to. 80 * @name: name of the symlink. 81 */ 82 int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name) 83 { 84 struct dentry * dentry = kobj->dentry; 85 int error = 0; 86 87 BUG_ON(!kobj || !kobj->dentry || !name); 88 89 down(&dentry->d_inode->i_sem); 90 error = sysfs_add_link(dentry, name, target); 91 up(&dentry->d_inode->i_sem); 92 return error; 93 } 94 95 96 /** 97 * sysfs_remove_link - remove symlink in object's directory. 98 * @kobj: object we're acting for. 99 * @name: name of the symlink to remove. 100 */ 101 102 void sysfs_remove_link(struct kobject * kobj, char * name) 103 { 104 sysfs_hash_and_remove(kobj->dentry,name); 105 } 106 107 static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, 108 char *path) 109 { 110 char * s; 111 int depth, size; 112 113 depth = object_depth(kobj); 114 size = object_path_length(target) + depth * 3 - 1; 115 if (size > PATH_MAX) 116 return -ENAMETOOLONG; 117 118 pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size); 119 120 for (s = path; depth--; s += 3) 121 strcpy(s,"../"); 122 123 fill_object_path(target, path, size); 124 pr_debug("%s: path = '%s'\n", __FUNCTION__, path); 125 126 return 0; 127 } 128 129 static int sysfs_getlink(struct dentry *dentry, char * path) 130 { 131 struct kobject *kobj, *target_kobj; 132 int error = 0; 133 134 kobj = sysfs_get_kobject(dentry->d_parent); 135 if (!kobj) 136 return -EINVAL; 137 138 target_kobj = sysfs_get_kobject(dentry); 139 if (!target_kobj) { 140 kobject_put(kobj); 141 return -EINVAL; 142 } 143 144 down_read(&sysfs_rename_sem); 145 error = sysfs_get_target_path(kobj, target_kobj, path); 146 up_read(&sysfs_rename_sem); 147 148 kobject_put(kobj); 149 kobject_put(target_kobj); 150 return error; 151 152 } 153 154 static int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 155 { 156 int error = -ENOMEM; 157 unsigned long page = get_zeroed_page(GFP_KERNEL); 158 if (page) 159 error = sysfs_getlink(dentry, (char *) page); 160 nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); 161 return 0; 162 } 163 164 static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd) 165 { 166 char *page = nd_get_link(nd); 167 if (!IS_ERR(page)) 168 free_page((unsigned long)page); 169 } 170 171 struct inode_operations sysfs_symlink_inode_operations = { 172 .readlink = generic_readlink, 173 .follow_link = sysfs_follow_link, 174 .put_link = sysfs_put_link, 175 }; 176 177 178 EXPORT_SYMBOL_GPL(sysfs_create_link); 179 EXPORT_SYMBOL_GPL(sysfs_remove_link); 180 181