symlink.c 4.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * symlink.c - operations for sysfs symlinks.
 */

#include <linux/fs.h>
6
#include <linux/mount.h>
L
Linus Torvalds 已提交
7 8 9
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/namei.h>
D
Dave Young 已提交
10
#include <linux/mutex.h>
L
Linus Torvalds 已提交
11 12 13

#include "sysfs.h"

14
static int object_depth(struct sysfs_dirent *sd)
L
Linus Torvalds 已提交
15 16
{
	int depth = 0;
17 18 19 20

	for (; sd->s_parent; sd = sd->s_parent)
		depth++;

L
Linus Torvalds 已提交
21 22 23
	return depth;
}

24
static int object_path_length(struct sysfs_dirent * sd)
L
Linus Torvalds 已提交
25 26
{
	int length = 1;
27 28 29 30

	for (; sd->s_parent; sd = sd->s_parent)
		length += strlen(sd->s_name) + 1;

L
Linus Torvalds 已提交
31 32 33
	return length;
}

34
static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length)
L
Linus Torvalds 已提交
35 36
{
	--length;
37 38
	for (; sd->s_parent; sd = sd->s_parent) {
		int cur = strlen(sd->s_name);
L
Linus Torvalds 已提交
39 40 41

		/* back up enough to print this bus id with '/' */
		length -= cur;
42
		strncpy(buffer + length, sd->s_name, cur);
L
Linus Torvalds 已提交
43 44 45 46 47 48 49 50 51 52
		*(buffer + --length) = '/';
	}
}

/**
 *	sysfs_create_link - create symlink between two objects.
 *	@kobj:	object whose directory we're creating the link in.
 *	@target:	object we're pointing to.
 *	@name:		name of the symlink.
 */
53
int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
L
Linus Torvalds 已提交
54
{
55 56
	struct sysfs_dirent *parent_sd = NULL;
	struct sysfs_dirent *target_sd = NULL;
57
	struct sysfs_dirent *sd = NULL;
58
	struct sysfs_addrm_cxt acxt;
59
	int error;
L
Linus Torvalds 已提交
60

61 62 63 64
	BUG_ON(!name);

	if (!kobj) {
		if (sysfs_mount && sysfs_mount->mnt_sb)
65
			parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata;
66
	} else
67
		parent_sd = kobj->sd;
68

69
	error = -EFAULT;
70
	if (!parent_sd)
71
		goto out_put;
72

73
	/* target->sd can go away beneath us but is protected with
T
Tejun Heo 已提交
74
	 * sysfs_assoc_lock.  Fetch target_sd from it.
75
	 */
T
Tejun Heo 已提交
76
	spin_lock(&sysfs_assoc_lock);
77 78
	if (target->sd)
		target_sd = sysfs_get(target->sd);
T
Tejun Heo 已提交
79
	spin_unlock(&sysfs_assoc_lock);
80

81
	error = -ENOENT;
82
	if (!target_sd)
83 84 85 86 87 88
		goto out_put;

	error = -ENOMEM;
	sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
	if (!sd)
		goto out_put;
89

90
	sd->s_elem.symlink.target_sd = target_sd;
91
	target_sd = NULL;	/* reference is now owned by the symlink */
L
Linus Torvalds 已提交
92

93
	sysfs_addrm_start(&acxt, parent_sd);
94 95
	error = sysfs_add_one(&acxt, sd);
	sysfs_addrm_finish(&acxt);
96

97
	if (error)
98 99 100
		goto out_put;

	return 0;
101

102 103 104
 out_put:
	sysfs_put(target_sd);
	sysfs_put(sd);
L
Linus Torvalds 已提交
105 106 107 108 109 110 111 112 113 114
	return error;
}


/**
 *	sysfs_remove_link - remove symlink in object's directory.
 *	@kobj:	object we're acting for.
 *	@name:	name of the symlink to remove.
 */

115
void sysfs_remove_link(struct kobject * kobj, const char * name)
L
Linus Torvalds 已提交
116
{
117
	sysfs_hash_and_remove(kobj->sd, name);
L
Linus Torvalds 已提交
118 119
}

120 121
static int sysfs_get_target_path(struct sysfs_dirent * parent_sd,
				 struct sysfs_dirent * target_sd, char *path)
L
Linus Torvalds 已提交
122 123 124 125
{
	char * s;
	int depth, size;

126 127
	depth = object_depth(parent_sd);
	size = object_path_length(target_sd) + depth * 3 - 1;
L
Linus Torvalds 已提交
128 129 130 131 132 133 134 135
	if (size > PATH_MAX)
		return -ENAMETOOLONG;

	pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);

	for (s = path; depth--; s += 3)
		strcpy(s,"../");

136
	fill_object_path(target_sd, path, size);
L
Linus Torvalds 已提交
137 138 139 140 141 142 143
	pr_debug("%s: path = '%s'\n", __FUNCTION__, path);

	return 0;
}

static int sysfs_getlink(struct dentry *dentry, char * path)
{
144 145 146 147
	struct sysfs_dirent *sd = dentry->d_fsdata;
	struct sysfs_dirent *parent_sd = sd->s_parent;
	struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd;
	int error;
L
Linus Torvalds 已提交
148

149
	mutex_lock(&sysfs_mutex);
150
	error = sysfs_get_target_path(parent_sd, target_sd, path);
151
	mutex_unlock(&sysfs_mutex);
L
Linus Torvalds 已提交
152

153
	return error;
L
Linus Torvalds 已提交
154 155
}

156
static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
L
Linus Torvalds 已提交
157 158 159 160 161 162
{
	int error = -ENOMEM;
	unsigned long page = get_zeroed_page(GFP_KERNEL);
	if (page)
		error = sysfs_getlink(dentry, (char *) page); 
	nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
163
	return NULL;
L
Linus Torvalds 已提交
164 165
}

166
static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
L
Linus Torvalds 已提交
167 168 169 170 171 172
{
	char *page = nd_get_link(nd);
	if (!IS_ERR(page))
		free_page((unsigned long)page);
}

173
const struct inode_operations sysfs_symlink_inode_operations = {
L
Linus Torvalds 已提交
174 175 176 177 178 179 180 181
	.readlink = generic_readlink,
	.follow_link = sysfs_follow_link,
	.put_link = sysfs_put_link,
};


EXPORT_SYMBOL_GPL(sysfs_create_link);
EXPORT_SYMBOL_GPL(sysfs_remove_link);