aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2007-01-24 14:35:52 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-07 13:37:14 -0500
commitb592fcfe7f06c15ec11774b5be7ce0de3aa86e73 (patch)
tree13f2cb344f8871edd30dc15007534405197d8480 /fs/sysfs
parent2f65168de7d68a5795e945e781d85b313bdc97b9 (diff)
sysfs: Shadow directory support
The problem. When implementing a network namespace I need to be able to have multiple network devices with the same name. Currently this is a problem for /sys/class/net/*. What I want is a separate /sys/class/net directory in sysfs for each network namespace, and I want to name each of them /sys/class/net. I looked and the VFS actually allows that. All that is needed is for /sys/class/net to implement a follow link method to redirect lookups to the real directory you want. Implementing a follow link method that is sensitive to the current network namespace turns out to be 3 lines of code so it looks like a clean approach. Modifying sysfs so it doesn't get in my was is a bit trickier. I am calling the concept of multiple directories all at the same path in the filesystem shadow directories. With the directory entry really at that location the shadow master. The following patch modifies sysfs so it can handle a directory structure slightly different from the kobject tree so I can implement the shadow directories for handling /sys/class/net/. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Cc: Maneesh Soni <maneesh@in.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs')
-rw-r--r--fs/sysfs/dir.c207
-rw-r--r--fs/sysfs/group.c1
-rw-r--r--fs/sysfs/inode.c10
-rw-r--r--fs/sysfs/mount.c2
-rw-r--r--fs/sysfs/sysfs.h5
5 files changed, 189 insertions, 36 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 */
36static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, 36static 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
54static 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
61static 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
81int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, 96static 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; 113out:
114 return sd;
115}
116
117int 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
101static int init_dir(struct inode * inode) 128static 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
173int sysfs_create_dir(struct kobject * kobj) 200int 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/** 331static 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
311void 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
368void 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
340int sysfs_rename_dir(struct kobject * kobj, const char *new_name) 374int 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
603int 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
636struct 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
664out:
665 return shadow;
666nomem:
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
681void sysfs_remove_shadow_dir(struct dentry *shadow)
682{
683 __sysfs_remove_dir(shadow);
684}
685
549const struct file_operations sysfs_dir_operations = { 686const 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,
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index 46a277b0838e..b20951c93761 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -13,6 +13,7 @@
13#include <linux/dcache.h> 13#include <linux/dcache.h>
14#include <linux/namei.h> 14#include <linux/namei.h>
15#include <linux/err.h> 15#include <linux/err.h>
16#include <linux/fs.h>
16#include <asm/semaphore.h> 17#include <asm/semaphore.h>
17#include "sysfs.h" 18#include "sysfs.h"
18 19
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index dbd820f9aeed..542d2bcc73df 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -33,6 +33,16 @@ static struct inode_operations sysfs_inode_operations ={
33 .setattr = sysfs_setattr, 33 .setattr = sysfs_setattr,
34}; 34};
35 35
36void sysfs_delete_inode(struct inode *inode)
37{
38 /* Free the shadowed directory inode operations */
39 if (sysfs_is_shadowed_inode(inode)) {
40 kfree(inode->i_op);
41 inode->i_op = NULL;
42 }
43 return generic_delete_inode(inode);
44}
45
36int sysfs_setattr(struct dentry * dentry, struct iattr * iattr) 46int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
37{ 47{
38 struct inode * inode = dentry->d_inode; 48 struct inode * inode = dentry->d_inode;
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index a1a58b97f322..f6a87a824883 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -23,7 +23,7 @@ static void sysfs_clear_inode(struct inode *inode);
23 23
24static struct super_operations sysfs_ops = { 24static struct super_operations sysfs_ops = {
25 .statfs = simple_statfs, 25 .statfs = simple_statfs,
26 .drop_inode = generic_delete_inode, 26 .drop_inode = sysfs_delete_inode,
27 .clear_inode = sysfs_clear_inode, 27 .clear_inode = sysfs_clear_inode,
28}; 28};
29 29
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 39c623fdc277..fe1cbfd208ed 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -2,6 +2,7 @@
2extern struct vfsmount * sysfs_mount; 2extern struct vfsmount * sysfs_mount;
3extern struct kmem_cache *sysfs_dir_cachep; 3extern struct kmem_cache *sysfs_dir_cachep;
4 4
5extern void sysfs_delete_inode(struct inode *inode);
5extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *); 6extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *);
6extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); 7extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *));
7 8
@@ -112,3 +113,7 @@ static inline void sysfs_put(struct sysfs_dirent * sd)
112 release_sysfs_dirent(sd); 113 release_sysfs_dirent(sd);
113} 114}
114 115
116static inline int sysfs_is_shadowed_inode(struct inode *inode)
117{
118 return S_ISDIR(inode->i_mode) && inode->i_op->follow_link;
119}