diff options
Diffstat (limited to 'fs/sysfs/symlink.c')
-rw-r--r-- | fs/sysfs/symlink.c | 179 |
1 files changed, 23 insertions, 156 deletions
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 3ae3f1bf1a09..1b8c9ed8511a 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c | |||
@@ -11,11 +11,8 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/fs.h> | 13 | #include <linux/fs.h> |
14 | #include <linux/gfp.h> | ||
15 | #include <linux/mount.h> | ||
16 | #include <linux/module.h> | 14 | #include <linux/module.h> |
17 | #include <linux/kobject.h> | 15 | #include <linux/kobject.h> |
18 | #include <linux/namei.h> | ||
19 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
20 | #include <linux/security.h> | 17 | #include <linux/security.h> |
21 | 18 | ||
@@ -25,11 +22,7 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, | |||
25 | struct kobject *target, | 22 | struct kobject *target, |
26 | const char *name, int warn) | 23 | const char *name, int warn) |
27 | { | 24 | { |
28 | struct sysfs_dirent *target_sd = NULL; | 25 | struct sysfs_dirent *sd, *target_sd = NULL; |
29 | struct sysfs_dirent *sd = NULL; | ||
30 | struct sysfs_addrm_cxt acxt; | ||
31 | enum kobj_ns_type ns_type; | ||
32 | int error; | ||
33 | 26 | ||
34 | BUG_ON(!name || !parent_sd); | 27 | BUG_ON(!name || !parent_sd); |
35 | 28 | ||
@@ -39,53 +32,24 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, | |||
39 | * sysfs_remove_dir() for details. | 32 | * sysfs_remove_dir() for details. |
40 | */ | 33 | */ |
41 | spin_lock(&sysfs_symlink_target_lock); | 34 | spin_lock(&sysfs_symlink_target_lock); |
42 | if (target->sd) | 35 | if (target->sd) { |
43 | target_sd = sysfs_get(target->sd); | 36 | target_sd = target->sd; |
37 | kernfs_get(target_sd); | ||
38 | } | ||
44 | spin_unlock(&sysfs_symlink_target_lock); | 39 | spin_unlock(&sysfs_symlink_target_lock); |
45 | 40 | ||
46 | error = -ENOENT; | ||
47 | if (!target_sd) | 41 | if (!target_sd) |
48 | goto out_put; | 42 | return -ENOENT; |
49 | |||
50 | error = -ENOMEM; | ||
51 | sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); | ||
52 | if (!sd) | ||
53 | goto out_put; | ||
54 | 43 | ||
55 | ns_type = sysfs_ns_type(parent_sd); | 44 | sd = kernfs_create_link(parent_sd, name, target_sd); |
56 | if (ns_type) | 45 | kernfs_put(target_sd); |
57 | sd->s_ns = target_sd->s_ns; | ||
58 | sd->s_symlink.target_sd = target_sd; | ||
59 | target_sd = NULL; /* reference is now owned by the symlink */ | ||
60 | |||
61 | sysfs_addrm_start(&acxt); | ||
62 | /* Symlinks must be between directories with the same ns_type */ | ||
63 | if (!ns_type || | ||
64 | (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) { | ||
65 | if (warn) | ||
66 | error = sysfs_add_one(&acxt, sd, parent_sd); | ||
67 | else | ||
68 | error = __sysfs_add_one(&acxt, sd, parent_sd); | ||
69 | } else { | ||
70 | error = -EINVAL; | ||
71 | WARN(1, KERN_WARNING | ||
72 | "sysfs: symlink across ns_types %s/%s -> %s/%s\n", | ||
73 | parent_sd->s_name, | ||
74 | sd->s_name, | ||
75 | sd->s_symlink.target_sd->s_parent->s_name, | ||
76 | sd->s_symlink.target_sd->s_name); | ||
77 | } | ||
78 | sysfs_addrm_finish(&acxt); | ||
79 | 46 | ||
80 | if (error) | 47 | if (!IS_ERR(sd)) |
81 | goto out_put; | 48 | return 0; |
82 | 49 | ||
83 | return 0; | 50 | if (warn && PTR_ERR(sd) == -EEXIST) |
84 | 51 | sysfs_warn_dup(parent_sd, name); | |
85 | out_put: | 52 | return PTR_ERR(sd); |
86 | sysfs_put(target_sd); | ||
87 | sysfs_put(sd); | ||
88 | return error; | ||
89 | } | 53 | } |
90 | 54 | ||
91 | /** | 55 | /** |
@@ -106,7 +70,7 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, | |||
106 | struct sysfs_dirent *parent_sd = NULL; | 70 | struct sysfs_dirent *parent_sd = NULL; |
107 | 71 | ||
108 | if (!kobj) | 72 | if (!kobj) |
109 | parent_sd = &sysfs_root; | 73 | parent_sd = sysfs_root_sd; |
110 | else | 74 | else |
111 | parent_sd = kobj->sd; | 75 | parent_sd = kobj->sd; |
112 | 76 | ||
@@ -164,10 +128,10 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, | |||
164 | * sysfs_remove_dir() for details. | 128 | * sysfs_remove_dir() for details. |
165 | */ | 129 | */ |
166 | spin_lock(&sysfs_symlink_target_lock); | 130 | spin_lock(&sysfs_symlink_target_lock); |
167 | if (targ->sd && sysfs_ns_type(kobj->sd)) | 131 | if (targ->sd && kernfs_ns_enabled(kobj->sd)) |
168 | ns = targ->sd->s_ns; | 132 | ns = targ->sd->s_ns; |
169 | spin_unlock(&sysfs_symlink_target_lock); | 133 | spin_unlock(&sysfs_symlink_target_lock); |
170 | sysfs_hash_and_remove(kobj->sd, name, ns); | 134 | kernfs_remove_by_name_ns(kobj->sd, name, ns); |
171 | } | 135 | } |
172 | 136 | ||
173 | /** | 137 | /** |
@@ -180,11 +144,11 @@ void sysfs_remove_link(struct kobject *kobj, const char *name) | |||
180 | struct sysfs_dirent *parent_sd = NULL; | 144 | struct sysfs_dirent *parent_sd = NULL; |
181 | 145 | ||
182 | if (!kobj) | 146 | if (!kobj) |
183 | parent_sd = &sysfs_root; | 147 | parent_sd = sysfs_root_sd; |
184 | else | 148 | else |
185 | parent_sd = kobj->sd; | 149 | parent_sd = kobj->sd; |
186 | 150 | ||
187 | sysfs_hash_and_remove(parent_sd, name, NULL); | 151 | kernfs_remove_by_name(parent_sd, name); |
188 | } | 152 | } |
189 | EXPORT_SYMBOL_GPL(sysfs_remove_link); | 153 | EXPORT_SYMBOL_GPL(sysfs_remove_link); |
190 | 154 | ||
@@ -206,7 +170,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, | |||
206 | int result; | 170 | int result; |
207 | 171 | ||
208 | if (!kobj) | 172 | if (!kobj) |
209 | parent_sd = &sysfs_root; | 173 | parent_sd = sysfs_root_sd; |
210 | else | 174 | else |
211 | parent_sd = kobj->sd; | 175 | parent_sd = kobj->sd; |
212 | 176 | ||
@@ -214,117 +178,20 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, | |||
214 | old_ns = targ->sd->s_ns; | 178 | old_ns = targ->sd->s_ns; |
215 | 179 | ||
216 | result = -ENOENT; | 180 | result = -ENOENT; |
217 | sd = sysfs_get_dirent_ns(parent_sd, old, old_ns); | 181 | sd = kernfs_find_and_get_ns(parent_sd, old, old_ns); |
218 | if (!sd) | 182 | if (!sd) |
219 | goto out; | 183 | goto out; |
220 | 184 | ||
221 | result = -EINVAL; | 185 | result = -EINVAL; |
222 | if (sysfs_type(sd) != SYSFS_KOBJ_LINK) | 186 | if (sysfs_type(sd) != SYSFS_KOBJ_LINK) |
223 | goto out; | 187 | goto out; |
224 | if (sd->s_symlink.target_sd->s_dir.kobj != targ) | 188 | if (sd->s_symlink.target_sd->priv != targ) |
225 | goto out; | 189 | goto out; |
226 | 190 | ||
227 | result = sysfs_rename(sd, parent_sd, new, new_ns); | 191 | result = kernfs_rename_ns(sd, parent_sd, new, new_ns); |
228 | 192 | ||
229 | out: | 193 | out: |
230 | sysfs_put(sd); | 194 | kernfs_put(sd); |
231 | return result; | 195 | return result; |
232 | } | 196 | } |
233 | EXPORT_SYMBOL_GPL(sysfs_rename_link_ns); | 197 | EXPORT_SYMBOL_GPL(sysfs_rename_link_ns); |
234 | |||
235 | static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, | ||
236 | struct sysfs_dirent *target_sd, char *path) | ||
237 | { | ||
238 | struct sysfs_dirent *base, *sd; | ||
239 | char *s = path; | ||
240 | int len = 0; | ||
241 | |||
242 | /* go up to the root, stop at the base */ | ||
243 | base = parent_sd; | ||
244 | while (base->s_parent) { | ||
245 | sd = target_sd->s_parent; | ||
246 | while (sd->s_parent && base != sd) | ||
247 | sd = sd->s_parent; | ||
248 | |||
249 | if (base == sd) | ||
250 | break; | ||
251 | |||
252 | strcpy(s, "../"); | ||
253 | s += 3; | ||
254 | base = base->s_parent; | ||
255 | } | ||
256 | |||
257 | /* determine end of target string for reverse fillup */ | ||
258 | sd = target_sd; | ||
259 | while (sd->s_parent && sd != base) { | ||
260 | len += strlen(sd->s_name) + 1; | ||
261 | sd = sd->s_parent; | ||
262 | } | ||
263 | |||
264 | /* check limits */ | ||
265 | if (len < 2) | ||
266 | return -EINVAL; | ||
267 | len--; | ||
268 | if ((s - path) + len > PATH_MAX) | ||
269 | return -ENAMETOOLONG; | ||
270 | |||
271 | /* reverse fillup of target string from target to base */ | ||
272 | sd = target_sd; | ||
273 | while (sd->s_parent && sd != base) { | ||
274 | int slen = strlen(sd->s_name); | ||
275 | |||
276 | len -= slen; | ||
277 | strncpy(s + len, sd->s_name, slen); | ||
278 | if (len) | ||
279 | s[--len] = '/'; | ||
280 | |||
281 | sd = sd->s_parent; | ||
282 | } | ||
283 | |||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static int sysfs_getlink(struct dentry *dentry, char *path) | ||
288 | { | ||
289 | struct sysfs_dirent *sd = dentry->d_fsdata; | ||
290 | struct sysfs_dirent *parent_sd = sd->s_parent; | ||
291 | struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; | ||
292 | int error; | ||
293 | |||
294 | mutex_lock(&sysfs_mutex); | ||
295 | error = sysfs_get_target_path(parent_sd, target_sd, path); | ||
296 | mutex_unlock(&sysfs_mutex); | ||
297 | |||
298 | return error; | ||
299 | } | ||
300 | |||
301 | static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
302 | { | ||
303 | int error = -ENOMEM; | ||
304 | unsigned long page = get_zeroed_page(GFP_KERNEL); | ||
305 | if (page) { | ||
306 | error = sysfs_getlink(dentry, (char *) page); | ||
307 | if (error < 0) | ||
308 | free_page((unsigned long)page); | ||
309 | } | ||
310 | nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); | ||
311 | return NULL; | ||
312 | } | ||
313 | |||
314 | static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, | ||
315 | void *cookie) | ||
316 | { | ||
317 | char *page = nd_get_link(nd); | ||
318 | if (!IS_ERR(page)) | ||
319 | free_page((unsigned long)page); | ||
320 | } | ||
321 | |||
322 | const struct inode_operations sysfs_symlink_inode_operations = { | ||
323 | .setxattr = sysfs_setxattr, | ||
324 | .readlink = generic_readlink, | ||
325 | .follow_link = sysfs_follow_link, | ||
326 | .put_link = sysfs_put_link, | ||
327 | .setattr = sysfs_setattr, | ||
328 | .getattr = sysfs_getattr, | ||
329 | .permission = sysfs_permission, | ||
330 | }; | ||