11da177e4SLinus Torvalds /* 26d66f5cdSTejun Heo * fs/sysfs/symlink.c - sysfs symlink implementation 36d66f5cdSTejun Heo * 46d66f5cdSTejun Heo * Copyright (c) 2001-3 Patrick Mochel 56d66f5cdSTejun Heo * Copyright (c) 2007 SUSE Linux Products GmbH 66d66f5cdSTejun Heo * Copyright (c) 2007 Tejun Heo <teheo@suse.de> 76d66f5cdSTejun Heo * 86d66f5cdSTejun Heo * This file is released under the GPLv2. 96d66f5cdSTejun Heo * 106d66f5cdSTejun Heo * Please see Documentation/filesystems/sysfs.txt for more information. 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 131da177e4SLinus Torvalds #include <linux/fs.h> 145a0e3ad6STejun Heo #include <linux/gfp.h> 15ceeee1fbSGreg Kroah-Hartman #include <linux/mount.h> 161da177e4SLinus Torvalds #include <linux/module.h> 171da177e4SLinus Torvalds #include <linux/kobject.h> 181da177e4SLinus Torvalds #include <linux/namei.h> 19869512abSDave Young #include <linux/mutex.h> 20ddd29ec6SDavid P. Quigley #include <linux/security.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include "sysfs.h" 231da177e4SLinus Torvalds 240bb8f3d6SRafael J. Wysocki static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, 250bb8f3d6SRafael J. Wysocki struct kobject *target, 2636ce6dadSCornelia Huck const char *name, int warn) 271da177e4SLinus Torvalds { 282b29ac25STejun Heo struct sysfs_dirent *target_sd = NULL; 293007e997STejun Heo struct sysfs_dirent *sd = NULL; 30fb6896daSTejun Heo struct sysfs_addrm_cxt acxt; 3196d6523aSEric W. Biederman enum kobj_ns_type ns_type; 323007e997STejun Heo int error; 331da177e4SLinus Torvalds 340bb8f3d6SRafael J. Wysocki BUG_ON(!name || !parent_sd); 352b29ac25STejun Heo 36608e266aSTejun Heo /* target->sd can go away beneath us but is protected with 375f995323STejun Heo * sysfs_assoc_lock. Fetch target_sd from it. 382b29ac25STejun Heo */ 395f995323STejun Heo spin_lock(&sysfs_assoc_lock); 40608e266aSTejun Heo if (target->sd) 41608e266aSTejun Heo target_sd = sysfs_get(target->sd); 425f995323STejun Heo spin_unlock(&sysfs_assoc_lock); 432b29ac25STejun Heo 443007e997STejun Heo error = -ENOENT; 452b29ac25STejun Heo if (!target_sd) 463007e997STejun Heo goto out_put; 471da177e4SLinus Torvalds 483007e997STejun Heo error = -ENOMEM; 493007e997STejun Heo sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); 503007e997STejun Heo if (!sd) 513007e997STejun Heo goto out_put; 52a1da4dfeSTejun Heo 5396d6523aSEric W. Biederman ns_type = sysfs_ns_type(parent_sd); 5496d6523aSEric W. Biederman if (ns_type) 554b30ee58STejun Heo sd->s_ns = target_sd->s_ns; 56b1fc3d61STejun Heo sd->s_symlink.target_sd = target_sd; 57a1da4dfeSTejun Heo target_sd = NULL; /* reference is now owned by the symlink */ 582b29ac25STejun Heo 59fb6896daSTejun Heo sysfs_addrm_start(&acxt, parent_sd); 6096d6523aSEric W. Biederman /* Symlinks must be between directories with the same ns_type */ 61d3300212SEric W. Biederman if (!ns_type || 62d3300212SEric W. Biederman (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) { 6336ce6dadSCornelia Huck if (warn) 6423dc2799STejun Heo error = sysfs_add_one(&acxt, sd); 6536ce6dadSCornelia Huck else 6636ce6dadSCornelia Huck error = __sysfs_add_one(&acxt, sd); 6796d6523aSEric W. Biederman } else { 6896d6523aSEric W. Biederman error = -EINVAL; 6996d6523aSEric W. Biederman WARN(1, KERN_WARNING 7096d6523aSEric W. Biederman "sysfs: symlink across ns_types %s/%s -> %s/%s\n", 7196d6523aSEric W. Biederman parent_sd->s_name, 7296d6523aSEric W. Biederman sd->s_name, 7396d6523aSEric W. Biederman sd->s_symlink.target_sd->s_parent->s_name, 7496d6523aSEric W. Biederman sd->s_symlink.target_sd->s_name); 7596d6523aSEric W. Biederman } 7623dc2799STejun Heo sysfs_addrm_finish(&acxt); 773007e997STejun Heo 7823dc2799STejun Heo if (error) 79967e35dcSTejun Heo goto out_put; 80967e35dcSTejun Heo 813007e997STejun Heo return 0; 823007e997STejun Heo 833007e997STejun Heo out_put: 842b29ac25STejun Heo sysfs_put(target_sd); 853007e997STejun Heo sysfs_put(sd); 861da177e4SLinus Torvalds return error; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds /** 900bb8f3d6SRafael J. Wysocki * sysfs_create_link_sd - create symlink to a given object. 910bb8f3d6SRafael J. Wysocki * @sd: directory we're creating the link in. 920bb8f3d6SRafael J. Wysocki * @target: object we're pointing to. 930bb8f3d6SRafael J. Wysocki * @name: name of the symlink. 940bb8f3d6SRafael J. Wysocki */ 950bb8f3d6SRafael J. Wysocki int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target, 960bb8f3d6SRafael J. Wysocki const char *name) 970bb8f3d6SRafael J. Wysocki { 980bb8f3d6SRafael J. Wysocki return sysfs_do_create_link_sd(sd, target, name, 1); 990bb8f3d6SRafael J. Wysocki } 1000bb8f3d6SRafael J. Wysocki 1010bb8f3d6SRafael J. Wysocki static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, 1020bb8f3d6SRafael J. Wysocki const char *name, int warn) 1030bb8f3d6SRafael J. Wysocki { 1040bb8f3d6SRafael J. Wysocki struct sysfs_dirent *parent_sd = NULL; 1050bb8f3d6SRafael J. Wysocki 1060bb8f3d6SRafael J. Wysocki if (!kobj) 1070bb8f3d6SRafael J. Wysocki parent_sd = &sysfs_root; 1080bb8f3d6SRafael J. Wysocki else 1090bb8f3d6SRafael J. Wysocki parent_sd = kobj->sd; 1100bb8f3d6SRafael J. Wysocki 1110bb8f3d6SRafael J. Wysocki if (!parent_sd) 1120bb8f3d6SRafael J. Wysocki return -EFAULT; 1130bb8f3d6SRafael J. Wysocki 1140bb8f3d6SRafael J. Wysocki return sysfs_do_create_link_sd(parent_sd, target, name, warn); 1150bb8f3d6SRafael J. Wysocki } 1160bb8f3d6SRafael J. Wysocki 1170bb8f3d6SRafael J. Wysocki /** 11836ce6dadSCornelia Huck * sysfs_create_link - create symlink between two objects. 11936ce6dadSCornelia Huck * @kobj: object whose directory we're creating the link in. 12036ce6dadSCornelia Huck * @target: object we're pointing to. 12136ce6dadSCornelia Huck * @name: name of the symlink. 12236ce6dadSCornelia Huck */ 12336ce6dadSCornelia Huck int sysfs_create_link(struct kobject *kobj, struct kobject *target, 12436ce6dadSCornelia Huck const char *name) 12536ce6dadSCornelia Huck { 12636ce6dadSCornelia Huck return sysfs_do_create_link(kobj, target, name, 1); 12736ce6dadSCornelia Huck } 1281b866757SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_create_link); 12936ce6dadSCornelia Huck 13036ce6dadSCornelia Huck /** 13136ce6dadSCornelia Huck * sysfs_create_link_nowarn - create symlink between two objects. 13236ce6dadSCornelia Huck * @kobj: object whose directory we're creating the link in. 13336ce6dadSCornelia Huck * @target: object we're pointing to. 13436ce6dadSCornelia Huck * @name: name of the symlink. 13536ce6dadSCornelia Huck * 1366f1cbd4aSRobert P. J. Day * This function does the same as sysfs_create_link(), but it 13736ce6dadSCornelia Huck * doesn't warn if the link already exists. 13836ce6dadSCornelia Huck */ 13936ce6dadSCornelia Huck int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, 14036ce6dadSCornelia Huck const char *name) 14136ce6dadSCornelia Huck { 14236ce6dadSCornelia Huck return sysfs_do_create_link(kobj, target, name, 0); 14336ce6dadSCornelia Huck } 14436ce6dadSCornelia Huck 14536ce6dadSCornelia Huck /** 146746edb7aSEric W. Biederman * sysfs_delete_link - remove symlink in object's directory. 147746edb7aSEric W. Biederman * @kobj: object we're acting for. 148746edb7aSEric W. Biederman * @targ: object we're pointing to. 149746edb7aSEric W. Biederman * @name: name of the symlink to remove. 150746edb7aSEric W. Biederman * 151746edb7aSEric W. Biederman * Unlike sysfs_remove_link sysfs_delete_link has enough information 152746edb7aSEric W. Biederman * to successfully delete symlinks in tagged directories. 153746edb7aSEric W. Biederman */ 154746edb7aSEric W. Biederman void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, 155746edb7aSEric W. Biederman const char *name) 156746edb7aSEric W. Biederman { 157746edb7aSEric W. Biederman const void *ns = NULL; 158746edb7aSEric W. Biederman spin_lock(&sysfs_assoc_lock); 159521d0453SEric W. Biederman if (targ->sd && sysfs_ns_type(kobj->sd)) 160746edb7aSEric W. Biederman ns = targ->sd->s_ns; 161746edb7aSEric W. Biederman spin_unlock(&sysfs_assoc_lock); 162746edb7aSEric W. Biederman sysfs_hash_and_remove(kobj->sd, ns, name); 163746edb7aSEric W. Biederman } 164746edb7aSEric W. Biederman 165746edb7aSEric W. Biederman /** 1661da177e4SLinus Torvalds * sysfs_remove_link - remove symlink in object's directory. 1671da177e4SLinus Torvalds * @kobj: object we're acting for. 1681da177e4SLinus Torvalds * @name: name of the symlink to remove. 1691da177e4SLinus Torvalds */ 170e3a15db2SDmitry Torokhov void sysfs_remove_link(struct kobject *kobj, const char *name) 1711da177e4SLinus Torvalds { 172a839c5afSMark Fasheh struct sysfs_dirent *parent_sd = NULL; 173a839c5afSMark Fasheh 174a839c5afSMark Fasheh if (!kobj) 175a839c5afSMark Fasheh parent_sd = &sysfs_root; 176a839c5afSMark Fasheh else 177a839c5afSMark Fasheh parent_sd = kobj->sd; 178a839c5afSMark Fasheh 1793ff195b0SEric W. Biederman sysfs_hash_and_remove(parent_sd, NULL, name); 1801da177e4SLinus Torvalds } 1811b866757SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_remove_link); 1821da177e4SLinus Torvalds 1837cb32942SEric W. Biederman /** 1844b30ee58STejun Heo * sysfs_rename_link_ns - rename symlink in object's directory. 1857cb32942SEric W. Biederman * @kobj: object we're acting for. 1867cb32942SEric W. Biederman * @targ: object we're pointing to. 1877cb32942SEric W. Biederman * @old: previous name of the symlink. 1887cb32942SEric W. Biederman * @new: new name of the symlink. 1894b30ee58STejun Heo * @new_ns: new namespace of the symlink. 1907cb32942SEric W. Biederman * 1917cb32942SEric W. Biederman * A helper function for the common rename symlink idiom. 1927cb32942SEric W. Biederman */ 1934b30ee58STejun Heo int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, 1944b30ee58STejun Heo const char *old, const char *new, const void *new_ns) 1957cb32942SEric W. Biederman { 1967cb32942SEric W. Biederman struct sysfs_dirent *parent_sd, *sd = NULL; 1974b30ee58STejun Heo const void *old_ns = NULL; 1987cb32942SEric W. Biederman int result; 1997cb32942SEric W. Biederman 2007cb32942SEric W. Biederman if (!kobj) 2017cb32942SEric W. Biederman parent_sd = &sysfs_root; 2027cb32942SEric W. Biederman else 2037cb32942SEric W. Biederman parent_sd = kobj->sd; 2047cb32942SEric W. Biederman 2053ff195b0SEric W. Biederman if (targ->sd) 2063ff195b0SEric W. Biederman old_ns = targ->sd->s_ns; 2073ff195b0SEric W. Biederman 2087cb32942SEric W. Biederman result = -ENOENT; 2093ff195b0SEric W. Biederman sd = sysfs_get_dirent(parent_sd, old_ns, old); 2107cb32942SEric W. Biederman if (!sd) 2117cb32942SEric W. Biederman goto out; 2127cb32942SEric W. Biederman 2137cb32942SEric W. Biederman result = -EINVAL; 2147cb32942SEric W. Biederman if (sysfs_type(sd) != SYSFS_KOBJ_LINK) 2157cb32942SEric W. Biederman goto out; 2167cb32942SEric W. Biederman if (sd->s_symlink.target_sd->s_dir.kobj != targ) 2177cb32942SEric W. Biederman goto out; 2187cb32942SEric W. Biederman 2193ff195b0SEric W. Biederman result = sysfs_rename(sd, parent_sd, new_ns, new); 2207cb32942SEric W. Biederman 2217cb32942SEric W. Biederman out: 2227cb32942SEric W. Biederman sysfs_put(sd); 2237cb32942SEric W. Biederman return result; 2247cb32942SEric W. Biederman } 2254b30ee58STejun Heo EXPORT_SYMBOL_GPL(sysfs_rename_link_ns); 2267cb32942SEric W. Biederman 2272b29ac25STejun Heo static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, 2282b29ac25STejun Heo struct sysfs_dirent *target_sd, char *path) 2291da177e4SLinus Torvalds { 2302f90a851SKay Sievers struct sysfs_dirent *base, *sd; 2312f90a851SKay Sievers char *s = path; 2322f90a851SKay Sievers int len = 0; 2331da177e4SLinus Torvalds 2342f90a851SKay Sievers /* go up to the root, stop at the base */ 2352f90a851SKay Sievers base = parent_sd; 2362f90a851SKay Sievers while (base->s_parent) { 2372f90a851SKay Sievers sd = target_sd->s_parent; 2382f90a851SKay Sievers while (sd->s_parent && base != sd) 2392f90a851SKay Sievers sd = sd->s_parent; 2402f90a851SKay Sievers 2412f90a851SKay Sievers if (base == sd) 2422f90a851SKay Sievers break; 2432f90a851SKay Sievers 2442f90a851SKay Sievers strcpy(s, "../"); 2452f90a851SKay Sievers s += 3; 2462f90a851SKay Sievers base = base->s_parent; 2472f90a851SKay Sievers } 2482f90a851SKay Sievers 2492f90a851SKay Sievers /* determine end of target string for reverse fillup */ 2502f90a851SKay Sievers sd = target_sd; 2512f90a851SKay Sievers while (sd->s_parent && sd != base) { 2522f90a851SKay Sievers len += strlen(sd->s_name) + 1; 2532f90a851SKay Sievers sd = sd->s_parent; 2542f90a851SKay Sievers } 2552f90a851SKay Sievers 2562f90a851SKay Sievers /* check limits */ 2572f90a851SKay Sievers if (len < 2) 2582f90a851SKay Sievers return -EINVAL; 2592f90a851SKay Sievers len--; 2602f90a851SKay Sievers if ((s - path) + len > PATH_MAX) 2611da177e4SLinus Torvalds return -ENAMETOOLONG; 2621da177e4SLinus Torvalds 2632f90a851SKay Sievers /* reverse fillup of target string from target to base */ 2642f90a851SKay Sievers sd = target_sd; 2652f90a851SKay Sievers while (sd->s_parent && sd != base) { 2662f90a851SKay Sievers int slen = strlen(sd->s_name); 2671da177e4SLinus Torvalds 2682f90a851SKay Sievers len -= slen; 2692f90a851SKay Sievers strncpy(s + len, sd->s_name, slen); 2702f90a851SKay Sievers if (len) 2712f90a851SKay Sievers s[--len] = '/'; 2721da177e4SLinus Torvalds 2732f90a851SKay Sievers sd = sd->s_parent; 2742f90a851SKay Sievers } 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds return 0; 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds static int sysfs_getlink(struct dentry *dentry, char *path) 2801da177e4SLinus Torvalds { 2812b29ac25STejun Heo struct sysfs_dirent *sd = dentry->d_fsdata; 2822b29ac25STejun Heo struct sysfs_dirent *parent_sd = sd->s_parent; 283b1fc3d61STejun Heo struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; 2842b29ac25STejun Heo int error; 2851da177e4SLinus Torvalds 2863007e997STejun Heo mutex_lock(&sysfs_mutex); 2872b29ac25STejun Heo error = sysfs_get_target_path(parent_sd, target_sd, path); 2883007e997STejun Heo mutex_unlock(&sysfs_mutex); 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds return error; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 293cc314eefSLinus Torvalds static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 2941da177e4SLinus Torvalds { 2951da177e4SLinus Torvalds int error = -ENOMEM; 2961da177e4SLinus Torvalds unsigned long page = get_zeroed_page(GFP_KERNEL); 297557411ebSArmin Kuster if (page) { 2981da177e4SLinus Torvalds error = sysfs_getlink(dentry, (char *) page); 299557411ebSArmin Kuster if (error < 0) 300557411ebSArmin Kuster free_page((unsigned long)page); 301557411ebSArmin Kuster } 3021da177e4SLinus Torvalds nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); 303cc314eefSLinus Torvalds return NULL; 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds 306ddfd6d07SGreg Kroah-Hartman static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, 307ddfd6d07SGreg Kroah-Hartman void *cookie) 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds char *page = nd_get_link(nd); 3101da177e4SLinus Torvalds if (!IS_ERR(page)) 3111da177e4SLinus Torvalds free_page((unsigned long)page); 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 314c5ef1c42SArjan van de Ven const struct inode_operations sysfs_symlink_inode_operations = { 315ddd29ec6SDavid P. Quigley .setxattr = sysfs_setxattr, 3161da177e4SLinus Torvalds .readlink = generic_readlink, 3171da177e4SLinus Torvalds .follow_link = sysfs_follow_link, 3181da177e4SLinus Torvalds .put_link = sysfs_put_link, 319e61ab4aeSEric W. Biederman .setattr = sysfs_setattr, 320e61ab4aeSEric W. Biederman .getattr = sysfs_getattr, 321e61ab4aeSEric W. Biederman .permission = sysfs_permission, 3221da177e4SLinus Torvalds }; 323