diff options
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r-- | fs/sysfs/dir.c | 214 |
1 files changed, 175 insertions, 39 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 511edef8b321..9dcdf556c99c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/kobject.h> | 10 | #include <linux/kobject.h> |
11 | #include <linux/namei.h> | 11 | #include <linux/namei.h> |
12 | #include <asm/semaphore.h> | ||
12 | #include "sysfs.h" | 13 | #include "sysfs.h" |
13 | 14 | ||
14 | DECLARE_RWSEM(sysfs_rename_sem); | 15 | DECLARE_RWSEM(sysfs_rename_sem); |
@@ -32,8 +33,7 @@ static struct dentry_operations sysfs_dentry_ops = { | |||
32 | /* | 33 | /* |
33 | * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent | 34 | * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent |
34 | */ | 35 | */ |
35 | static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, | 36 | static struct sysfs_dirent * __sysfs_new_dirent(void * element) |
36 | void * element) | ||
37 | { | 37 | { |
38 | struct sysfs_dirent * sd; | 38 | struct sysfs_dirent * sd; |
39 | 39 | ||
@@ -45,12 +45,28 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, | |||
45 | atomic_set(&sd->s_count, 1); | 45 | atomic_set(&sd->s_count, 1); |
46 | atomic_set(&sd->s_event, 1); | 46 | atomic_set(&sd->s_event, 1); |
47 | INIT_LIST_HEAD(&sd->s_children); | 47 | INIT_LIST_HEAD(&sd->s_children); |
48 | list_add(&sd->s_sibling, &parent_sd->s_children); | 48 | INIT_LIST_HEAD(&sd->s_sibling); |
49 | sd->s_element = element; | 49 | sd->s_element = element; |
50 | 50 | ||
51 | return sd; | 51 | return sd; |
52 | } | 52 | } |
53 | 53 | ||
54 | static void __sysfs_list_dirent(struct sysfs_dirent *parent_sd, | ||
55 | struct sysfs_dirent *sd) | ||
56 | { | ||
57 | if (sd) | ||
58 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
59 | } | ||
60 | |||
61 | static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent *parent_sd, | ||
62 | void * element) | ||
63 | { | ||
64 | struct sysfs_dirent *sd; | ||
65 | sd = __sysfs_new_dirent(element); | ||
66 | __sysfs_list_dirent(parent_sd, sd); | ||
67 | return sd; | ||
68 | } | ||
69 | |||
54 | /* | 70 | /* |
55 | * | 71 | * |
56 | * Return -EEXIST if there is already a sysfs element with the same name for | 72 | * Return -EEXIST if there is already a sysfs element with the same name for |
@@ -77,14 +93,14 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, | |||
77 | } | 93 | } |
78 | 94 | ||
79 | 95 | ||
80 | int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | 96 | static struct sysfs_dirent * |
81 | void * element, umode_t mode, int type) | 97 | __sysfs_make_dirent(struct dentry *dentry, void *element, mode_t mode, int type) |
82 | { | 98 | { |
83 | struct sysfs_dirent * sd; | 99 | struct sysfs_dirent * sd; |
84 | 100 | ||
85 | sd = sysfs_new_dirent(parent_sd, element); | 101 | sd = __sysfs_new_dirent(element); |
86 | if (!sd) | 102 | if (!sd) |
87 | return -ENOMEM; | 103 | goto out; |
88 | 104 | ||
89 | sd->s_mode = mode; | 105 | sd->s_mode = mode; |
90 | sd->s_type = type; | 106 | sd->s_type = type; |
@@ -94,7 +110,19 @@ int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | |||
94 | dentry->d_op = &sysfs_dentry_ops; | 110 | dentry->d_op = &sysfs_dentry_ops; |
95 | } | 111 | } |
96 | 112 | ||
97 | return 0; | 113 | out: |
114 | return sd; | ||
115 | } | ||
116 | |||
117 | int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | ||
118 | void * element, umode_t mode, int type) | ||
119 | { | ||
120 | struct sysfs_dirent *sd; | ||
121 | |||
122 | sd = __sysfs_make_dirent(dentry, element, mode, type); | ||
123 | __sysfs_list_dirent(parent_sd, sd); | ||
124 | |||
125 | return sd ? 0 : -ENOMEM; | ||
98 | } | 126 | } |
99 | 127 | ||
100 | static int init_dir(struct inode * inode) | 128 | static int init_dir(struct inode * inode) |
@@ -165,11 +193,11 @@ int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d) | |||
165 | 193 | ||
166 | /** | 194 | /** |
167 | * sysfs_create_dir - create a directory for an object. | 195 | * sysfs_create_dir - create a directory for an object. |
168 | * @parent: parent parent object. | ||
169 | * @kobj: object we're creating directory for. | 196 | * @kobj: object we're creating directory for. |
197 | * @shadow_parent: parent parent object. | ||
170 | */ | 198 | */ |
171 | 199 | ||
172 | int sysfs_create_dir(struct kobject * kobj) | 200 | int sysfs_create_dir(struct kobject * kobj, struct dentry *shadow_parent) |
173 | { | 201 | { |
174 | struct dentry * dentry = NULL; | 202 | struct dentry * dentry = NULL; |
175 | struct dentry * parent; | 203 | struct dentry * parent; |
@@ -177,7 +205,9 @@ int sysfs_create_dir(struct kobject * kobj) | |||
177 | 205 | ||
178 | BUG_ON(!kobj); | 206 | BUG_ON(!kobj); |
179 | 207 | ||
180 | if (kobj->parent) | 208 | if (shadow_parent) |
209 | parent = shadow_parent; | ||
210 | else if (kobj->parent) | ||
181 | parent = kobj->parent->dentry; | 211 | parent = kobj->parent->dentry; |
182 | else if (sysfs_mount && sysfs_mount->mnt_sb) | 212 | else if (sysfs_mount && sysfs_mount->mnt_sb) |
183 | parent = sysfs_mount->mnt_sb->s_root; | 213 | parent = sysfs_mount->mnt_sb->s_root; |
@@ -298,21 +328,12 @@ void sysfs_remove_subdir(struct dentry * d) | |||
298 | } | 328 | } |
299 | 329 | ||
300 | 330 | ||
301 | /** | 331 | static void __sysfs_remove_dir(struct dentry *dentry) |
302 | * sysfs_remove_dir - remove an object's directory. | ||
303 | * @kobj: object. | ||
304 | * | ||
305 | * The only thing special about this is that we remove any files in | ||
306 | * the directory before we remove the directory, and we've inlined | ||
307 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
308 | */ | ||
309 | |||
310 | void sysfs_remove_dir(struct kobject * kobj) | ||
311 | { | 332 | { |
312 | struct dentry * dentry = dget(kobj->dentry); | ||
313 | struct sysfs_dirent * parent_sd; | 333 | struct sysfs_dirent * parent_sd; |
314 | struct sysfs_dirent * sd, * tmp; | 334 | struct sysfs_dirent * sd, * tmp; |
315 | 335 | ||
336 | dget(dentry); | ||
316 | if (!dentry) | 337 | if (!dentry) |
317 | return; | 338 | return; |
318 | 339 | ||
@@ -333,32 +354,60 @@ void sysfs_remove_dir(struct kobject * kobj) | |||
333 | * Drop reference from dget() on entrance. | 354 | * Drop reference from dget() on entrance. |
334 | */ | 355 | */ |
335 | dput(dentry); | 356 | dput(dentry); |
357 | } | ||
358 | |||
359 | /** | ||
360 | * sysfs_remove_dir - remove an object's directory. | ||
361 | * @kobj: object. | ||
362 | * | ||
363 | * The only thing special about this is that we remove any files in | ||
364 | * the directory before we remove the directory, and we've inlined | ||
365 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
366 | */ | ||
367 | |||
368 | void sysfs_remove_dir(struct kobject * kobj) | ||
369 | { | ||
370 | __sysfs_remove_dir(kobj->dentry); | ||
336 | kobj->dentry = NULL; | 371 | kobj->dentry = NULL; |
337 | } | 372 | } |
338 | 373 | ||
339 | int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | 374 | int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, |
375 | const char *new_name) | ||
340 | { | 376 | { |
341 | int error = 0; | 377 | int error = 0; |
342 | struct dentry * new_dentry, * parent; | 378 | struct dentry * new_dentry; |
343 | |||
344 | if (!strcmp(kobject_name(kobj), new_name)) | ||
345 | return -EINVAL; | ||
346 | 379 | ||
347 | if (!kobj->parent) | 380 | if (!new_parent) |
348 | return -EINVAL; | 381 | return -EFAULT; |
349 | 382 | ||
350 | down_write(&sysfs_rename_sem); | 383 | down_write(&sysfs_rename_sem); |
351 | parent = kobj->parent->dentry; | 384 | mutex_lock(&new_parent->d_inode->i_mutex); |
352 | |||
353 | mutex_lock(&parent->d_inode->i_mutex); | ||
354 | 385 | ||
355 | new_dentry = lookup_one_len(new_name, parent, strlen(new_name)); | 386 | new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); |
356 | if (!IS_ERR(new_dentry)) { | 387 | if (!IS_ERR(new_dentry)) { |
357 | if (!new_dentry->d_inode) { | 388 | /* By allowing two different directories with the |
389 | * same d_parent we allow this routine to move | ||
390 | * between different shadows of the same directory | ||
391 | */ | ||
392 | if (kobj->dentry->d_parent->d_inode != new_parent->d_inode) | ||
393 | return -EINVAL; | ||
394 | else if (new_dentry->d_parent->d_inode != new_parent->d_inode) | ||
395 | error = -EINVAL; | ||
396 | else if (new_dentry == kobj->dentry) | ||
397 | error = -EINVAL; | ||
398 | else if (!new_dentry->d_inode) { | ||
358 | error = kobject_set_name(kobj, "%s", new_name); | 399 | error = kobject_set_name(kobj, "%s", new_name); |
359 | if (!error) { | 400 | if (!error) { |
401 | struct sysfs_dirent *sd, *parent_sd; | ||
402 | |||
360 | d_add(new_dentry, NULL); | 403 | d_add(new_dentry, NULL); |
361 | d_move(kobj->dentry, new_dentry); | 404 | d_move(kobj->dentry, new_dentry); |
405 | |||
406 | sd = kobj->dentry->d_fsdata; | ||
407 | parent_sd = new_parent->d_fsdata; | ||
408 | |||
409 | list_del_init(&sd->s_sibling); | ||
410 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
362 | } | 411 | } |
363 | else | 412 | else |
364 | d_drop(new_dentry); | 413 | d_drop(new_dentry); |
@@ -366,7 +415,7 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | |||
366 | error = -EEXIST; | 415 | error = -EEXIST; |
367 | dput(new_dentry); | 416 | dput(new_dentry); |
368 | } | 417 | } |
369 | mutex_unlock(&parent->d_inode->i_mutex); | 418 | mutex_unlock(&new_parent->d_inode->i_mutex); |
370 | up_write(&sysfs_rename_sem); | 419 | up_write(&sysfs_rename_sem); |
371 | 420 | ||
372 | return error; | 421 | return error; |
@@ -378,12 +427,10 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent) | |||
378 | struct sysfs_dirent *new_parent_sd, *sd; | 427 | struct sysfs_dirent *new_parent_sd, *sd; |
379 | int error; | 428 | int error; |
380 | 429 | ||
381 | if (!new_parent) | ||
382 | return -EINVAL; | ||
383 | |||
384 | old_parent_dentry = kobj->parent ? | 430 | old_parent_dentry = kobj->parent ? |
385 | kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; | 431 | kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; |
386 | new_parent_dentry = new_parent->dentry; | 432 | new_parent_dentry = new_parent ? |
433 | new_parent->dentry : sysfs_mount->mnt_sb->s_root; | ||
387 | 434 | ||
388 | again: | 435 | again: |
389 | mutex_lock(&old_parent_dentry->d_inode->i_mutex); | 436 | mutex_lock(&old_parent_dentry->d_inode->i_mutex); |
@@ -547,6 +594,95 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
547 | return offset; | 594 | return offset; |
548 | } | 595 | } |
549 | 596 | ||
597 | |||
598 | /** | ||
599 | * sysfs_make_shadowed_dir - Setup so a directory can be shadowed | ||
600 | * @kobj: object we're creating shadow of. | ||
601 | */ | ||
602 | |||
603 | int sysfs_make_shadowed_dir(struct kobject *kobj, | ||
604 | void * (*follow_link)(struct dentry *, struct nameidata *)) | ||
605 | { | ||
606 | struct inode *inode; | ||
607 | struct inode_operations *i_op; | ||
608 | |||
609 | inode = kobj->dentry->d_inode; | ||
610 | if (inode->i_op != &sysfs_dir_inode_operations) | ||
611 | return -EINVAL; | ||
612 | |||
613 | i_op = kmalloc(sizeof(*i_op), GFP_KERNEL); | ||
614 | if (!i_op) | ||
615 | return -ENOMEM; | ||
616 | |||
617 | memcpy(i_op, &sysfs_dir_inode_operations, sizeof(*i_op)); | ||
618 | i_op->follow_link = follow_link; | ||
619 | |||
620 | /* Locking of inode->i_op? | ||
621 | * Since setting i_op is a single word write and they | ||
622 | * are atomic we should be ok here. | ||
623 | */ | ||
624 | inode->i_op = i_op; | ||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | /** | ||
629 | * sysfs_create_shadow_dir - create a shadow directory for an object. | ||
630 | * @kobj: object we're creating directory for. | ||
631 | * | ||
632 | * sysfs_make_shadowed_dir must already have been called on this | ||
633 | * directory. | ||
634 | */ | ||
635 | |||
636 | struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) | ||
637 | { | ||
638 | struct sysfs_dirent *sd; | ||
639 | struct dentry *parent, *dir, *shadow; | ||
640 | struct inode *inode; | ||
641 | |||
642 | dir = kobj->dentry; | ||
643 | inode = dir->d_inode; | ||
644 | parent = dir->d_parent; | ||
645 | shadow = ERR_PTR(-EINVAL); | ||
646 | if (!sysfs_is_shadowed_inode(inode)) | ||
647 | goto out; | ||
648 | |||
649 | shadow = d_alloc(parent, &dir->d_name); | ||
650 | if (!shadow) | ||
651 | goto nomem; | ||
652 | |||
653 | sd = __sysfs_make_dirent(shadow, kobj, inode->i_mode, SYSFS_DIR); | ||
654 | if (!sd) | ||
655 | goto nomem; | ||
656 | |||
657 | d_instantiate(shadow, igrab(inode)); | ||
658 | inc_nlink(inode); | ||
659 | inc_nlink(parent->d_inode); | ||
660 | shadow->d_op = &sysfs_dentry_ops; | ||
661 | |||
662 | dget(shadow); /* Extra count - pin the dentry in core */ | ||
663 | |||
664 | out: | ||
665 | return shadow; | ||
666 | nomem: | ||
667 | dput(shadow); | ||
668 | shadow = ERR_PTR(-ENOMEM); | ||
669 | goto out; | ||
670 | } | ||
671 | |||
672 | /** | ||
673 | * sysfs_remove_shadow_dir - remove an object's directory. | ||
674 | * @shadow: dentry of shadow directory | ||
675 | * | ||
676 | * The only thing special about this is that we remove any files in | ||
677 | * the directory before we remove the directory, and we've inlined | ||
678 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
679 | */ | ||
680 | |||
681 | void sysfs_remove_shadow_dir(struct dentry *shadow) | ||
682 | { | ||
683 | __sysfs_remove_dir(shadow); | ||
684 | } | ||
685 | |||
550 | const struct file_operations sysfs_dir_operations = { | 686 | const struct file_operations sysfs_dir_operations = { |
551 | .open = sysfs_dir_open, | 687 | .open = sysfs_dir_open, |
552 | .release = sysfs_dir_close, | 688 | .release = sysfs_dir_close, |