diff options
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r-- | fs/sysfs/dir.c | 207 |
1 files changed, 172 insertions, 35 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 9ff04491e3ea..9dcdf556c99c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -33,8 +33,7 @@ static struct dentry_operations sysfs_dentry_ops = { | |||
33 | /* | 33 | /* |
34 | * 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 |
35 | */ | 35 | */ |
36 | static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, | 36 | static struct sysfs_dirent * __sysfs_new_dirent(void * element) |
37 | void * element) | ||
38 | { | 37 | { |
39 | struct sysfs_dirent * sd; | 38 | struct sysfs_dirent * sd; |
40 | 39 | ||
@@ -46,12 +45,28 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, | |||
46 | atomic_set(&sd->s_count, 1); | 45 | atomic_set(&sd->s_count, 1); |
47 | atomic_set(&sd->s_event, 1); | 46 | atomic_set(&sd->s_event, 1); |
48 | INIT_LIST_HEAD(&sd->s_children); | 47 | INIT_LIST_HEAD(&sd->s_children); |
49 | list_add(&sd->s_sibling, &parent_sd->s_children); | 48 | INIT_LIST_HEAD(&sd->s_sibling); |
50 | sd->s_element = element; | 49 | sd->s_element = element; |
51 | 50 | ||
52 | return sd; | 51 | return sd; |
53 | } | 52 | } |
54 | 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 | |||
55 | /* | 70 | /* |
56 | * | 71 | * |
57 | * 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 |
@@ -78,14 +93,14 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, | |||
78 | } | 93 | } |
79 | 94 | ||
80 | 95 | ||
81 | int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | 96 | static struct sysfs_dirent * |
82 | void * element, umode_t mode, int type) | 97 | __sysfs_make_dirent(struct dentry *dentry, void *element, mode_t mode, int type) |
83 | { | 98 | { |
84 | struct sysfs_dirent * sd; | 99 | struct sysfs_dirent * sd; |
85 | 100 | ||
86 | sd = sysfs_new_dirent(parent_sd, element); | 101 | sd = __sysfs_new_dirent(element); |
87 | if (!sd) | 102 | if (!sd) |
88 | return -ENOMEM; | 103 | goto out; |
89 | 104 | ||
90 | sd->s_mode = mode; | 105 | sd->s_mode = mode; |
91 | sd->s_type = type; | 106 | sd->s_type = type; |
@@ -95,7 +110,19 @@ int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | |||
95 | dentry->d_op = &sysfs_dentry_ops; | 110 | dentry->d_op = &sysfs_dentry_ops; |
96 | } | 111 | } |
97 | 112 | ||
98 | 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; | ||
99 | } | 126 | } |
100 | 127 | ||
101 | static int init_dir(struct inode * inode) | 128 | static int init_dir(struct inode * inode) |
@@ -166,11 +193,11 @@ int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d) | |||
166 | 193 | ||
167 | /** | 194 | /** |
168 | * sysfs_create_dir - create a directory for an object. | 195 | * sysfs_create_dir - create a directory for an object. |
169 | * @parent: parent parent object. | ||
170 | * @kobj: object we're creating directory for. | 196 | * @kobj: object we're creating directory for. |
197 | * @shadow_parent: parent parent object. | ||
171 | */ | 198 | */ |
172 | 199 | ||
173 | int sysfs_create_dir(struct kobject * kobj) | 200 | int sysfs_create_dir(struct kobject * kobj, struct dentry *shadow_parent) |
174 | { | 201 | { |
175 | struct dentry * dentry = NULL; | 202 | struct dentry * dentry = NULL; |
176 | struct dentry * parent; | 203 | struct dentry * parent; |
@@ -178,7 +205,9 @@ int sysfs_create_dir(struct kobject * kobj) | |||
178 | 205 | ||
179 | BUG_ON(!kobj); | 206 | BUG_ON(!kobj); |
180 | 207 | ||
181 | if (kobj->parent) | 208 | if (shadow_parent) |
209 | parent = shadow_parent; | ||
210 | else if (kobj->parent) | ||
182 | parent = kobj->parent->dentry; | 211 | parent = kobj->parent->dentry; |
183 | else if (sysfs_mount && sysfs_mount->mnt_sb) | 212 | else if (sysfs_mount && sysfs_mount->mnt_sb) |
184 | parent = sysfs_mount->mnt_sb->s_root; | 213 | parent = sysfs_mount->mnt_sb->s_root; |
@@ -299,21 +328,12 @@ void sysfs_remove_subdir(struct dentry * d) | |||
299 | } | 328 | } |
300 | 329 | ||
301 | 330 | ||
302 | /** | 331 | static void __sysfs_remove_dir(struct dentry *dentry) |
303 | * sysfs_remove_dir - remove an object's directory. | ||
304 | * @kobj: object. | ||
305 | * | ||
306 | * The only thing special about this is that we remove any files in | ||
307 | * the directory before we remove the directory, and we've inlined | ||
308 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
309 | */ | ||
310 | |||
311 | void sysfs_remove_dir(struct kobject * kobj) | ||
312 | { | 332 | { |
313 | struct dentry * dentry = dget(kobj->dentry); | ||
314 | struct sysfs_dirent * parent_sd; | 333 | struct sysfs_dirent * parent_sd; |
315 | struct sysfs_dirent * sd, * tmp; | 334 | struct sysfs_dirent * sd, * tmp; |
316 | 335 | ||
336 | dget(dentry); | ||
317 | if (!dentry) | 337 | if (!dentry) |
318 | return; | 338 | return; |
319 | 339 | ||
@@ -334,32 +354,60 @@ void sysfs_remove_dir(struct kobject * kobj) | |||
334 | * Drop reference from dget() on entrance. | 354 | * Drop reference from dget() on entrance. |
335 | */ | 355 | */ |
336 | 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); | ||
337 | kobj->dentry = NULL; | 371 | kobj->dentry = NULL; |
338 | } | 372 | } |
339 | 373 | ||
340 | 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) | ||
341 | { | 376 | { |
342 | int error = 0; | 377 | int error = 0; |
343 | struct dentry * new_dentry, * parent; | 378 | struct dentry * new_dentry; |
344 | 379 | ||
345 | if (!strcmp(kobject_name(kobj), new_name)) | 380 | if (!new_parent) |
346 | return -EINVAL; | 381 | return -EFAULT; |
347 | |||
348 | if (!kobj->parent) | ||
349 | return -EINVAL; | ||
350 | 382 | ||
351 | down_write(&sysfs_rename_sem); | 383 | down_write(&sysfs_rename_sem); |
352 | parent = kobj->parent->dentry; | 384 | mutex_lock(&new_parent->d_inode->i_mutex); |
353 | 385 | ||
354 | mutex_lock(&parent->d_inode->i_mutex); | 386 | new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); |
355 | |||
356 | new_dentry = lookup_one_len(new_name, parent, strlen(new_name)); | ||
357 | if (!IS_ERR(new_dentry)) { | 387 | if (!IS_ERR(new_dentry)) { |
358 | 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) { | ||
359 | error = kobject_set_name(kobj, "%s", new_name); | 399 | error = kobject_set_name(kobj, "%s", new_name); |
360 | if (!error) { | 400 | if (!error) { |
401 | struct sysfs_dirent *sd, *parent_sd; | ||
402 | |||
361 | d_add(new_dentry, NULL); | 403 | d_add(new_dentry, NULL); |
362 | 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); | ||
363 | } | 411 | } |
364 | else | 412 | else |
365 | d_drop(new_dentry); | 413 | d_drop(new_dentry); |
@@ -367,7 +415,7 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | |||
367 | error = -EEXIST; | 415 | error = -EEXIST; |
368 | dput(new_dentry); | 416 | dput(new_dentry); |
369 | } | 417 | } |
370 | mutex_unlock(&parent->d_inode->i_mutex); | 418 | mutex_unlock(&new_parent->d_inode->i_mutex); |
371 | up_write(&sysfs_rename_sem); | 419 | up_write(&sysfs_rename_sem); |
372 | 420 | ||
373 | return error; | 421 | return error; |
@@ -546,6 +594,95 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
546 | return offset; | 594 | return offset; |
547 | } | 595 | } |
548 | 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 | |||
549 | const struct file_operations sysfs_dir_operations = { | 686 | const struct file_operations sysfs_dir_operations = { |
550 | .open = sysfs_dir_open, | 687 | .open = sysfs_dir_open, |
551 | .release = sysfs_dir_close, | 688 | .release = sysfs_dir_close, |