Blame view

fs/sysfs/symlink.c 7.11 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
6d66f5cd2   Tejun Heo   sysfs: add copyri...
2
3
4
5
6
7
8
9
10
   * fs/sysfs/symlink.c - sysfs symlink implementation
   *
   * Copyright (c) 2001-3 Patrick Mochel
   * Copyright (c) 2007 SUSE Linux Products GmbH
   * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
   *
   * This file is released under the GPLv2.
   *
   * Please see Documentation/filesystems/sysfs.txt for more information.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
   */
  
  #include <linux/fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/gfp.h>
ceeee1fb2   Greg Kroah-Hartman   SYSFS: allow sysf...
15
  #include <linux/mount.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
  #include <linux/module.h>
  #include <linux/kobject.h>
  #include <linux/namei.h>
869512ab5   Dave Young   sysfs: cleanup se...
19
  #include <linux/mutex.h>
ddd29ec65   David P. Quigley   sysfs: Add labeli...
20
  #include <linux/security.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
  
  #include "sysfs.h"
36ce6dad6   Cornelia Huck   driver core: Supp...
23
24
  static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
  				const char *name, int warn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  {
2b29ac252   Tejun Heo   sysfs: reimplemen...
26
27
  	struct sysfs_dirent *parent_sd = NULL;
  	struct sysfs_dirent *target_sd = NULL;
3007e997d   Tejun Heo   sysfs: use sysfs_...
28
  	struct sysfs_dirent *sd = NULL;
fb6896da3   Tejun Heo   sysfs: restructur...
29
  	struct sysfs_addrm_cxt acxt;
96d6523ad   Eric W. Biederman   sysfs: Don't allo...
30
  	enum kobj_ns_type ns_type;
3007e997d   Tejun Heo   sysfs: use sysfs_...
31
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32

ceeee1fb2   Greg Kroah-Hartman   SYSFS: allow sysf...
33
  	BUG_ON(!name);
7d0c7d676   Eric W. Biederman   sysfs: Make sysfs...
34
35
36
  	if (!kobj)
  		parent_sd = &sysfs_root;
  	else
608e266a2   Tejun Heo   sysfs: make kobj ...
37
  		parent_sd = kobj->sd;
ceeee1fb2   Greg Kroah-Hartman   SYSFS: allow sysf...
38

3007e997d   Tejun Heo   sysfs: use sysfs_...
39
  	error = -EFAULT;
608e266a2   Tejun Heo   sysfs: make kobj ...
40
  	if (!parent_sd)
3007e997d   Tejun Heo   sysfs: use sysfs_...
41
  		goto out_put;
2b29ac252   Tejun Heo   sysfs: reimplemen...
42

608e266a2   Tejun Heo   sysfs: make kobj ...
43
  	/* target->sd can go away beneath us but is protected with
5f9953237   Tejun Heo   sysfs: consolidat...
44
  	 * sysfs_assoc_lock.  Fetch target_sd from it.
2b29ac252   Tejun Heo   sysfs: reimplemen...
45
  	 */
5f9953237   Tejun Heo   sysfs: consolidat...
46
  	spin_lock(&sysfs_assoc_lock);
608e266a2   Tejun Heo   sysfs: make kobj ...
47
48
  	if (target->sd)
  		target_sd = sysfs_get(target->sd);
5f9953237   Tejun Heo   sysfs: consolidat...
49
  	spin_unlock(&sysfs_assoc_lock);
2b29ac252   Tejun Heo   sysfs: reimplemen...
50

3007e997d   Tejun Heo   sysfs: use sysfs_...
51
  	error = -ENOENT;
2b29ac252   Tejun Heo   sysfs: reimplemen...
52
  	if (!target_sd)
3007e997d   Tejun Heo   sysfs: use sysfs_...
53
54
55
56
57
58
  		goto out_put;
  
  	error = -ENOMEM;
  	sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
  	if (!sd)
  		goto out_put;
a1da4dfe3   Tejun Heo   sysfs: kill an ex...
59

96d6523ad   Eric W. Biederman   sysfs: Don't allo...
60
61
  	ns_type = sysfs_ns_type(parent_sd);
  	if (ns_type)
3ff195b01   Eric W. Biederman   sysfs: Implement ...
62
  		sd->s_ns = target->ktype->namespace(target);
b1fc3d614   Tejun Heo   sysfs: make s_ele...
63
  	sd->s_symlink.target_sd = target_sd;
a1da4dfe3   Tejun Heo   sysfs: kill an ex...
64
  	target_sd = NULL;	/* reference is now owned by the symlink */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65

fb6896da3   Tejun Heo   sysfs: restructur...
66
  	sysfs_addrm_start(&acxt, parent_sd);
96d6523ad   Eric W. Biederman   sysfs: Don't allo...
67
  	/* Symlinks must be between directories with the same ns_type */
d33002129   Eric W. Biederman   sysfs: allow crea...
68
69
  	if (!ns_type ||
  	    (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) {
96d6523ad   Eric W. Biederman   sysfs: Don't allo...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  		if (warn)
  			error = sysfs_add_one(&acxt, sd);
  		else
  			error = __sysfs_add_one(&acxt, sd);
  	} else {
  		error = -EINVAL;
  		WARN(1, KERN_WARNING
  			"sysfs: symlink across ns_types %s/%s -> %s/%s
  ",
  			parent_sd->s_name,
  			sd->s_name,
  			sd->s_symlink.target_sd->s_parent->s_name,
  			sd->s_symlink.target_sd->s_name);
  	}
23dc27995   Tejun Heo   sysfs: make sysfs...
84
  	sysfs_addrm_finish(&acxt);
2b29ac252   Tejun Heo   sysfs: reimplemen...
85

23dc27995   Tejun Heo   sysfs: make sysfs...
86
  	if (error)
967e35dcc   Tejun Heo   sysfs: cosmetic c...
87
  		goto out_put;
967e35dcc   Tejun Heo   sysfs: cosmetic c...
88
89
  
  	return 0;
fb6896da3   Tejun Heo   sysfs: restructur...
90

3007e997d   Tejun Heo   sysfs: use sysfs_...
91
92
93
   out_put:
  	sysfs_put(target_sd);
  	sysfs_put(sd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  /**
36ce6dad6   Cornelia Huck   driver core: Supp...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
   *	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.
   */
  int sysfs_create_link(struct kobject *kobj, struct kobject *target,
  		      const char *name)
  {
  	return sysfs_do_create_link(kobj, target, name, 1);
  }
  
  /**
   *	sysfs_create_link_nowarn - 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.
   *
   *	This function does the same as sysf_create_link(), but it
   *	doesn't warn if the link already exists.
   */
  int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target,
  			     const char *name)
  {
  	return sysfs_do_create_link(kobj, target, name, 0);
  }
  
  /**
746edb7ae   Eric W. Biederman   sysfs: Implement ...
124
125
126
127
128
129
130
131
132
133
134
135
136
   *	sysfs_delete_link - remove symlink in object's directory.
   *	@kobj:	object we're acting for.
   *	@targ:	object we're pointing to.
   *	@name:	name of the symlink to remove.
   *
   *	Unlike sysfs_remove_link sysfs_delete_link has enough information
   *	to successfully delete symlinks in tagged directories.
   */
  void sysfs_delete_link(struct kobject *kobj, struct kobject *targ,
  			const char *name)
  {
  	const void *ns = NULL;
  	spin_lock(&sysfs_assoc_lock);
521d04535   Eric W. Biederman   sysfs: sysfs_dele...
137
  	if (targ->sd && sysfs_ns_type(kobj->sd))
746edb7ae   Eric W. Biederman   sysfs: Implement ...
138
139
140
141
142
143
  		ns = targ->sd->s_ns;
  	spin_unlock(&sysfs_assoc_lock);
  	sysfs_hash_and_remove(kobj->sd, ns, name);
  }
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
   *	sysfs_remove_link - remove symlink in object's directory.
   *	@kobj:	object we're acting for.
   *	@name:	name of the symlink to remove.
   */
e3a15db24   Dmitry Torokhov   [PATCH] sysfs_{cr...
148
  void sysfs_remove_link(struct kobject * kobj, const char * name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  {
a839c5afc   Mark Fasheh   sysfs: Allow remo...
150
151
152
153
154
155
  	struct sysfs_dirent *parent_sd = NULL;
  
  	if (!kobj)
  		parent_sd = &sysfs_root;
  	else
  		parent_sd = kobj->sd;
3ff195b01   Eric W. Biederman   sysfs: Implement ...
156
  	sysfs_hash_and_remove(parent_sd, NULL, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  }
7cb32942d   Eric W. Biederman   sysfs: Implement ...
158
159
160
161
162
163
164
165
166
167
168
169
170
  /**
   *	sysfs_rename_link - rename symlink in object's directory.
   *	@kobj:	object we're acting for.
   *	@targ:	object we're pointing to.
   *	@old:	previous name of the symlink.
   *	@new:	new name of the symlink.
   *
   *	A helper function for the common rename symlink idiom.
   */
  int sysfs_rename_link(struct kobject *kobj, struct kobject *targ,
  			const char *old, const char *new)
  {
  	struct sysfs_dirent *parent_sd, *sd = NULL;
3ff195b01   Eric W. Biederman   sysfs: Implement ...
171
  	const void *old_ns = NULL, *new_ns = NULL;
7cb32942d   Eric W. Biederman   sysfs: Implement ...
172
173
174
175
176
177
  	int result;
  
  	if (!kobj)
  		parent_sd = &sysfs_root;
  	else
  		parent_sd = kobj->sd;
3ff195b01   Eric W. Biederman   sysfs: Implement ...
178
179
  	if (targ->sd)
  		old_ns = targ->sd->s_ns;
7cb32942d   Eric W. Biederman   sysfs: Implement ...
180
  	result = -ENOENT;
3ff195b01   Eric W. Biederman   sysfs: Implement ...
181
  	sd = sysfs_get_dirent(parent_sd, old_ns, old);
7cb32942d   Eric W. Biederman   sysfs: Implement ...
182
183
184
185
186
187
188
189
  	if (!sd)
  		goto out;
  
  	result = -EINVAL;
  	if (sysfs_type(sd) != SYSFS_KOBJ_LINK)
  		goto out;
  	if (sd->s_symlink.target_sd->s_dir.kobj != targ)
  		goto out;
3ff195b01   Eric W. Biederman   sysfs: Implement ...
190
191
192
193
  	if (sysfs_ns_type(parent_sd))
  		new_ns = targ->ktype->namespace(targ);
  
  	result = sysfs_rename(sd, parent_sd, new_ns, new);
7cb32942d   Eric W. Biederman   sysfs: Implement ...
194
195
196
197
198
  
  out:
  	sysfs_put(sd);
  	return result;
  }
2f90a8518   Kay Sievers   sysfs: create opt...
199
200
  static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
  				 struct sysfs_dirent *target_sd, char *path)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  {
2f90a8518   Kay Sievers   sysfs: create opt...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  	struct sysfs_dirent *base, *sd;
  	char *s = path;
  	int len = 0;
  
  	/* go up to the root, stop at the base */
  	base = parent_sd;
  	while (base->s_parent) {
  		sd = target_sd->s_parent;
  		while (sd->s_parent && base != sd)
  			sd = sd->s_parent;
  
  		if (base == sd)
  			break;
  
  		strcpy(s, "../");
  		s += 3;
  		base = base->s_parent;
  	}
  
  	/* determine end of target string for reverse fillup */
  	sd = target_sd;
  	while (sd->s_parent && sd != base) {
  		len += strlen(sd->s_name) + 1;
  		sd = sd->s_parent;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227

2f90a8518   Kay Sievers   sysfs: create opt...
228
229
230
231
232
  	/* check limits */
  	if (len < 2)
  		return -EINVAL;
  	len--;
  	if ((s - path) + len > PATH_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  		return -ENAMETOOLONG;
2f90a8518   Kay Sievers   sysfs: create opt...
234
235
236
237
  	/* reverse fillup of target string from target to base */
  	sd = target_sd;
  	while (sd->s_parent && sd != base) {
  		int slen = strlen(sd->s_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238

2f90a8518   Kay Sievers   sysfs: create opt...
239
240
241
242
  		len -= slen;
  		strncpy(s + len, sd->s_name, slen);
  		if (len)
  			s[--len] = '/';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243

2f90a8518   Kay Sievers   sysfs: create opt...
244
245
  		sd = sd->s_parent;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
  
  	return 0;
  }
  
  static int sysfs_getlink(struct dentry *dentry, char * path)
  {
2b29ac252   Tejun Heo   sysfs: reimplemen...
252
253
  	struct sysfs_dirent *sd = dentry->d_fsdata;
  	struct sysfs_dirent *parent_sd = sd->s_parent;
b1fc3d614   Tejun Heo   sysfs: make s_ele...
254
  	struct sysfs_dirent *target_sd = sd->s_symlink.target_sd;
2b29ac252   Tejun Heo   sysfs: reimplemen...
255
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256

3007e997d   Tejun Heo   sysfs: use sysfs_...
257
  	mutex_lock(&sysfs_mutex);
2b29ac252   Tejun Heo   sysfs: reimplemen...
258
  	error = sysfs_get_target_path(parent_sd, target_sd, path);
3007e997d   Tejun Heo   sysfs: use sysfs_...
259
  	mutex_unlock(&sysfs_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260

2b29ac252   Tejun Heo   sysfs: reimplemen...
261
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
  }
cc314eef0   Linus Torvalds   Fix nasty ncpfs s...
263
  static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
  {
  	int error = -ENOMEM;
  	unsigned long page = get_zeroed_page(GFP_KERNEL);
557411eb2   Armin Kuster   Sysfs: fix possib...
267
  	if (page) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
  		error = sysfs_getlink(dentry, (char *) page); 
557411eb2   Armin Kuster   Sysfs: fix possib...
269
270
271
  		if (error < 0)
  			free_page((unsigned long)page);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  	nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
cc314eef0   Linus Torvalds   Fix nasty ncpfs s...
273
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  }
cc314eef0   Linus Torvalds   Fix nasty ncpfs s...
275
  static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
280
  {
  	char *page = nd_get_link(nd);
  	if (!IS_ERR(page))
  		free_page((unsigned long)page);
  }
c5ef1c42c   Arjan van de Ven   [PATCH] mark stru...
281
  const struct inode_operations sysfs_symlink_inode_operations = {
c099aacd4   Eric W. Biederman   sysfs: Nicely ind...
282
283
284
285
  	.setxattr	= sysfs_setxattr,
  	.readlink	= generic_readlink,
  	.follow_link	= sysfs_follow_link,
  	.put_link	= sysfs_put_link,
e61ab4ae4   Eric W. Biederman   sysfs: Implement ...
286
287
288
  	.setattr	= sysfs_setattr,
  	.getattr	= sysfs_getattr,
  	.permission	= sysfs_permission,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
  };
  
  
  EXPORT_SYMBOL_GPL(sysfs_create_link);
  EXPORT_SYMBOL_GPL(sysfs_remove_link);
e0f43752a   Simon Arlott   bridge: update sy...
294
  EXPORT_SYMBOL_GPL(sysfs_rename_link);