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