aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r--fs/sysfs/dir.c219
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
14DECLARE_RWSEM(sysfs_rename_sem); 15DECLARE_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 */
35static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, 36static 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
53static 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
60static 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
80int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, 95static 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; 112out:
113 return sd;
114}
115
116int 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
100static int init_dir(struct inode * inode) 127static 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
172int sysfs_create_dir(struct kobject * kobj) 199int 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
270struct inode_operations sysfs_dir_inode_operations = { 299const 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/** 330static 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
310void 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
367void 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
339int sysfs_rename_dir(struct kobject * kobj, const char *new_name) 373int 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
388again: 434again:
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
602int 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
635struct 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
663out:
664 return shadow;
665nomem:
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
680void sysfs_remove_shadow_dir(struct dentry *shadow)
681{
682 __sysfs_remove_dir(shadow);
683}
684
550const struct file_operations sysfs_dir_operations = { 685const 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,