11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * symlink.c - operations for sysfs symlinks. 31da177e4SLinus Torvalds */ 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds #include <linux/fs.h> 6ceeee1fbSGreg Kroah-Hartman #include <linux/mount.h> 71da177e4SLinus Torvalds #include <linux/module.h> 81da177e4SLinus Torvalds #include <linux/kobject.h> 91da177e4SLinus Torvalds #include <linux/namei.h> 1094bebf4dSOliver Neukum #include <asm/semaphore.h> 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include "sysfs.h" 131da177e4SLinus Torvalds 142b29ac25STejun Heo static int object_depth(struct sysfs_dirent *sd) 151da177e4SLinus Torvalds { 161da177e4SLinus Torvalds int depth = 0; 172b29ac25STejun Heo 182b29ac25STejun Heo for (; sd->s_parent; sd = sd->s_parent) 192b29ac25STejun Heo depth++; 202b29ac25STejun Heo 211da177e4SLinus Torvalds return depth; 221da177e4SLinus Torvalds } 231da177e4SLinus Torvalds 242b29ac25STejun Heo static int object_path_length(struct sysfs_dirent * sd) 251da177e4SLinus Torvalds { 261da177e4SLinus Torvalds int length = 1; 272b29ac25STejun Heo 282b29ac25STejun Heo for (; sd->s_parent; sd = sd->s_parent) 292b29ac25STejun Heo length += strlen(sd->s_name) + 1; 302b29ac25STejun Heo 311da177e4SLinus Torvalds return length; 321da177e4SLinus Torvalds } 331da177e4SLinus Torvalds 342b29ac25STejun Heo static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length) 351da177e4SLinus Torvalds { 361da177e4SLinus Torvalds --length; 372b29ac25STejun Heo for (; sd->s_parent; sd = sd->s_parent) { 382b29ac25STejun Heo int cur = strlen(sd->s_name); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds /* back up enough to print this bus id with '/' */ 411da177e4SLinus Torvalds length -= cur; 422b29ac25STejun Heo strncpy(buffer + length, sd->s_name, cur); 431da177e4SLinus Torvalds *(buffer + --length) = '/'; 441da177e4SLinus Torvalds } 451da177e4SLinus Torvalds } 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds /** 481da177e4SLinus Torvalds * sysfs_create_link - create symlink between two objects. 491da177e4SLinus Torvalds * @kobj: object whose directory we're creating the link in. 501da177e4SLinus Torvalds * @target: object we're pointing to. 511da177e4SLinus Torvalds * @name: name of the symlink. 521da177e4SLinus Torvalds */ 53e3a15db2SDmitry Torokhov int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) 541da177e4SLinus Torvalds { 552b29ac25STejun Heo struct sysfs_dirent *parent_sd = NULL; 562b29ac25STejun Heo struct sysfs_dirent *target_sd = NULL; 573007e997STejun Heo struct sysfs_dirent *sd = NULL; 58fb6896daSTejun Heo struct sysfs_addrm_cxt acxt; 593007e997STejun Heo int error; 601da177e4SLinus Torvalds 61ceeee1fbSGreg Kroah-Hartman BUG_ON(!name); 62ceeee1fbSGreg Kroah-Hartman 63ceeee1fbSGreg Kroah-Hartman if (!kobj) { 64ceeee1fbSGreg Kroah-Hartman if (sysfs_mount && sysfs_mount->mnt_sb) 65608e266aSTejun Heo parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata; 66ceeee1fbSGreg Kroah-Hartman } else 67608e266aSTejun Heo parent_sd = kobj->sd; 68ceeee1fbSGreg Kroah-Hartman 693007e997STejun Heo error = -EFAULT; 70608e266aSTejun Heo if (!parent_sd) 713007e997STejun Heo goto out_put; 722b29ac25STejun Heo 73608e266aSTejun Heo /* target->sd can go away beneath us but is protected with 745f995323STejun Heo * sysfs_assoc_lock. Fetch target_sd from it. 752b29ac25STejun Heo */ 765f995323STejun Heo spin_lock(&sysfs_assoc_lock); 77608e266aSTejun Heo if (target->sd) 78608e266aSTejun Heo target_sd = sysfs_get(target->sd); 795f995323STejun Heo spin_unlock(&sysfs_assoc_lock); 802b29ac25STejun Heo 813007e997STejun Heo error = -ENOENT; 822b29ac25STejun Heo if (!target_sd) 833007e997STejun Heo goto out_put; 841da177e4SLinus Torvalds 853007e997STejun Heo error = -ENOMEM; 863007e997STejun Heo sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); 873007e997STejun Heo if (!sd) 883007e997STejun Heo goto out_put; 893007e997STejun Heo sd->s_elem.symlink.target_sd = target_sd; 902b29ac25STejun Heo 91fb6896daSTejun Heo sysfs_addrm_start(&acxt, parent_sd); 923007e997STejun Heo 93fb6896daSTejun Heo if (!sysfs_find_dirent(parent_sd, name)) { 94fb6896daSTejun Heo sysfs_add_one(&acxt, sd); 95fb6896daSTejun Heo sysfs_link_sibling(sd); 96fb6896daSTejun Heo } 97fb6896daSTejun Heo 98fb6896daSTejun Heo if (sysfs_addrm_finish(&acxt)) 993007e997STejun Heo return 0; 1003007e997STejun Heo 101fb6896daSTejun Heo error = -EEXIST; 102fb6896daSTejun Heo /* fall through */ 1033007e997STejun Heo out_put: 1042b29ac25STejun Heo sysfs_put(target_sd); 1053007e997STejun Heo sysfs_put(sd); 1061da177e4SLinus Torvalds return error; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds /** 1111da177e4SLinus Torvalds * sysfs_remove_link - remove symlink in object's directory. 1121da177e4SLinus Torvalds * @kobj: object we're acting for. 1131da177e4SLinus Torvalds * @name: name of the symlink to remove. 1141da177e4SLinus Torvalds */ 1151da177e4SLinus Torvalds 116e3a15db2SDmitry Torokhov void sysfs_remove_link(struct kobject * kobj, const char * name) 1171da177e4SLinus Torvalds { 118608e266aSTejun Heo sysfs_hash_and_remove(kobj->sd, name); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 1212b29ac25STejun Heo static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, 1222b29ac25STejun Heo struct sysfs_dirent * target_sd, char *path) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds char * s; 1251da177e4SLinus Torvalds int depth, size; 1261da177e4SLinus Torvalds 1272b29ac25STejun Heo depth = object_depth(parent_sd); 1282b29ac25STejun Heo size = object_path_length(target_sd) + depth * 3 - 1; 1291da177e4SLinus Torvalds if (size > PATH_MAX) 1301da177e4SLinus Torvalds return -ENAMETOOLONG; 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size); 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds for (s = path; depth--; s += 3) 1351da177e4SLinus Torvalds strcpy(s,"../"); 1361da177e4SLinus Torvalds 1372b29ac25STejun Heo fill_object_path(target_sd, path, size); 1381da177e4SLinus Torvalds pr_debug("%s: path = '%s'\n", __FUNCTION__, path); 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds return 0; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds static int sysfs_getlink(struct dentry *dentry, char * path) 1441da177e4SLinus Torvalds { 1452b29ac25STejun Heo struct sysfs_dirent *sd = dentry->d_fsdata; 1462b29ac25STejun Heo struct sysfs_dirent *parent_sd = sd->s_parent; 1472b29ac25STejun Heo struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; 1482b29ac25STejun Heo int error; 1491da177e4SLinus Torvalds 1503007e997STejun Heo mutex_lock(&sysfs_mutex); 1512b29ac25STejun Heo error = sysfs_get_target_path(parent_sd, target_sd, path); 1523007e997STejun Heo mutex_unlock(&sysfs_mutex); 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds return error; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 157cc314eefSLinus Torvalds static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 1581da177e4SLinus Torvalds { 1591da177e4SLinus Torvalds int error = -ENOMEM; 1601da177e4SLinus Torvalds unsigned long page = get_zeroed_page(GFP_KERNEL); 1611da177e4SLinus Torvalds if (page) 1621da177e4SLinus Torvalds error = sysfs_getlink(dentry, (char *) page); 1631da177e4SLinus Torvalds nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); 164cc314eefSLinus Torvalds return NULL; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 167cc314eefSLinus Torvalds static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) 1681da177e4SLinus Torvalds { 1691da177e4SLinus Torvalds char *page = nd_get_link(nd); 1701da177e4SLinus Torvalds if (!IS_ERR(page)) 1711da177e4SLinus Torvalds free_page((unsigned long)page); 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds 174c5ef1c42SArjan van de Ven const struct inode_operations sysfs_symlink_inode_operations = { 1751da177e4SLinus Torvalds .readlink = generic_readlink, 1761da177e4SLinus Torvalds .follow_link = sysfs_follow_link, 1771da177e4SLinus Torvalds .put_link = sysfs_put_link, 1781da177e4SLinus Torvalds }; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(sysfs_create_link); 1821da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(sysfs_remove_link); 183