blob: 7d240e6b7176432180585776ac0aaa354195643f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Tejun Heo6d66f5c2007-09-20 17:31:38 +09002 * fs/sysfs/dir.c - sysfs core and dir operation implementation
3 *
4 * Copyright (c) 2001-3 Patrick Mochel
5 * Copyright (c) 2007 SUSE Linux Products GmbH
6 * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
7 *
8 * This file is released under the GPLv2.
9 *
10 * Please see Documentation/filesystems/sysfs.txt for more information.
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 */
12
13#undef DEBUG
14
15#include <linux/fs.h>
16#include <linux/mount.h>
17#include <linux/module.h>
18#include <linux/kobject.h>
Christoph Hellwig5f45f1a2005-06-23 00:09:12 -070019#include <linux/namei.h>
Tejun Heo2b611bb2007-06-14 03:45:13 +090020#include <linux/idr.h>
Tejun Heo8619f972007-06-14 03:45:18 +090021#include <linux/completion.h>
Dave Young869512a2007-07-26 14:53:53 +000022#include <linux/mutex.h>
Robert P. J. Dayc6f87732008-03-13 22:41:52 -040023#include <linux/slab.h>
Eric W. Biederman4c3da222009-11-04 02:50:06 -080024#include <linux/security.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include "sysfs.h"
26
Tejun Heo3007e992007-06-14 04:27:23 +090027DEFINE_MUTEX(sysfs_mutex);
Roel Kluinf7a75f02007-10-16 23:30:25 -070028DEFINE_SPINLOCK(sysfs_assoc_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Roel Kluinf7a75f02007-10-16 23:30:25 -070030static DEFINE_SPINLOCK(sysfs_ino_lock);
Tejun Heo2b611bb2007-06-14 03:45:13 +090031static DEFINE_IDA(sysfs_ino_ida);
32
Tejun Heob6b4a432007-06-14 03:45:18 +090033/**
Tejun Heo0c73f182007-06-14 03:45:18 +090034 * sysfs_link_sibling - link sysfs_dirent into sibling list
35 * @sd: sysfs_dirent of interest
36 *
37 * Link @sd into its sibling list which starts from
Tejun Heobc747f32007-09-20 16:05:12 +090038 * sd->s_parent->s_dir.children.
Tejun Heo0c73f182007-06-14 03:45:18 +090039 *
40 * Locking:
Tejun Heo3007e992007-06-14 04:27:23 +090041 * mutex_lock(sysfs_mutex)
Tejun Heo0c73f182007-06-14 03:45:18 +090042 */
Tejun Heo41fc1c22007-08-02 21:38:03 +090043static void sysfs_link_sibling(struct sysfs_dirent *sd)
Tejun Heo0c73f182007-06-14 03:45:18 +090044{
45 struct sysfs_dirent *parent_sd = sd->s_parent;
Eric W. Biederman3efa65b2007-08-20 21:36:30 +090046 struct sysfs_dirent **pos;
Tejun Heo0c73f182007-06-14 03:45:18 +090047
48 BUG_ON(sd->s_sibling);
Eric W. Biederman3efa65b2007-08-20 21:36:30 +090049
Mikulas Patocka7f9838f2011-07-21 19:59:22 -040050 if (sysfs_type(sd) == SYSFS_DIR)
51 parent_sd->s_dir.subdirs++;
52
Eric W. Biederman3efa65b2007-08-20 21:36:30 +090053 /* Store directory entries in order by ino. This allows
54 * readdir to properly restart without having to add a
Tejun Heobc747f32007-09-20 16:05:12 +090055 * cursor into the s_dir.children list.
Eric W. Biederman3efa65b2007-08-20 21:36:30 +090056 */
Tejun Heobc747f32007-09-20 16:05:12 +090057 for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {
Eric W. Biederman3efa65b2007-08-20 21:36:30 +090058 if (sd->s_ino < (*pos)->s_ino)
59 break;
60 }
61 sd->s_sibling = *pos;
62 *pos = sd;
Tejun Heo0c73f182007-06-14 03:45:18 +090063}
64
65/**
66 * sysfs_unlink_sibling - unlink sysfs_dirent from sibling list
67 * @sd: sysfs_dirent of interest
68 *
69 * Unlink @sd from its sibling list which starts from
Tejun Heobc747f32007-09-20 16:05:12 +090070 * sd->s_parent->s_dir.children.
Tejun Heo0c73f182007-06-14 03:45:18 +090071 *
72 * Locking:
Tejun Heo3007e992007-06-14 04:27:23 +090073 * mutex_lock(sysfs_mutex)
Tejun Heo0c73f182007-06-14 03:45:18 +090074 */
Tejun Heo41fc1c22007-08-02 21:38:03 +090075static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
Tejun Heo0c73f182007-06-14 03:45:18 +090076{
77 struct sysfs_dirent **pos;
78
Mikulas Patocka7f9838f2011-07-21 19:59:22 -040079 if (sysfs_type(sd) == SYSFS_DIR)
80 sd->s_parent->s_dir.subdirs--;
81
Tejun Heobc747f32007-09-20 16:05:12 +090082 for (pos = &sd->s_parent->s_dir.children; *pos;
83 pos = &(*pos)->s_sibling) {
Tejun Heo0c73f182007-06-14 03:45:18 +090084 if (*pos == sd) {
85 *pos = sd->s_sibling;
86 sd->s_sibling = NULL;
87 break;
88 }
89 }
90}
91
92/**
Tejun Heob6b4a432007-06-14 03:45:18 +090093 * sysfs_get_active - get an active reference to sysfs_dirent
94 * @sd: sysfs_dirent to get an active reference to
95 *
96 * Get an active reference of @sd. This function is noop if @sd
97 * is NULL.
98 *
99 * RETURNS:
100 * Pointer to @sd on success, NULL on failure.
101 */
Eric W. Biedermane72ceb82010-02-11 15:18:38 -0800102struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
Tejun Heob6b4a432007-06-14 03:45:18 +0900103{
Tejun Heo8619f972007-06-14 03:45:18 +0900104 if (unlikely(!sd))
105 return NULL;
106
107 while (1) {
108 int v, t;
109
110 v = atomic_read(&sd->s_active);
111 if (unlikely(v < 0))
112 return NULL;
113
114 t = atomic_cmpxchg(&sd->s_active, v, v + 1);
Eric W. Biederman846f9972010-01-02 13:37:12 -0800115 if (likely(t == v)) {
116 rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
Tejun Heo8619f972007-06-14 03:45:18 +0900117 return sd;
Eric W. Biederman846f9972010-01-02 13:37:12 -0800118 }
Tejun Heo8619f972007-06-14 03:45:18 +0900119 if (t < 0)
120 return NULL;
121
122 cpu_relax();
Tejun Heob6b4a432007-06-14 03:45:18 +0900123 }
Tejun Heob6b4a432007-06-14 03:45:18 +0900124}
125
126/**
127 * sysfs_put_active - put an active reference to sysfs_dirent
128 * @sd: sysfs_dirent to put an active reference to
129 *
130 * Put an active reference to @sd. This function is noop if @sd
131 * is NULL.
132 */
Eric W. Biedermane72ceb82010-02-11 15:18:38 -0800133void sysfs_put_active(struct sysfs_dirent *sd)
Tejun Heob6b4a432007-06-14 03:45:18 +0900134{
Tejun Heo8619f972007-06-14 03:45:18 +0900135 struct completion *cmpl;
136 int v;
137
138 if (unlikely(!sd))
139 return;
140
Eric W. Biederman846f9972010-01-02 13:37:12 -0800141 rwsem_release(&sd->dep_map, 1, _RET_IP_);
Tejun Heo8619f972007-06-14 03:45:18 +0900142 v = atomic_dec_return(&sd->s_active);
143 if (likely(v != SD_DEACTIVATED_BIAS))
144 return;
145
146 /* atomic_dec_return() is a mb(), we'll always see the updated
Tejun Heo0c73f182007-06-14 03:45:18 +0900147 * sd->s_sibling.
Tejun Heo8619f972007-06-14 03:45:18 +0900148 */
Tejun Heo0c73f182007-06-14 03:45:18 +0900149 cmpl = (void *)sd->s_sibling;
Tejun Heo8619f972007-06-14 03:45:18 +0900150 complete(cmpl);
Tejun Heob6b4a432007-06-14 03:45:18 +0900151}
152
153/**
Tejun Heob6b4a432007-06-14 03:45:18 +0900154 * sysfs_deactivate - deactivate sysfs_dirent
155 * @sd: sysfs_dirent to deactivate
156 *
Tejun Heo8619f972007-06-14 03:45:18 +0900157 * Deny new active references and drain existing ones.
Tejun Heob6b4a432007-06-14 03:45:18 +0900158 */
Tejun Heofb6896d2007-06-14 04:27:24 +0900159static void sysfs_deactivate(struct sysfs_dirent *sd)
Tejun Heob6b4a432007-06-14 03:45:18 +0900160{
Tejun Heo8619f972007-06-14 03:45:18 +0900161 DECLARE_COMPLETION_ONSTACK(wait);
162 int v;
Tejun Heob6b4a432007-06-14 03:45:18 +0900163
Tejun Heo380e6fb2007-06-14 04:27:22 +0900164 BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED));
Eric W. Biedermana2db6842010-02-11 15:20:00 -0800165
166 if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF))
167 return;
168
Tejun Heo0c73f182007-06-14 03:45:18 +0900169 sd->s_sibling = (void *)&wait;
Tejun Heo8619f972007-06-14 03:45:18 +0900170
Eric W. Biederman846f9972010-01-02 13:37:12 -0800171 rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_);
Tejun Heo8619f972007-06-14 03:45:18 +0900172 /* atomic_add_return() is a mb(), put_active() will always see
Tejun Heo0c73f182007-06-14 03:45:18 +0900173 * the updated sd->s_sibling.
Tejun Heob6b4a432007-06-14 03:45:18 +0900174 */
Tejun Heo8619f972007-06-14 03:45:18 +0900175 v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active);
176
Eric W. Biederman846f9972010-01-02 13:37:12 -0800177 if (v != SD_DEACTIVATED_BIAS) {
178 lock_contended(&sd->dep_map, _RET_IP_);
Tejun Heo8619f972007-06-14 03:45:18 +0900179 wait_for_completion(&wait);
Eric W. Biederman846f9972010-01-02 13:37:12 -0800180 }
Tejun Heo8619f972007-06-14 03:45:18 +0900181
Tejun Heo0c73f182007-06-14 03:45:18 +0900182 sd->s_sibling = NULL;
Eric W. Biederman846f9972010-01-02 13:37:12 -0800183
184 lock_acquired(&sd->dep_map, _RET_IP_);
185 rwsem_release(&sd->dep_map, 1, _RET_IP_);
Tejun Heob6b4a432007-06-14 03:45:18 +0900186}
187
Tejun Heo42b37df2007-06-14 03:45:17 +0900188static int sysfs_alloc_ino(ino_t *pino)
Tejun Heo2b611bb2007-06-14 03:45:13 +0900189{
190 int ino, rc;
191
192 retry:
193 spin_lock(&sysfs_ino_lock);
194 rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino);
195 spin_unlock(&sysfs_ino_lock);
196
197 if (rc == -EAGAIN) {
198 if (ida_pre_get(&sysfs_ino_ida, GFP_KERNEL))
199 goto retry;
200 rc = -ENOMEM;
201 }
202
203 *pino = ino;
204 return rc;
205}
206
207static void sysfs_free_ino(ino_t ino)
208{
209 spin_lock(&sysfs_ino_lock);
210 ida_remove(&sysfs_ino_ida, ino);
211 spin_unlock(&sysfs_ino_lock);
212}
213
Tejun Heofa7f9122007-06-14 03:45:13 +0900214void release_sysfs_dirent(struct sysfs_dirent * sd)
215{
Tejun Heo13b30862007-06-14 03:45:14 +0900216 struct sysfs_dirent *parent_sd;
217
218 repeat:
Tejun Heo3007e992007-06-14 04:27:23 +0900219 /* Moving/renaming is always done while holding reference.
220 * sd->s_parent won't change beneath us.
221 */
Tejun Heo13b30862007-06-14 03:45:14 +0900222 parent_sd = sd->s_parent;
223
Tejun Heob402d722007-06-14 04:27:21 +0900224 if (sysfs_type(sd) == SYSFS_KOBJ_LINK)
Tejun Heob1fc3d62007-09-20 16:05:11 +0900225 sysfs_put(sd->s_symlink.target_sd);
Tejun Heob402d722007-06-14 04:27:21 +0900226 if (sysfs_type(sd) & SYSFS_COPY_NAME)
Tejun Heo0c096b52007-06-14 03:45:15 +0900227 kfree(sd->s_name);
Eric W. Biederman4c3da222009-11-04 02:50:06 -0800228 if (sd->s_iattr && sd->s_iattr->ia_secdata)
229 security_release_secctx(sd->s_iattr->ia_secdata,
230 sd->s_iattr->ia_secdata_len);
Tejun Heofa7f9122007-06-14 03:45:13 +0900231 kfree(sd->s_iattr);
Tejun Heo2b611bb2007-06-14 03:45:13 +0900232 sysfs_free_ino(sd->s_ino);
Tejun Heofa7f9122007-06-14 03:45:13 +0900233 kmem_cache_free(sysfs_dir_cachep, sd);
Tejun Heo13b30862007-06-14 03:45:14 +0900234
235 sd = parent_sd;
236 if (sd && atomic_dec_and_test(&sd->s_count))
237 goto repeat;
Tejun Heofa7f9122007-06-14 03:45:13 +0900238}
239
Nick Pigginfe15ce42011-01-07 17:49:23 +1100240static int sysfs_dentry_delete(const struct dentry *dentry)
Eric W. Biedermane8f077c2009-11-07 23:27:01 -0800241{
242 struct sysfs_dirent *sd = dentry->d_fsdata;
243 return !!(sd->s_flags & SYSFS_FLAG_REMOVED);
244}
245
246static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
247{
Nick Piggin34286d62011-01-07 17:49:57 +1100248 struct sysfs_dirent *sd;
Eric W. Biedermane8f077c2009-11-07 23:27:01 -0800249 int is_dir;
250
Nick Piggin34286d62011-01-07 17:49:57 +1100251 if (nd->flags & LOOKUP_RCU)
252 return -ECHILD;
253
254 sd = dentry->d_fsdata;
Eric W. Biedermane8f077c2009-11-07 23:27:01 -0800255 mutex_lock(&sysfs_mutex);
256
257 /* The sysfs dirent has been deleted */
258 if (sd->s_flags & SYSFS_FLAG_REMOVED)
259 goto out_bad;
260
Eric W. Biederman832b6af2009-11-20 16:08:56 -0800261 /* The sysfs dirent has been moved? */
262 if (dentry->d_parent->d_fsdata != sd->s_parent)
263 goto out_bad;
264
265 /* The sysfs dirent has been renamed */
266 if (strcmp(dentry->d_name.name, sd->s_name) != 0)
267 goto out_bad;
268
Eric W. Biedermane8f077c2009-11-07 23:27:01 -0800269 mutex_unlock(&sysfs_mutex);
270out_valid:
271 return 1;
272out_bad:
273 /* Remove the dentry from the dcache hashes.
274 * If this is a deleted dentry we use d_drop instead of d_delete
275 * so sysfs doesn't need to cope with negative dentries.
Eric W. Biederman832b6af2009-11-20 16:08:56 -0800276 *
277 * If this is a dentry that has simply been renamed we
278 * use d_drop to remove it from the dcache lookup on its
279 * old parent. If this dentry persists later when a lookup
280 * is performed at its new name the dentry will be readded
281 * to the dcache hashes.
Eric W. Biedermane8f077c2009-11-07 23:27:01 -0800282 */
283 is_dir = (sysfs_type(sd) == SYSFS_DIR);
284 mutex_unlock(&sysfs_mutex);
285 if (is_dir) {
286 /* If we have submounts we must allow the vfs caches
287 * to lie about the state of the filesystem to prevent
288 * leaks and other nasty things.
289 */
290 if (have_submounts(dentry))
291 goto out_valid;
292 shrink_dcache_parent(dentry);
293 }
294 d_drop(dentry);
295 return 0;
296}
297
Eric W. Biederman28a027c2009-11-07 23:27:00 -0800298static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
300 struct sysfs_dirent * sd = dentry->d_fsdata;
301
Eric W. Biederman5a26b792007-08-20 21:36:30 +0900302 sysfs_put(sd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 iput(inode);
304}
305
Al Viroee1ec322009-02-20 06:01:46 +0000306static const struct dentry_operations sysfs_dentry_ops = {
Eric W. Biedermane8f077c2009-11-07 23:27:01 -0800307 .d_revalidate = sysfs_dentry_revalidate,
308 .d_delete = sysfs_dentry_delete,
Eric W. Biederman28a027c2009-11-07 23:27:00 -0800309 .d_iput = sysfs_dentry_iput,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310};
311
Tejun Heo3e519032007-06-14 03:45:15 +0900312struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
Tejun Heo0c096b52007-06-14 03:45:15 +0900314 char *dup_name = NULL;
Akinobu Mita01da2422007-07-14 11:03:35 +0900315 struct sysfs_dirent *sd;
Tejun Heo0c096b52007-06-14 03:45:15 +0900316
317 if (type & SYSFS_COPY_NAME) {
318 name = dup_name = kstrdup(name, GFP_KERNEL);
319 if (!name)
Akinobu Mita01da2422007-07-14 11:03:35 +0900320 return NULL;
Tejun Heo0c096b52007-06-14 03:45:15 +0900321 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
Robert P. J. Dayc3762222007-02-10 01:45:03 -0800323 sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 if (!sd)
Akinobu Mita01da2422007-07-14 11:03:35 +0900325 goto err_out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
Tejun Heo0c096b52007-06-14 03:45:15 +0900327 if (sysfs_alloc_ino(&sd->s_ino))
Akinobu Mita01da2422007-07-14 11:03:35 +0900328 goto err_out2;
Tejun Heo2b611bb2007-06-14 03:45:13 +0900329
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 atomic_set(&sd->s_count, 1);
Tejun Heo8619f972007-06-14 03:45:18 +0900331 atomic_set(&sd->s_active, 0);
Tejun Heoa26cd722007-06-14 03:45:14 +0900332
Tejun Heo0c096b52007-06-14 03:45:15 +0900333 sd->s_name = name;
Tejun Heoa26cd722007-06-14 03:45:14 +0900334 sd->s_mode = mode;
Tejun Heob402d722007-06-14 04:27:21 +0900335 sd->s_flags = type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337 return sd;
Tejun Heo0c096b52007-06-14 03:45:15 +0900338
Akinobu Mita01da2422007-07-14 11:03:35 +0900339 err_out2:
Tejun Heo0c096b52007-06-14 03:45:15 +0900340 kmem_cache_free(sysfs_dir_cachep, sd);
Akinobu Mita01da2422007-07-14 11:03:35 +0900341 err_out1:
342 kfree(dup_name);
Tejun Heo0c096b52007-06-14 03:45:15 +0900343 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344}
345
Tejun Heo3007e992007-06-14 04:27:23 +0900346/**
Tejun Heofb6896d2007-06-14 04:27:24 +0900347 * sysfs_addrm_start - prepare for sysfs_dirent add/remove
348 * @acxt: pointer to sysfs_addrm_cxt to be used
349 * @parent_sd: parent sysfs_dirent
Tejun Heo3007e992007-06-14 04:27:23 +0900350 *
Tejun Heofb6896d2007-06-14 04:27:24 +0900351 * This function is called when the caller is about to add or
352 * remove sysfs_dirent under @parent_sd. This function acquires
Eric W. Biedermana16bbc32009-11-20 16:08:55 -0800353 * sysfs_mutex. @acxt is used to keep and pass context to
Tejun Heofb6896d2007-06-14 04:27:24 +0900354 * other addrm functions.
Tejun Heo3007e992007-06-14 04:27:23 +0900355 *
356 * LOCKING:
Tejun Heofb6896d2007-06-14 04:27:24 +0900357 * Kernel thread context (may sleep). sysfs_mutex is locked on
Eric W. Biedermana16bbc32009-11-20 16:08:55 -0800358 * return.
Tejun Heo3007e992007-06-14 04:27:23 +0900359 */
Tejun Heofb6896d2007-06-14 04:27:24 +0900360void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
361 struct sysfs_dirent *parent_sd)
Eric W. Biedermanb592fcf2007-01-24 12:35:52 -0700362{
Tejun Heofb6896d2007-06-14 04:27:24 +0900363 memset(acxt, 0, sizeof(*acxt));
364 acxt->parent_sd = parent_sd;
365
Tejun Heofb6896d2007-06-14 04:27:24 +0900366 mutex_lock(&sysfs_mutex);
Tejun Heofb6896d2007-06-14 04:27:24 +0900367}
368
369/**
Cornelia Huck36ce6da2008-06-10 11:09:08 +0200370 * __sysfs_add_one - add sysfs_dirent to parent without warning
371 * @acxt: addrm context to use
372 * @sd: sysfs_dirent to be added
373 *
374 * Get @acxt->parent_sd and set sd->s_parent to it and increment
375 * nlink of parent inode if @sd is a directory and link into the
376 * children list of the parent.
377 *
378 * This function should be called between calls to
379 * sysfs_addrm_start() and sysfs_addrm_finish() and should be
380 * passed the same @acxt as passed to sysfs_addrm_start().
381 *
382 * LOCKING:
383 * Determined by sysfs_addrm_start().
384 *
385 * RETURNS:
386 * 0 on success, -EEXIST if entry with the given name already
387 * exists.
388 */
389int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
390{
Eric W. Biederman6b0bfe92009-11-20 16:08:51 -0800391 struct sysfs_inode_attrs *ps_iattr;
392
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700393 if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name))
Cornelia Huck36ce6da2008-06-10 11:09:08 +0200394 return -EEXIST;
395
396 sd->s_parent = sysfs_get(acxt->parent_sd);
397
Cornelia Huck36ce6da2008-06-10 11:09:08 +0200398 sysfs_link_sibling(sd);
399
Eric W. Biederman6b0bfe92009-11-20 16:08:51 -0800400 /* Update timestamps on the parent */
401 ps_iattr = acxt->parent_sd->s_iattr;
402 if (ps_iattr) {
403 struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
404 ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
405 }
406
Cornelia Huck36ce6da2008-06-10 11:09:08 +0200407 return 0;
408}
409
410/**
Alex Chiang425cb022009-02-12 10:56:59 -0700411 * sysfs_pathname - return full path to sysfs dirent
412 * @sd: sysfs_dirent whose path we want
413 * @path: caller allocated buffer
414 *
415 * Gives the name "/" to the sysfs_root entry; any path returned
416 * is relative to wherever sysfs is mounted.
417 *
418 * XXX: does no error checking on @path size
419 */
420static char *sysfs_pathname(struct sysfs_dirent *sd, char *path)
421{
422 if (sd->s_parent) {
423 sysfs_pathname(sd->s_parent, path);
424 strcat(path, "/");
425 }
426 strcat(path, sd->s_name);
427 return path;
428}
429
430/**
Tejun Heofb6896d2007-06-14 04:27:24 +0900431 * sysfs_add_one - add sysfs_dirent to parent
432 * @acxt: addrm context to use
433 * @sd: sysfs_dirent to be added
434 *
435 * Get @acxt->parent_sd and set sd->s_parent to it and increment
Tejun Heo181b2e42007-09-20 16:05:09 +0900436 * nlink of parent inode if @sd is a directory and link into the
437 * children list of the parent.
Tejun Heofb6896d2007-06-14 04:27:24 +0900438 *
439 * This function should be called between calls to
440 * sysfs_addrm_start() and sysfs_addrm_finish() and should be
441 * passed the same @acxt as passed to sysfs_addrm_start().
442 *
443 * LOCKING:
444 * Determined by sysfs_addrm_start().
Tejun Heo23dc2792007-08-02 21:38:03 +0900445 *
446 * RETURNS:
447 * 0 on success, -EEXIST if entry with the given name already
448 * exists.
Tejun Heofb6896d2007-06-14 04:27:24 +0900449 */
Tejun Heo23dc2792007-08-02 21:38:03 +0900450int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
Tejun Heofb6896d2007-06-14 04:27:24 +0900451{
Cornelia Huck36ce6da2008-06-10 11:09:08 +0200452 int ret;
Tejun Heo23dc2792007-08-02 21:38:03 +0900453
Cornelia Huck36ce6da2008-06-10 11:09:08 +0200454 ret = __sysfs_add_one(acxt, sd);
Alex Chiang425cb022009-02-12 10:56:59 -0700455 if (ret == -EEXIST) {
456 char *path = kzalloc(PATH_MAX, GFP_KERNEL);
457 WARN(1, KERN_WARNING
458 "sysfs: cannot create duplicate filename '%s'\n",
459 (path == NULL) ? sd->s_name :
460 strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"),
461 sd->s_name));
462 kfree(path);
463 }
464
Cornelia Huck36ce6da2008-06-10 11:09:08 +0200465 return ret;
Tejun Heofb6896d2007-06-14 04:27:24 +0900466}
467
468/**
469 * sysfs_remove_one - remove sysfs_dirent from parent
470 * @acxt: addrm context to use
Jean Delvare9fd5b1c2008-01-08 18:11:24 +0100471 * @sd: sysfs_dirent to be removed
Tejun Heofb6896d2007-06-14 04:27:24 +0900472 *
473 * Mark @sd removed and drop nlink of parent inode if @sd is a
Tejun Heo181b2e42007-09-20 16:05:09 +0900474 * directory. @sd is unlinked from the children list.
Tejun Heofb6896d2007-06-14 04:27:24 +0900475 *
476 * This function should be called between calls to
477 * sysfs_addrm_start() and sysfs_addrm_finish() and should be
478 * passed the same @acxt as passed to sysfs_addrm_start().
479 *
480 * LOCKING:
481 * Determined by sysfs_addrm_start().
482 */
483void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
484{
Eric W. Biederman6b0bfe92009-11-20 16:08:51 -0800485 struct sysfs_inode_attrs *ps_iattr;
486
Tejun Heo41fc1c22007-08-02 21:38:03 +0900487 BUG_ON(sd->s_flags & SYSFS_FLAG_REMOVED);
488
489 sysfs_unlink_sibling(sd);
Tejun Heofb6896d2007-06-14 04:27:24 +0900490
Eric W. Biederman6b0bfe92009-11-20 16:08:51 -0800491 /* Update timestamps on the parent */
492 ps_iattr = acxt->parent_sd->s_iattr;
493 if (ps_iattr) {
494 struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
495 ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
496 }
497
Tejun Heofb6896d2007-06-14 04:27:24 +0900498 sd->s_flags |= SYSFS_FLAG_REMOVED;
499 sd->s_sibling = acxt->removed;
500 acxt->removed = sd;
Tejun Heoa0edd7c2007-06-14 04:27:24 +0900501}
502
503/**
Tejun Heofb6896d2007-06-14 04:27:24 +0900504 * sysfs_addrm_finish - finish up sysfs_dirent add/remove
505 * @acxt: addrm context to finish up
506 *
507 * Finish up sysfs_dirent add/remove. Resources acquired by
508 * sysfs_addrm_start() are released and removed sysfs_dirents are
Eric W. Biedermana16bbc32009-11-20 16:08:55 -0800509 * cleaned up.
Tejun Heofb6896d2007-06-14 04:27:24 +0900510 *
511 * LOCKING:
Eric W. Biedermana16bbc32009-11-20 16:08:55 -0800512 * sysfs_mutex is released.
Tejun Heofb6896d2007-06-14 04:27:24 +0900513 */
Tejun Heo990e53f2007-08-02 21:38:03 +0900514void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
Tejun Heofb6896d2007-06-14 04:27:24 +0900515{
516 /* release resources acquired by sysfs_addrm_start() */
517 mutex_unlock(&sysfs_mutex);
Tejun Heofb6896d2007-06-14 04:27:24 +0900518
519 /* kill removed sysfs_dirents */
520 while (acxt->removed) {
521 struct sysfs_dirent *sd = acxt->removed;
522
523 acxt->removed = sd->s_sibling;
524 sd->s_sibling = NULL;
525
Tejun Heofb6896d2007-06-14 04:27:24 +0900526 sysfs_deactivate(sd);
Eric W. Biedermane0edd3c2009-03-04 11:57:20 -0800527 unmap_bin_file(sd);
Tejun Heofb6896d2007-06-14 04:27:24 +0900528 sysfs_put(sd);
529 }
Eric W. Biedermanb592fcf2007-01-24 12:35:52 -0700530}
531
Tejun Heof0b0af42007-06-14 04:27:22 +0900532/**
533 * sysfs_find_dirent - find sysfs_dirent with the given name
534 * @parent_sd: sysfs_dirent to search under
535 * @name: name to look for
Maneesh Sonic5168652006-03-09 19:40:14 +0530536 *
Tejun Heof0b0af42007-06-14 04:27:22 +0900537 * Look for sysfs_dirent with name @name under @parent_sd.
Maneesh Sonic5168652006-03-09 19:40:14 +0530538 *
Tejun Heof0b0af42007-06-14 04:27:22 +0900539 * LOCKING:
Tejun Heo3007e992007-06-14 04:27:23 +0900540 * mutex_lock(sysfs_mutex)
Tejun Heof0b0af42007-06-14 04:27:22 +0900541 *
542 * RETURNS:
543 * Pointer to sysfs_dirent if found, NULL if not.
Maneesh Sonic5168652006-03-09 19:40:14 +0530544 */
Tejun Heof0b0af42007-06-14 04:27:22 +0900545struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700546 const void *ns,
Tejun Heof0b0af42007-06-14 04:27:22 +0900547 const unsigned char *name)
Maneesh Sonic5168652006-03-09 19:40:14 +0530548{
Tejun Heof0b0af42007-06-14 04:27:22 +0900549 struct sysfs_dirent *sd;
Maneesh Sonic5168652006-03-09 19:40:14 +0530550
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700551 for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) {
Eric W. Biedermanaf10ec72010-03-30 11:31:27 -0700552 if (ns && sd->s_ns && (sd->s_ns != ns))
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700553 continue;
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900554 if (!strcmp(sd->s_name, name))
Tejun Heof0b0af42007-06-14 04:27:22 +0900555 return sd;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700556 }
Tejun Heof0b0af42007-06-14 04:27:22 +0900557 return NULL;
558}
Maneesh Sonic5168652006-03-09 19:40:14 +0530559
Tejun Heof0b0af42007-06-14 04:27:22 +0900560/**
561 * sysfs_get_dirent - find and get sysfs_dirent with the given name
562 * @parent_sd: sysfs_dirent to search under
563 * @name: name to look for
564 *
565 * Look for sysfs_dirent with name @name under @parent_sd and get
566 * it if found.
567 *
568 * LOCKING:
Tejun Heo3007e992007-06-14 04:27:23 +0900569 * Kernel thread context (may sleep). Grabs sysfs_mutex.
Tejun Heof0b0af42007-06-14 04:27:22 +0900570 *
571 * RETURNS:
572 * Pointer to sysfs_dirent if found, NULL if not.
573 */
574struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700575 const void *ns,
Tejun Heof0b0af42007-06-14 04:27:22 +0900576 const unsigned char *name)
577{
578 struct sysfs_dirent *sd;
579
Tejun Heo3007e992007-06-14 04:27:23 +0900580 mutex_lock(&sysfs_mutex);
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700581 sd = sysfs_find_dirent(parent_sd, ns, name);
Tejun Heof0b0af42007-06-14 04:27:22 +0900582 sysfs_get(sd);
Tejun Heo3007e992007-06-14 04:27:23 +0900583 mutex_unlock(&sysfs_mutex);
Tejun Heof0b0af42007-06-14 04:27:22 +0900584
585 return sd;
Maneesh Sonic5168652006-03-09 19:40:14 +0530586}
Neil Brownf1282c82008-07-16 08:58:04 +1000587EXPORT_SYMBOL_GPL(sysfs_get_dirent);
Maneesh Sonic5168652006-03-09 19:40:14 +0530588
Tejun Heo608e2662007-06-14 04:27:22 +0900589static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700590 enum kobj_ns_type type, const void *ns, const char *name,
591 struct sysfs_dirent **p_sd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
Tejun Heo51225032007-06-14 04:27:25 +0900594 struct sysfs_addrm_cxt acxt;
Tejun Heodfeb9fb2007-06-14 03:45:14 +0900595 struct sysfs_dirent *sd;
Tejun Heo23dc2792007-08-02 21:38:03 +0900596 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Tejun Heofc9f54b2007-06-14 03:45:17 +0900598 /* allocate */
Tejun Heo3e519032007-06-14 03:45:15 +0900599 sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
Tejun Heoa26cd722007-06-14 03:45:14 +0900600 if (!sd)
Tejun Heo51225032007-06-14 04:27:25 +0900601 return -ENOMEM;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700602
603 sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);
604 sd->s_ns = ns;
Tejun Heob1fc3d62007-09-20 16:05:11 +0900605 sd->s_dir.kobj = kobj;
Tejun Heodfeb9fb2007-06-14 03:45:14 +0900606
Tejun Heo51225032007-06-14 04:27:25 +0900607 /* link in */
608 sysfs_addrm_start(&acxt, parent_sd);
Tejun Heo23dc2792007-08-02 21:38:03 +0900609 rc = sysfs_add_one(&acxt, sd);
610 sysfs_addrm_finish(&acxt);
Tejun Heo967e35d2007-07-18 16:38:11 +0900611
Tejun Heo23dc2792007-08-02 21:38:03 +0900612 if (rc == 0)
613 *p_sd = sd;
614 else
Tejun Heo967e35d2007-07-18 16:38:11 +0900615 sysfs_put(sd);
Tejun Heofc9f54b2007-06-14 03:45:17 +0900616
Tejun Heo23dc2792007-08-02 21:38:03 +0900617 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618}
619
Tejun Heo608e2662007-06-14 04:27:22 +0900620int sysfs_create_subdir(struct kobject *kobj, const char *name,
621 struct sysfs_dirent **p_sd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622{
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700623 return create_dir(kobj, kobj->sd,
624 KOBJ_NS_TYPE_NONE, NULL, name, p_sd);
625}
626
Serge E. Hallynbe867b12010-05-03 16:23:15 -0500627/**
628 * sysfs_read_ns_type: return associated ns_type
629 * @kobj: the kobject being queried
630 *
631 * Each kobject can be tagged with exactly one namespace type
632 * (i.e. network or user). Return the ns_type associated with
633 * this object if any
634 */
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700635static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj)
636{
637 const struct kobj_ns_type_operations *ops;
638 enum kobj_ns_type type;
639
640 ops = kobj_child_ns_ops(kobj);
641 if (!ops)
642 return KOBJ_NS_TYPE_NONE;
643
644 type = ops->type;
645 BUG_ON(type <= KOBJ_NS_TYPE_NONE);
646 BUG_ON(type >= KOBJ_NS_TYPES);
647 BUG_ON(!kobj_ns_type_registered(type));
648
649 return type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650}
651
652/**
653 * sysfs_create_dir - create a directory for an object.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 * @kobj: object we're creating directory for.
655 */
Eric W. Biederman90bc6132007-07-31 19:15:08 +0900656int sysfs_create_dir(struct kobject * kobj)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700658 enum kobj_ns_type type;
Tejun Heo608e2662007-06-14 04:27:22 +0900659 struct sysfs_dirent *parent_sd, *sd;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700660 const void *ns = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 int error = 0;
662
663 BUG_ON(!kobj);
664
Eric W. Biederman90bc6132007-07-31 19:15:08 +0900665 if (kobj->parent)
Tejun Heo608e2662007-06-14 04:27:22 +0900666 parent_sd = kobj->parent->sd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 else
Eric W. Biederman7d0c7d62007-08-20 21:36:30 +0900668 parent_sd = &sysfs_root;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700670 if (sysfs_ns_type(parent_sd))
671 ns = kobj->ktype->namespace(kobj);
672 type = sysfs_read_ns_type(kobj);
673
674 error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 if (!error)
Tejun Heo608e2662007-06-14 04:27:22 +0900676 kobj->sd = sd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return error;
678}
679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
681 struct nameidata *nd)
682{
Tejun Heo6cb52142007-07-31 19:15:08 +0900683 struct dentry *ret = NULL;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700684 struct dentry *parent = dentry->d_parent;
685 struct sysfs_dirent *parent_sd = parent->d_fsdata;
Tejun Heoa7a04752007-08-02 21:38:02 +0900686 struct sysfs_dirent *sd;
Tejun Heofc9f54b2007-06-14 03:45:17 +0900687 struct inode *inode;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700688 enum kobj_ns_type type;
689 const void *ns;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Tejun Heo6cb52142007-07-31 19:15:08 +0900691 mutex_lock(&sysfs_mutex);
692
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700693 type = sysfs_ns_type(parent_sd);
694 ns = sysfs_info(dir->i_sb)->ns[type];
695
696 sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Tejun Heofc9f54b2007-06-14 03:45:17 +0900698 /* no such entry */
Tejun Heoe49452c2008-01-16 12:06:14 +0900699 if (!sd) {
700 ret = ERR_PTR(-ENOENT);
Tejun Heo6cb52142007-07-31 19:15:08 +0900701 goto out_unlock;
Tejun Heoe49452c2008-01-16 12:06:14 +0900702 }
Tejun Heofc9f54b2007-06-14 03:45:17 +0900703
704 /* attach dentry and inode */
Eric W. Biedermanfac26222010-02-12 19:22:27 -0800705 inode = sysfs_get_inode(dir->i_sb, sd);
Tejun Heo6cb52142007-07-31 19:15:08 +0900706 if (!inode) {
707 ret = ERR_PTR(-ENOMEM);
708 goto out_unlock;
709 }
Tejun Heo3007e992007-06-14 04:27:23 +0900710
Tejun Heod6b4fd22007-09-20 16:05:11 +0900711 /* instantiate and hash dentry */
Eric W. Biederman832b6af2009-11-20 16:08:56 -0800712 ret = d_find_alias(inode);
713 if (!ret) {
Nick Pigginfb045ad2011-01-07 17:49:55 +1100714 d_set_d_op(dentry, &sysfs_dentry_ops);
Eric W. Biederman832b6af2009-11-20 16:08:56 -0800715 dentry->d_fsdata = sysfs_get(sd);
716 d_add(dentry, inode);
717 } else {
718 d_move(ret, dentry);
719 iput(inode);
720 }
Tejun Heofc9f54b2007-06-14 03:45:17 +0900721
Tejun Heo6cb52142007-07-31 19:15:08 +0900722 out_unlock:
Tejun Heo3007e992007-06-14 04:27:23 +0900723 mutex_unlock(&sysfs_mutex);
Tejun Heo6cb52142007-07-31 19:15:08 +0900724 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725}
726
Arjan van de Venc5ef1c42007-02-12 00:55:40 -0800727const struct inode_operations sysfs_dir_inode_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 .lookup = sysfs_lookup,
Eric W. Biedermane61ab4a2009-11-20 16:08:53 -0800729 .permission = sysfs_permission,
Maneesh Soni988d1862005-05-31 10:39:14 +0530730 .setattr = sysfs_setattr,
Eric W. Biedermane61ab4a2009-11-20 16:08:53 -0800731 .getattr = sysfs_getattr,
David P. Quigleyddd29ec2009-09-09 14:25:37 -0400732 .setxattr = sysfs_setxattr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733};
734
Tejun Heo608e2662007-06-14 04:27:22 +0900735static void remove_dir(struct sysfs_dirent *sd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
Tejun Heofb6896d2007-06-14 04:27:24 +0900737 struct sysfs_addrm_cxt acxt;
738
739 sysfs_addrm_start(&acxt, sd->s_parent);
Tejun Heofb6896d2007-06-14 04:27:24 +0900740 sysfs_remove_one(&acxt, sd);
741 sysfs_addrm_finish(&acxt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742}
743
Tejun Heo608e2662007-06-14 04:27:22 +0900744void sysfs_remove_subdir(struct sysfs_dirent *sd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745{
Tejun Heo608e2662007-06-14 04:27:22 +0900746 remove_dir(sd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747}
748
749
Tejun Heo608e2662007-06-14 04:27:22 +0900750static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
Tejun Heofb6896d2007-06-14 04:27:24 +0900752 struct sysfs_addrm_cxt acxt;
Tejun Heo0c73f182007-06-14 03:45:18 +0900753 struct sysfs_dirent **pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Tejun Heo608e2662007-06-14 04:27:22 +0900755 if (!dir_sd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 return;
757
Tejun Heo608e2662007-06-14 04:27:22 +0900758 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
Tejun Heofb6896d2007-06-14 04:27:24 +0900759 sysfs_addrm_start(&acxt, dir_sd);
Tejun Heobc747f32007-09-20 16:05:12 +0900760 pos = &dir_sd->s_dir.children;
Tejun Heo0c73f182007-06-14 03:45:18 +0900761 while (*pos) {
762 struct sysfs_dirent *sd = *pos;
763
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900764 if (sysfs_type(sd) != SYSFS_DIR)
Tejun Heofb6896d2007-06-14 04:27:24 +0900765 sysfs_remove_one(&acxt, sd);
Tejun Heo41fc1c22007-08-02 21:38:03 +0900766 else
Tejun Heo0c73f182007-06-14 03:45:18 +0900767 pos = &(*pos)->s_sibling;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 }
Tejun Heofb6896d2007-06-14 04:27:24 +0900769 sysfs_addrm_finish(&acxt);
Tejun Heo0ab66082007-06-14 03:45:16 +0900770
Tejun Heo608e2662007-06-14 04:27:22 +0900771 remove_dir(dir_sd);
Eric W. Biedermanb592fcf2007-01-24 12:35:52 -0700772}
773
774/**
775 * sysfs_remove_dir - remove an object's directory.
776 * @kobj: object.
777 *
778 * The only thing special about this is that we remove any files in
779 * the directory before we remove the directory, and we've inlined
780 * what used to be sysfs_rmdir() below, instead of calling separately.
781 */
782
783void sysfs_remove_dir(struct kobject * kobj)
784{
Tejun Heo608e2662007-06-14 04:27:22 +0900785 struct sysfs_dirent *sd = kobj->sd;
Tejun Heoaecdced2007-06-14 03:45:15 +0900786
Tejun Heo5f995322007-06-14 04:27:23 +0900787 spin_lock(&sysfs_assoc_lock);
Tejun Heo608e2662007-06-14 04:27:22 +0900788 kobj->sd = NULL;
Tejun Heo5f995322007-06-14 04:27:23 +0900789 spin_unlock(&sysfs_assoc_lock);
Tejun Heoaecdced2007-06-14 03:45:15 +0900790
Tejun Heo608e2662007-06-14 04:27:22 +0900791 __sysfs_remove_dir(sd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792}
793
Eric W. Biedermanca1bab32009-11-20 16:08:57 -0800794int sysfs_rename(struct sysfs_dirent *sd,
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700795 struct sysfs_dirent *new_parent_sd, const void *new_ns,
796 const char *new_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797{
Tejun Heo51225032007-06-14 04:27:25 +0900798 const char *dup_name = NULL;
Tejun Heo996b7372007-06-14 03:45:14 +0900799 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Eric W. Biederman832b6af2009-11-20 16:08:56 -0800801 mutex_lock(&sysfs_mutex);
Eric W. Biederman932ea2e2007-08-20 21:36:30 +0900802
Eric W. Biederman9918f9a2007-08-20 21:36:31 +0900803 error = 0;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700804 if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) &&
Eric W. Biedermanca1bab32009-11-20 16:08:57 -0800805 (strcmp(sd->s_name, new_name) == 0))
Eric W. Biederman9918f9a2007-08-20 21:36:31 +0900806 goto out; /* nothing to rename */
807
Tejun Heo996b7372007-06-14 03:45:14 +0900808 error = -EEXIST;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700809 if (sysfs_find_dirent(new_parent_sd, new_ns, new_name))
Eric W. Biederman832b6af2009-11-20 16:08:56 -0800810 goto out;
Tejun Heo996b7372007-06-14 03:45:14 +0900811
Eric W. Biederman0b4a4fe2008-07-03 18:05:28 -0700812 /* rename sysfs_dirent */
Eric W. Biedermanca1bab32009-11-20 16:08:57 -0800813 if (strcmp(sd->s_name, new_name) != 0) {
814 error = -ENOMEM;
815 new_name = dup_name = kstrdup(new_name, GFP_KERNEL);
816 if (!new_name)
817 goto out;
Tejun Heo996b7372007-06-14 03:45:14 +0900818
Eric W. Biedermanca1bab32009-11-20 16:08:57 -0800819 dup_name = sd->s_name;
820 sd->s_name = new_name;
821 }
822
823 /* Remove from old parent's list and insert into new parent's list. */
824 if (sd->s_parent != new_parent_sd) {
825 sysfs_unlink_sibling(sd);
826 sysfs_get(new_parent_sd);
827 sysfs_put(sd->s_parent);
828 sd->s_parent = new_parent_sd;
829 sysfs_link_sibling(sd);
830 }
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700831 sd->s_ns = new_ns;
Tejun Heo0c096b52007-06-14 03:45:15 +0900832
Tejun Heo996b7372007-06-14 03:45:14 +0900833 error = 0;
Eric W. Biederman9918f9a2007-08-20 21:36:31 +0900834 out:
Eric W. Biederman832b6af2009-11-20 16:08:56 -0800835 mutex_unlock(&sysfs_mutex);
836 kfree(dup_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 return error;
838}
839
Eric W. Biedermanca1bab32009-11-20 16:08:57 -0800840int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
841{
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700842 struct sysfs_dirent *parent_sd = kobj->sd->s_parent;
843 const void *new_ns = NULL;
844
845 if (sysfs_ns_type(parent_sd))
846 new_ns = kobj->ktype->namespace(kobj);
847
848 return sysfs_rename(kobj->sd, parent_sd, new_ns, new_name);
Eric W. Biedermanca1bab32009-11-20 16:08:57 -0800849}
850
Tejun Heo51225032007-06-14 04:27:25 +0900851int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
Cornelia Huck8a824722006-11-20 17:07:51 +0100852{
Tejun Heo51225032007-06-14 04:27:25 +0900853 struct sysfs_dirent *sd = kobj->sd;
854 struct sysfs_dirent *new_parent_sd;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700855 const void *new_ns = NULL;
Cornelia Huck8a824722006-11-20 17:07:51 +0100856
Tejun Heo51225032007-06-14 04:27:25 +0900857 BUG_ON(!sd->s_parent);
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700858 if (sysfs_ns_type(sd->s_parent))
859 new_ns = kobj->ktype->namespace(kobj);
Eric W. Biedermanca1bab32009-11-20 16:08:57 -0800860 new_parent_sd = new_parent_kobj && new_parent_kobj->sd ?
Cornelia Hucka6a83572009-10-06 15:33:35 +0200861 new_parent_kobj->sd : &sysfs_root;
Cornelia Huck8a824722006-11-20 17:07:51 +0100862
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700863 return sysfs_rename(sd, new_parent_sd, new_ns, sd->s_name);
Cornelia Huck8a824722006-11-20 17:07:51 +0100864}
865
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866/* Relationship between s_mode and the DT_xxx types */
867static inline unsigned char dt_type(struct sysfs_dirent *sd)
868{
869 return (sd->s_mode >> 12) & 15;
870}
871
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800872static int sysfs_dir_release(struct inode *inode, struct file *filp)
873{
874 sysfs_put(filp->private_data);
875 return 0;
876}
877
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700878static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
879 struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800880{
881 if (pos) {
882 int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
883 pos->s_parent == parent_sd &&
884 ino == pos->s_ino;
885 sysfs_put(pos);
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700886 if (!valid)
887 pos = NULL;
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800888 }
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700889 if (!pos && (ino > 1) && (ino < INT_MAX)) {
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800890 pos = parent_sd->s_dir.children;
891 while (pos && (ino > pos->s_ino))
892 pos = pos->s_sibling;
893 }
Eric W. Biedermanaf10ec72010-03-30 11:31:27 -0700894 while (pos && pos->s_ns && pos->s_ns != ns)
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700895 pos = pos->s_sibling;
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800896 return pos;
897}
898
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700899static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
900 struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800901{
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700902 pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800903 if (pos)
904 pos = pos->s_sibling;
Eric W. Biedermanaf10ec72010-03-30 11:31:27 -0700905 while (pos && pos->s_ns && pos->s_ns != ns)
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700906 pos = pos->s_sibling;
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800907 return pos;
908}
909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
911{
Josef "Jeff" Sipekf427f5d2006-12-08 02:36:36 -0800912 struct dentry *dentry = filp->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 struct sysfs_dirent * parent_sd = dentry->d_fsdata;
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800914 struct sysfs_dirent *pos = filp->private_data;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700915 enum kobj_ns_type type;
916 const void *ns;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 ino_t ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700919 type = sysfs_ns_type(parent_sd);
920 ns = sysfs_info(dentry->d_sb)->ns[type];
921
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900922 if (filp->f_pos == 0) {
923 ino = parent_sd->s_ino;
924 if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0)
925 filp->f_pos++;
926 }
927 if (filp->f_pos == 1) {
928 if (parent_sd->s_parent)
929 ino = parent_sd->s_parent->s_ino;
930 else
Eric Sandeendc351252007-06-11 14:02:45 +0900931 ino = parent_sd->s_ino;
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900932 if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 filp->f_pos++;
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900934 }
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800935 mutex_lock(&sysfs_mutex);
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700936 for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos);
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800937 pos;
Eric W. Biederman3ff195b2010-03-30 11:31:26 -0700938 pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) {
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800939 const char * name;
940 unsigned int type;
941 int len, ret;
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900942
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800943 name = pos->s_name;
944 len = strlen(name);
945 ino = pos->s_ino;
946 type = dt_type(pos);
947 filp->f_pos = ino;
948 filp->private_data = sysfs_get(pos);
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900949
Eric W. Biederman3efa65b2007-08-20 21:36:30 +0900950 mutex_unlock(&sysfs_mutex);
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800951 ret = filldir(dirent, name, len, filp->f_pos, ino, type);
952 mutex_lock(&sysfs_mutex);
953 if (ret < 0)
954 break;
955 }
956 mutex_unlock(&sysfs_mutex);
957 if ((filp->f_pos > 1) && !pos) { /* EOF */
958 filp->f_pos = INT_MAX;
959 filp->private_data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 }
961 return 0;
962}
963
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -0800965const struct file_operations sysfs_dir_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 .read = generic_read_dir,
967 .readdir = sysfs_readdir,
Eric W. Biederman1e5289c2010-01-01 14:43:53 -0800968 .release = sysfs_dir_release,
Christoph Hellwig3222a3e2008-09-03 21:53:01 +0200969 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970};