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