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