ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / sysfs / dir.c
1 /*
2  * dir.c - Operations for sysfs directories.
3  */
4
5 #undef DEBUG
6
7 #include <linux/fs.h>
8 #include <linux/mount.h>
9 #include <linux/module.h>
10 #include <linux/kobject.h>
11 #include "sysfs.h"
12
13 static int init_dir(struct inode * inode)
14 {
15         inode->i_op = &simple_dir_inode_operations;
16         inode->i_fop = &simple_dir_operations;
17
18         /* directory inodes start off with i_nlink == 2 (for "." entry) */
19         inode->i_nlink++;
20         return 0;
21 }
22
23
24 static int create_dir(struct kobject * k, struct dentry * p,
25                       const char * n, struct dentry ** d)
26 {
27         int error;
28
29         down(&p->d_inode->i_sem);
30         *d = sysfs_get_dentry(p,n);
31         if (!IS_ERR(*d)) {
32                 error = sysfs_create(*d,
33                                          S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO,
34                                          init_dir);
35                 if (!error) {
36                         (*d)->d_fsdata = k;
37                         p->d_inode->i_nlink++;
38                 }
39                 dput(*d);
40         } else
41                 error = PTR_ERR(*d);
42         up(&p->d_inode->i_sem);
43         return error;
44 }
45
46
47 int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d)
48 {
49         return create_dir(k,k->dentry,n,d);
50 }
51
52 /**
53  *      sysfs_create_dir - create a directory for an object.
54  *      @parent:        parent parent object.
55  *      @kobj:          object we're creating directory for. 
56  */
57
58 int sysfs_create_dir(struct kobject * kobj)
59 {
60         struct dentry * dentry = NULL;
61         struct dentry * parent;
62         int error = 0;
63
64         if (!kobj)
65                 return -EINVAL;
66
67         if (kobj->parent)
68                 parent = kobj->parent->dentry;
69         else if (sysfs_mount && sysfs_mount->mnt_sb)
70                 parent = sysfs_mount->mnt_sb->s_root;
71         else
72                 return -EFAULT;
73
74         error = create_dir(kobj,parent,kobject_name(kobj),&dentry);
75         if (!error)
76                 kobj->dentry = dentry;
77         return error;
78 }
79
80
81 static void remove_dir(struct dentry * d)
82 {
83         struct dentry * parent = dget(d->d_parent);
84         down(&parent->d_inode->i_sem);
85         d_delete(d);
86         if (d->d_inode)
87                 simple_rmdir(parent->d_inode,d);
88
89         pr_debug(" o %s removing done (%d)\n",d->d_name.name,
90                  atomic_read(&d->d_count));
91
92         up(&parent->d_inode->i_sem);
93         dput(parent);
94 }
95
96 void sysfs_remove_subdir(struct dentry * d)
97 {
98         remove_dir(d);
99 }
100
101
102 /**
103  *      sysfs_remove_dir - remove an object's directory.
104  *      @kobj:  object. 
105  *
106  *      The only thing special about this is that we remove any files in 
107  *      the directory before we remove the directory, and we've inlined
108  *      what used to be sysfs_rmdir() below, instead of calling separately.
109  */
110
111 void sysfs_remove_dir(struct kobject * kobj)
112 {
113         struct list_head * node;
114         struct dentry * dentry = dget(kobj->dentry);
115
116         if (!dentry)
117                 return;
118
119         pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
120         down(&dentry->d_inode->i_sem);
121
122         spin_lock(&dcache_lock);
123 restart:
124         node = dentry->d_subdirs.next;
125         while (node != &dentry->d_subdirs) {
126                 struct dentry * d = list_entry(node,struct dentry,d_child);
127
128                 node = node->next;
129                 pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count));
130                 if (!d_unhashed(d) && (d->d_inode)) {
131                         d = dget_locked(d);
132                         pr_debug("removing");
133
134                         /**
135                          * Unlink and unhash.
136                          */
137                         spin_unlock(&dcache_lock);
138                         d_delete(d);
139                         simple_unlink(dentry->d_inode,d);
140                         dput(d);
141                         pr_debug(" done\n");
142                         spin_lock(&dcache_lock);
143                         /* re-acquired dcache_lock, need to restart */
144                         goto restart;
145                 }
146         }
147         spin_unlock(&dcache_lock);
148         up(&dentry->d_inode->i_sem);
149
150         remove_dir(dentry);
151         /**
152          * Drop reference from dget() on entrance.
153          */
154         dput(dentry);
155 }
156
157 void sysfs_rename_dir(struct kobject * kobj, const char *new_name)
158 {
159         struct dentry * new_dentry, * parent;
160
161         if (!strcmp(kobject_name(kobj), new_name))
162                 return;
163
164         if (!kobj->parent)
165                 return;
166
167         parent = kobj->parent->dentry;
168
169         down(&parent->d_inode->i_sem);
170
171         new_dentry = sysfs_get_dentry(parent, new_name);
172         d_move(kobj->dentry, new_dentry);
173         kobject_set_name(kobj,new_name);
174         up(&parent->d_inode->i_sem);    
175 }
176
177 EXPORT_SYMBOL(sysfs_create_dir);
178 EXPORT_SYMBOL(sysfs_remove_dir);
179 EXPORT_SYMBOL(sysfs_rename_dir);
180