xref: /openbmc/linux/fs/sysfs/symlink.c (revision 4b30ee58)
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