11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * symlink.c - operations for sysfs symlinks. 31da177e4SLinus Torvalds */ 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds #include <linux/fs.h> 61da177e4SLinus Torvalds #include <linux/module.h> 71da177e4SLinus Torvalds #include <linux/kobject.h> 81da177e4SLinus Torvalds #include <linux/namei.h> 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #include "sysfs.h" 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds static int object_depth(struct kobject * kobj) 131da177e4SLinus Torvalds { 141da177e4SLinus Torvalds struct kobject * p = kobj; 151da177e4SLinus Torvalds int depth = 0; 161da177e4SLinus Torvalds do { depth++; } while ((p = p->parent)); 171da177e4SLinus Torvalds return depth; 181da177e4SLinus Torvalds } 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds static int object_path_length(struct kobject * kobj) 211da177e4SLinus Torvalds { 221da177e4SLinus Torvalds struct kobject * p = kobj; 231da177e4SLinus Torvalds int length = 1; 241da177e4SLinus Torvalds do { 251da177e4SLinus Torvalds length += strlen(kobject_name(p)) + 1; 261da177e4SLinus Torvalds p = p->parent; 271da177e4SLinus Torvalds } while (p); 281da177e4SLinus Torvalds return length; 291da177e4SLinus Torvalds } 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds static void fill_object_path(struct kobject * kobj, char * buffer, int length) 321da177e4SLinus Torvalds { 331da177e4SLinus Torvalds struct kobject * p; 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds --length; 361da177e4SLinus Torvalds for (p = kobj; p; p = p->parent) { 371da177e4SLinus Torvalds int cur = strlen(kobject_name(p)); 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds /* back up enough to print this bus id with '/' */ 401da177e4SLinus Torvalds length -= cur; 411da177e4SLinus Torvalds strncpy(buffer + length,kobject_name(p),cur); 421da177e4SLinus Torvalds *(buffer + --length) = '/'; 431da177e4SLinus Torvalds } 441da177e4SLinus Torvalds } 451da177e4SLinus Torvalds 46e3a15db2SDmitry Torokhov static int sysfs_add_link(struct dentry * parent, const char * name, struct kobject * target) 471da177e4SLinus Torvalds { 481da177e4SLinus Torvalds struct sysfs_dirent * parent_sd = parent->d_fsdata; 491da177e4SLinus Torvalds struct sysfs_symlink * sl; 501da177e4SLinus Torvalds int error = 0; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds error = -ENOMEM; 531da177e4SLinus Torvalds sl = kmalloc(sizeof(*sl), GFP_KERNEL); 541da177e4SLinus Torvalds if (!sl) 551da177e4SLinus Torvalds goto exit1; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); 581da177e4SLinus Torvalds if (!sl->link_name) 591da177e4SLinus Torvalds goto exit2; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds strcpy(sl->link_name, name); 621da177e4SLinus Torvalds sl->target_kobj = kobject_get(target); 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, 651da177e4SLinus Torvalds SYSFS_KOBJ_LINK); 661da177e4SLinus Torvalds if (!error) 671da177e4SLinus Torvalds return 0; 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds kfree(sl->link_name); 701da177e4SLinus Torvalds exit2: 711da177e4SLinus Torvalds kfree(sl); 721da177e4SLinus Torvalds exit1: 731da177e4SLinus Torvalds return error; 741da177e4SLinus Torvalds } 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds /** 771da177e4SLinus Torvalds * sysfs_create_link - create symlink between two objects. 781da177e4SLinus Torvalds * @kobj: object whose directory we're creating the link in. 791da177e4SLinus Torvalds * @target: object we're pointing to. 801da177e4SLinus Torvalds * @name: name of the symlink. 811da177e4SLinus Torvalds */ 82e3a15db2SDmitry Torokhov int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) 831da177e4SLinus Torvalds { 841da177e4SLinus Torvalds struct dentry * dentry = kobj->dentry; 851da177e4SLinus Torvalds int error = 0; 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds BUG_ON(!kobj || !kobj->dentry || !name); 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds down(&dentry->d_inode->i_sem); 901da177e4SLinus Torvalds error = sysfs_add_link(dentry, name, target); 911da177e4SLinus Torvalds up(&dentry->d_inode->i_sem); 921da177e4SLinus Torvalds return error; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds /** 971da177e4SLinus Torvalds * sysfs_remove_link - remove symlink in object's directory. 981da177e4SLinus Torvalds * @kobj: object we're acting for. 991da177e4SLinus Torvalds * @name: name of the symlink to remove. 1001da177e4SLinus Torvalds */ 1011da177e4SLinus Torvalds 102e3a15db2SDmitry Torokhov void sysfs_remove_link(struct kobject * kobj, const char * name) 1031da177e4SLinus Torvalds { 1041da177e4SLinus Torvalds sysfs_hash_and_remove(kobj->dentry,name); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, 1081da177e4SLinus Torvalds char *path) 1091da177e4SLinus Torvalds { 1101da177e4SLinus Torvalds char * s; 1111da177e4SLinus Torvalds int depth, size; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds depth = object_depth(kobj); 1141da177e4SLinus Torvalds size = object_path_length(target) + depth * 3 - 1; 1151da177e4SLinus Torvalds if (size > PATH_MAX) 1161da177e4SLinus Torvalds return -ENAMETOOLONG; 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size); 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds for (s = path; depth--; s += 3) 1211da177e4SLinus Torvalds strcpy(s,"../"); 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds fill_object_path(target, path, size); 1241da177e4SLinus Torvalds pr_debug("%s: path = '%s'\n", __FUNCTION__, path); 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds return 0; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds static int sysfs_getlink(struct dentry *dentry, char * path) 1301da177e4SLinus Torvalds { 1311da177e4SLinus Torvalds struct kobject *kobj, *target_kobj; 1321da177e4SLinus Torvalds int error = 0; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds kobj = sysfs_get_kobject(dentry->d_parent); 1351da177e4SLinus Torvalds if (!kobj) 1361da177e4SLinus Torvalds return -EINVAL; 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds target_kobj = sysfs_get_kobject(dentry); 1391da177e4SLinus Torvalds if (!target_kobj) { 1401da177e4SLinus Torvalds kobject_put(kobj); 1411da177e4SLinus Torvalds return -EINVAL; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds down_read(&sysfs_rename_sem); 1451da177e4SLinus Torvalds error = sysfs_get_target_path(kobj, target_kobj, path); 1461da177e4SLinus Torvalds up_read(&sysfs_rename_sem); 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds kobject_put(kobj); 1491da177e4SLinus Torvalds kobject_put(target_kobj); 1501da177e4SLinus Torvalds return error; 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 154cc314eefSLinus Torvalds static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 1551da177e4SLinus Torvalds { 1561da177e4SLinus Torvalds int error = -ENOMEM; 1571da177e4SLinus Torvalds unsigned long page = get_zeroed_page(GFP_KERNEL); 1581da177e4SLinus Torvalds if (page) 1591da177e4SLinus Torvalds error = sysfs_getlink(dentry, (char *) page); 1601da177e4SLinus Torvalds nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); 161cc314eefSLinus Torvalds return NULL; 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 164cc314eefSLinus Torvalds static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds char *page = nd_get_link(nd); 1671da177e4SLinus Torvalds if (!IS_ERR(page)) 1681da177e4SLinus Torvalds free_page((unsigned long)page); 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds struct inode_operations sysfs_symlink_inode_operations = { 1721da177e4SLinus Torvalds .readlink = generic_readlink, 1731da177e4SLinus Torvalds .follow_link = sysfs_follow_link, 1741da177e4SLinus Torvalds .put_link = sysfs_put_link, 1751da177e4SLinus Torvalds }; 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(sysfs_create_link); 1791da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(sysfs_remove_link); 1801da177e4SLinus Torvalds 181