Blame view

fs/kernfs/dir.c 39.9 KB
b8441ed27   Tejun Heo   sysfs, kernfs: ad...
1
2
3
4
5
6
7
8
9
  /*
   * fs/kernfs/dir.c - kernfs directory implementation
   *
   * Copyright (c) 2001-3 Patrick Mochel
   * Copyright (c) 2007 SUSE Linux Products GmbH
   * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org>
   *
   * This file is released under the GPLv2.
   */
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
10

abd54f028   Tejun Heo   kernfs: replace k...
11
  #include <linux/sched.h>
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
12
13
14
15
16
17
18
19
  #include <linux/fs.h>
  #include <linux/namei.h>
  #include <linux/idr.h>
  #include <linux/slab.h>
  #include <linux/security.h>
  #include <linux/hash.h>
  
  #include "kernfs-internal.h"
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
20
  DEFINE_MUTEX(kernfs_mutex);
3eef34ad7   Tejun Heo   kernfs: implement...
21
22
  static DEFINE_SPINLOCK(kernfs_rename_lock);	/* kn->parent and ->name */
  static char kernfs_pr_cont_buf[PATH_MAX];	/* protected by rename_lock */
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
23

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
24
  #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
25

81c173cb5   Tejun Heo   kernfs: remove KE...
26
27
28
29
30
  static bool kernfs_active(struct kernfs_node *kn)
  {
  	lockdep_assert_held(&kernfs_mutex);
  	return atomic_read(&kn->active) >= 0;
  }
182fd64b6   Tejun Heo   kernfs: remove KE...
31
32
33
34
35
36
37
38
  static bool kernfs_lockdep(struct kernfs_node *kn)
  {
  #ifdef CONFIG_DEBUG_LOCK_ALLOC
  	return kn->flags & KERNFS_LOCKDEP;
  #else
  	return false;
  #endif
  }
3eef34ad7   Tejun Heo   kernfs: implement...
39
40
41
42
  static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen)
  {
  	return strlcpy(buf, kn->parent ? kn->name : "/", buflen);
  }
9f6df573a   Aditya Kali   kernfs: Add API t...
43
44
  /* kernfs_node_depth - compute depth from @from to @to */
  static size_t kernfs_depth(struct kernfs_node *from, struct kernfs_node *to)
3eef34ad7   Tejun Heo   kernfs: implement...
45
  {
9f6df573a   Aditya Kali   kernfs: Add API t...
46
  	size_t depth = 0;
3eef34ad7   Tejun Heo   kernfs: implement...
47

9f6df573a   Aditya Kali   kernfs: Add API t...
48
49
50
51
52
53
  	while (to->parent && to != from) {
  		depth++;
  		to = to->parent;
  	}
  	return depth;
  }
3eef34ad7   Tejun Heo   kernfs: implement...
54

9f6df573a   Aditya Kali   kernfs: Add API t...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  static struct kernfs_node *kernfs_common_ancestor(struct kernfs_node *a,
  						  struct kernfs_node *b)
  {
  	size_t da, db;
  	struct kernfs_root *ra = kernfs_root(a), *rb = kernfs_root(b);
  
  	if (ra != rb)
  		return NULL;
  
  	da = kernfs_depth(ra->kn, a);
  	db = kernfs_depth(rb->kn, b);
  
  	while (da > db) {
  		a = a->parent;
  		da--;
  	}
  	while (db > da) {
  		b = b->parent;
  		db--;
  	}
  
  	/* worst case b and a will be the same at root */
  	while (b != a) {
  		b = b->parent;
  		a = a->parent;
  	}
  
  	return a;
  }
  
  /**
   * kernfs_path_from_node_locked - find a pseudo-absolute path to @kn_to,
   * where kn_from is treated as root of the path.
   * @kn_from: kernfs node which should be treated as root for the path
   * @kn_to: kernfs node to which path is needed
   * @buf: buffer to copy the path into
   * @buflen: size of @buf
   *
   * We need to handle couple of scenarios here:
   * [1] when @kn_from is an ancestor of @kn_to at some level
   * kn_from: /n1/n2/n3
   * kn_to:   /n1/n2/n3/n4/n5
   * result:  /n4/n5
   *
   * [2] when @kn_from is on a different hierarchy and we need to find common
   * ancestor between @kn_from and @kn_to.
   * kn_from: /n1/n2/n3/n4
   * kn_to:   /n1/n2/n5
   * result:  /../../n5
   * OR
   * kn_from: /n1/n2/n3/n4/n5   [depth=5]
   * kn_to:   /n1/n2/n3         [depth=3]
   * result:  /../..
   *
3abb1d90f   Tejun Heo   kernfs: make kern...
109
110
111
   * Returns the length of the full path.  If the full length is equal to or
   * greater than @buflen, @buf contains the truncated path with the trailing
   * '\0'.  On error, -errno is returned.
9f6df573a   Aditya Kali   kernfs: Add API t...
112
113
114
115
116
117
118
   */
  static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
  					struct kernfs_node *kn_from,
  					char *buf, size_t buflen)
  {
  	struct kernfs_node *kn, *common;
  	const char parent_str[] = "/..";
3abb1d90f   Tejun Heo   kernfs: make kern...
119
120
  	size_t depth_from, depth_to, len = 0;
  	int i, j;
9f6df573a   Aditya Kali   kernfs: Add API t...
121
122
123
124
125
126
127
128
129
  
  	if (!kn_from)
  		kn_from = kernfs_root(kn_to)->kn;
  
  	if (kn_from == kn_to)
  		return strlcpy(buf, "/", buflen);
  
  	common = kernfs_common_ancestor(kn_from, kn_to);
  	if (WARN_ON(!common))
3abb1d90f   Tejun Heo   kernfs: make kern...
130
  		return -EINVAL;
9f6df573a   Aditya Kali   kernfs: Add API t...
131
132
133
134
135
136
137
138
139
140
141
142
  
  	depth_to = kernfs_depth(common, kn_to);
  	depth_from = kernfs_depth(common, kn_from);
  
  	if (buf)
  		buf[0] = '\0';
  
  	for (i = 0; i < depth_from; i++)
  		len += strlcpy(buf + len, parent_str,
  			       len < buflen ? buflen - len : 0);
  
  	/* Calculate how many bytes we need for the rest */
3abb1d90f   Tejun Heo   kernfs: make kern...
143
144
145
146
147
148
149
  	for (i = depth_to - 1; i >= 0; i--) {
  		for (kn = kn_to, j = 0; j < i; j++)
  			kn = kn->parent;
  		len += strlcpy(buf + len, "/",
  			       len < buflen ? buflen - len : 0);
  		len += strlcpy(buf + len, kn->name,
  			       len < buflen ? buflen - len : 0);
9f6df573a   Aditya Kali   kernfs: Add API t...
150
  	}
3eef34ad7   Tejun Heo   kernfs: implement...
151

3abb1d90f   Tejun Heo   kernfs: make kern...
152
  	return len;
3eef34ad7   Tejun Heo   kernfs: implement...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  }
  
  /**
   * kernfs_name - obtain the name of a given node
   * @kn: kernfs_node of interest
   * @buf: buffer to copy @kn's name into
   * @buflen: size of @buf
   *
   * Copies the name of @kn into @buf of @buflen bytes.  The behavior is
   * similar to strlcpy().  It returns the length of @kn's name and if @buf
   * isn't long enough, it's filled upto @buflen-1 and nul terminated.
   *
   * This function can be called from any context.
   */
  int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
  {
  	unsigned long flags;
  	int ret;
  
  	spin_lock_irqsave(&kernfs_rename_lock, flags);
  	ret = kernfs_name_locked(kn, buf, buflen);
  	spin_unlock_irqrestore(&kernfs_rename_lock, flags);
  	return ret;
  }
  
  /**
9f6df573a   Aditya Kali   kernfs: Add API t...
179
180
181
182
183
184
185
186
187
188
189
   * kernfs_path_from_node - build path of node @to relative to @from.
   * @from: parent kernfs_node relative to which we need to build the path
   * @to: kernfs_node of interest
   * @buf: buffer to copy @to's path into
   * @buflen: size of @buf
   *
   * Builds @to's path relative to @from in @buf. @from and @to must
   * be on the same kernfs-root. If @from is not parent of @to, then a relative
   * path (which includes '..'s) as needed to reach from @from to @to is
   * returned.
   *
3abb1d90f   Tejun Heo   kernfs: make kern...
190
191
192
   * Returns the length of the full path.  If the full length is equal to or
   * greater than @buflen, @buf contains the truncated path with the trailing
   * '\0'.  On error, -errno is returned.
9f6df573a   Aditya Kali   kernfs: Add API t...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
   */
  int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from,
  			  char *buf, size_t buflen)
  {
  	unsigned long flags;
  	int ret;
  
  	spin_lock_irqsave(&kernfs_rename_lock, flags);
  	ret = kernfs_path_from_node_locked(to, from, buf, buflen);
  	spin_unlock_irqrestore(&kernfs_rename_lock, flags);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(kernfs_path_from_node);
  
  /**
3eef34ad7   Tejun Heo   kernfs: implement...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
   * pr_cont_kernfs_name - pr_cont name of a kernfs_node
   * @kn: kernfs_node of interest
   *
   * This function can be called from any context.
   */
  void pr_cont_kernfs_name(struct kernfs_node *kn)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&kernfs_rename_lock, flags);
  
  	kernfs_name_locked(kn, kernfs_pr_cont_buf, sizeof(kernfs_pr_cont_buf));
  	pr_cont("%s", kernfs_pr_cont_buf);
  
  	spin_unlock_irqrestore(&kernfs_rename_lock, flags);
  }
  
  /**
   * pr_cont_kernfs_path - pr_cont path of a kernfs_node
   * @kn: kernfs_node of interest
   *
   * This function can be called from any context.
   */
  void pr_cont_kernfs_path(struct kernfs_node *kn)
  {
  	unsigned long flags;
9f6df573a   Aditya Kali   kernfs: Add API t...
234
  	int sz;
3eef34ad7   Tejun Heo   kernfs: implement...
235
236
  
  	spin_lock_irqsave(&kernfs_rename_lock, flags);
9f6df573a   Aditya Kali   kernfs: Add API t...
237
238
239
240
241
242
243
244
245
246
247
248
249
  	sz = kernfs_path_from_node_locked(kn, NULL, kernfs_pr_cont_buf,
  					  sizeof(kernfs_pr_cont_buf));
  	if (sz < 0) {
  		pr_cont("(error)");
  		goto out;
  	}
  
  	if (sz >= sizeof(kernfs_pr_cont_buf)) {
  		pr_cont("(name too long)");
  		goto out;
  	}
  
  	pr_cont("%s", kernfs_pr_cont_buf);
3eef34ad7   Tejun Heo   kernfs: implement...
250

9f6df573a   Aditya Kali   kernfs: Add API t...
251
  out:
3eef34ad7   Tejun Heo   kernfs: implement...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  	spin_unlock_irqrestore(&kernfs_rename_lock, flags);
  }
  
  /**
   * kernfs_get_parent - determine the parent node and pin it
   * @kn: kernfs_node of interest
   *
   * Determines @kn's parent, pins and returns it.  This function can be
   * called from any context.
   */
  struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn)
  {
  	struct kernfs_node *parent;
  	unsigned long flags;
  
  	spin_lock_irqsave(&kernfs_rename_lock, flags);
  	parent = kn->parent;
  	kernfs_get(parent);
  	spin_unlock_irqrestore(&kernfs_rename_lock, flags);
  
  	return parent;
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
274
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
275
   *	kernfs_name_hash
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
276
277
278
279
280
   *	@name: Null terminated string to hash
   *	@ns:   Namespace tag to hash
   *
   *	Returns 31 bit hash of ns + name (so it fits in an off_t )
   */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
281
  static unsigned int kernfs_name_hash(const char *name, const void *ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
282
  {
8387ff257   Linus Torvalds   vfs: make the str...
283
  	unsigned long hash = init_name_hash(ns);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
284
285
286
  	unsigned int len = strlen(name);
  	while (len--)
  		hash = partial_name_hash(*name++, hash);
8387ff257   Linus Torvalds   vfs: make the str...
287
  	hash = end_name_hash(hash);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
288
289
  	hash &= 0x7fffffffU;
  	/* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
88391d49a   Richard Cochran   kernfs: fix off b...
290
  	if (hash < 2)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
291
292
293
294
295
  		hash += 2;
  	if (hash >= INT_MAX)
  		hash = INT_MAX - 1;
  	return hash;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
296
297
  static int kernfs_name_compare(unsigned int hash, const char *name,
  			       const void *ns, const struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
298
  {
72392ed0e   Rasmus Villemoes   kernfs: Fix kernf...
299
300
301
302
303
304
305
306
  	if (hash < kn->hash)
  		return -1;
  	if (hash > kn->hash)
  		return 1;
  	if (ns < kn->ns)
  		return -1;
  	if (ns > kn->ns)
  		return 1;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
307
  	return strcmp(name, kn->name);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
308
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
309
310
  static int kernfs_sd_compare(const struct kernfs_node *left,
  			     const struct kernfs_node *right)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
311
  {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
312
  	return kernfs_name_compare(left->hash, left->name, left->ns, right);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
313
314
315
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
316
   *	kernfs_link_sibling - link kernfs_node into sibling rbtree
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
317
   *	@kn: kernfs_node of interest
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
318
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
319
   *	Link @kn into its sibling rbtree which starts from
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
320
   *	@kn->parent->dir.children.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
321
322
   *
   *	Locking:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
323
   *	mutex_lock(kernfs_mutex)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
324
325
326
327
   *
   *	RETURNS:
   *	0 on susccess -EEXIST on failure.
   */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
328
  static int kernfs_link_sibling(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
329
  {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
330
  	struct rb_node **node = &kn->parent->dir.children.rb_node;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
331
  	struct rb_node *parent = NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
332
  	while (*node) {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
333
  		struct kernfs_node *pos;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
334
  		int result;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
335
  		pos = rb_to_kn(*node);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
336
  		parent = *node;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
337
  		result = kernfs_sd_compare(kn, pos);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
338
  		if (result < 0)
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
339
  			node = &pos->rb.rb_left;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
340
  		else if (result > 0)
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
341
  			node = &pos->rb.rb_right;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
342
343
344
  		else
  			return -EEXIST;
  	}
c1befb885   Jianyu Zhan   kernfs: fix a sub...
345

fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
346
  	/* add new node and rebalance the tree */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
347
348
  	rb_link_node(&kn->rb, parent, node);
  	rb_insert_color(&kn->rb, &kn->parent->dir.children);
c1befb885   Jianyu Zhan   kernfs: fix a sub...
349
350
351
352
  
  	/* successfully added, account subdir number */
  	if (kernfs_type(kn) == KERNFS_DIR)
  		kn->parent->dir.subdirs++;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
353
354
355
356
  	return 0;
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
357
   *	kernfs_unlink_sibling - unlink kernfs_node from sibling rbtree
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
358
   *	@kn: kernfs_node of interest
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
359
   *
35beab063   Tejun Heo   kernfs: restructu...
360
361
362
   *	Try to unlink @kn from its sibling rbtree which starts from
   *	kn->parent->dir.children.  Returns %true if @kn was actually
   *	removed, %false if @kn wasn't on the rbtree.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
363
364
   *
   *	Locking:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
365
   *	mutex_lock(kernfs_mutex)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
366
   */
35beab063   Tejun Heo   kernfs: restructu...
367
  static bool kernfs_unlink_sibling(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
368
  {
35beab063   Tejun Heo   kernfs: restructu...
369
370
  	if (RB_EMPTY_NODE(&kn->rb))
  		return false;
df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
371
  	if (kernfs_type(kn) == KERNFS_DIR)
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
372
  		kn->parent->dir.subdirs--;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
373

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
374
  	rb_erase(&kn->rb, &kn->parent->dir.children);
35beab063   Tejun Heo   kernfs: restructu...
375
376
  	RB_CLEAR_NODE(&kn->rb);
  	return true;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
377
378
379
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
380
   *	kernfs_get_active - get an active reference to kernfs_node
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
381
   *	@kn: kernfs_node to get an active reference to
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
382
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
383
   *	Get an active reference of @kn.  This function is noop if @kn
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
384
385
386
   *	is NULL.
   *
   *	RETURNS:
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
387
   *	Pointer to @kn on success, NULL on failure.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
388
   */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
389
  struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
390
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
391
  	if (unlikely(!kn))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
392
  		return NULL;
f4b3e631b   Greg Kroah-Hartman   Revert "kernfs: m...
393
394
  	if (!atomic_inc_unless_negative(&kn->active))
  		return NULL;
895a068a5   Tejun Heo   kernfs: make kern...
395

182fd64b6   Tejun Heo   kernfs: remove KE...
396
  	if (kernfs_lockdep(kn))
f4b3e631b   Greg Kroah-Hartman   Revert "kernfs: m...
397
398
  		rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_);
  	return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
399
400
401
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
402
   *	kernfs_put_active - put an active reference to kernfs_node
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
403
   *	@kn: kernfs_node to put an active reference to
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
404
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
405
   *	Put an active reference to @kn.  This function is noop if @kn
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
406
407
   *	is NULL.
   */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
408
  void kernfs_put_active(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
409
  {
abd54f028   Tejun Heo   kernfs: replace k...
410
  	struct kernfs_root *root = kernfs_root(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
411
  	int v;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
412
  	if (unlikely(!kn))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
413
  		return;
182fd64b6   Tejun Heo   kernfs: remove KE...
414
  	if (kernfs_lockdep(kn))
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
415
  		rwsem_release(&kn->dep_map, 1, _RET_IP_);
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
416
  	v = atomic_dec_return(&kn->active);
df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
417
  	if (likely(v != KN_DEACTIVATED_BIAS))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
418
  		return;
abd54f028   Tejun Heo   kernfs: replace k...
419
  	wake_up_all(&root->deactivate_waitq);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
420
421
422
  }
  
  /**
81c173cb5   Tejun Heo   kernfs: remove KE...
423
424
   * kernfs_drain - drain kernfs_node
   * @kn: kernfs_node to drain
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
425
   *
81c173cb5   Tejun Heo   kernfs: remove KE...
426
427
428
   * Drain existing usages and nuke all existing mmaps of @kn.  Mutiple
   * removers may invoke this function concurrently on @kn and all will
   * return after draining is complete.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
429
   */
81c173cb5   Tejun Heo   kernfs: remove KE...
430
  static void kernfs_drain(struct kernfs_node *kn)
35beab063   Tejun Heo   kernfs: restructu...
431
  	__releases(&kernfs_mutex) __acquires(&kernfs_mutex)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
432
  {
abd54f028   Tejun Heo   kernfs: replace k...
433
  	struct kernfs_root *root = kernfs_root(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
434

35beab063   Tejun Heo   kernfs: restructu...
435
  	lockdep_assert_held(&kernfs_mutex);
81c173cb5   Tejun Heo   kernfs: remove KE...
436
  	WARN_ON_ONCE(kernfs_active(kn));
ea1c472df   Tejun Heo   kernfs: replace k...
437

35beab063   Tejun Heo   kernfs: restructu...
438
  	mutex_unlock(&kernfs_mutex);
abd54f028   Tejun Heo   kernfs: replace k...
439

182fd64b6   Tejun Heo   kernfs: remove KE...
440
  	if (kernfs_lockdep(kn)) {
35beab063   Tejun Heo   kernfs: restructu...
441
442
443
444
  		rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
  		if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS)
  			lock_contended(&kn->dep_map, _RET_IP_);
  	}
abd54f028   Tejun Heo   kernfs: replace k...
445

35beab063   Tejun Heo   kernfs: restructu...
446
  	/* but everyone should wait for draining */
abd54f028   Tejun Heo   kernfs: replace k...
447
448
  	wait_event(root->deactivate_waitq,
  		   atomic_read(&kn->active) == KN_DEACTIVATED_BIAS);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
449

182fd64b6   Tejun Heo   kernfs: remove KE...
450
  	if (kernfs_lockdep(kn)) {
a6607930b   Tejun Heo   kernfs: make kern...
451
452
453
  		lock_acquired(&kn->dep_map, _RET_IP_);
  		rwsem_release(&kn->dep_map, 1, _RET_IP_);
  	}
35beab063   Tejun Heo   kernfs: restructu...
454

ccf02aaf8   Tejun Heo   kernfs: invoke ke...
455
  	kernfs_unmap_bin_file(kn);
35beab063   Tejun Heo   kernfs: restructu...
456
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
457
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
458
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
459
460
   * kernfs_get - get a reference count on a kernfs_node
   * @kn: the target kernfs_node
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
461
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
462
  void kernfs_get(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
463
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
464
  	if (kn) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
465
466
  		WARN_ON(!atomic_read(&kn->count));
  		atomic_inc(&kn->count);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
467
468
469
470
471
  	}
  }
  EXPORT_SYMBOL_GPL(kernfs_get);
  
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
472
473
   * kernfs_put - put a reference count on a kernfs_node
   * @kn: the target kernfs_node
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
474
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
475
   * Put a reference count of @kn and destroy it if it reached zero.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
476
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
477
  void kernfs_put(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
478
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
479
  	struct kernfs_node *parent;
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
480
  	struct kernfs_root *root;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
481

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
482
  	if (!kn || !atomic_dec_and_test(&kn->count))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
483
  		return;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
484
  	root = kernfs_root(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
485
   repeat:
81c173cb5   Tejun Heo   kernfs: remove KE...
486
487
  	/*
  	 * Moving/renaming is always done while holding reference.
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
488
  	 * kn->parent won't change beneath us.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
489
  	 */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
490
  	parent = kn->parent;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
491

81c173cb5   Tejun Heo   kernfs: remove KE...
492
493
494
495
  	WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS,
  		  "kernfs_put: %s/%s: released with incorrect active_ref %d
  ",
  		  parent ? parent->name : "", kn->name, atomic_read(&kn->active));
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
496

df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
497
  	if (kernfs_type(kn) == KERNFS_LINK)
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
498
  		kernfs_put(kn->symlink.target_kn);
dfeb0750b   Tejun Heo   kernfs: remove KE...
499
500
  
  	kfree_const(kn->name);
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
501
502
503
504
505
  	if (kn->iattr) {
  		if (kn->iattr->ia_secdata)
  			security_release_secctx(kn->iattr->ia_secdata,
  						kn->iattr->ia_secdata_len);
  		simple_xattrs_free(&kn->iattr->xattrs);
2322392b0   Tejun Heo   kernfs: implement...
506
  	}
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
507
508
  	kfree(kn->iattr);
  	ida_simple_remove(&root->ino_ida, kn->ino);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
509
  	kmem_cache_free(kernfs_node_cache, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
510

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
511
512
  	kn = parent;
  	if (kn) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
513
  		if (atomic_dec_and_test(&kn->count))
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
514
515
  			goto repeat;
  	} else {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
516
  		/* just released the root kn, free @root too */
bc755553d   Tejun Heo   sysfs, kernfs: ma...
517
  		ida_destroy(&root->ino_ida);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
518
519
  		kfree(root);
  	}
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
520
521
  }
  EXPORT_SYMBOL_GPL(kernfs_put);
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
522
  static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
523
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
524
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
525
526
527
  
  	if (flags & LOOKUP_RCU)
  		return -ECHILD;
19bbb9262   Tejun Heo   kernfs: allow neg...
528
  	/* Always perform fresh lookup for negatives */
2b0143b5c   David Howells   VFS: normal files...
529
  	if (d_really_is_negative(dentry))
19bbb9262   Tejun Heo   kernfs: allow neg...
530
  		goto out_bad_unlocked;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
531
  	kn = dentry->d_fsdata;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
532
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
533

81c173cb5   Tejun Heo   kernfs: remove KE...
534
535
  	/* The kernfs node has been deactivated */
  	if (!kernfs_active(kn))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
536
  		goto out_bad;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
537
  	/* The kernfs node has been moved? */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
538
  	if (dentry->d_parent->d_fsdata != kn->parent)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
539
  		goto out_bad;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
540
  	/* The kernfs node has been renamed */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
541
  	if (strcmp(dentry->d_name.name, kn->name) != 0)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
542
  		goto out_bad;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
543
  	/* The kernfs node has been moved to a different namespace */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
544
  	if (kn->parent && kernfs_ns_enabled(kn->parent) &&
c525aaddc   Tejun Heo   kernfs: s/sysfs/k...
545
  	    kernfs_info(dentry->d_sb)->ns != kn->ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
546
  		goto out_bad;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
547
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
548
549
  	return 1;
  out_bad:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
550
  	mutex_unlock(&kernfs_mutex);
19bbb9262   Tejun Heo   kernfs: allow neg...
551
  out_bad_unlocked:
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
552
553
  	return 0;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
554
  static void kernfs_dop_release(struct dentry *dentry)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
555
556
557
  {
  	kernfs_put(dentry->d_fsdata);
  }
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
558
  const struct dentry_operations kernfs_dops = {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
559
  	.d_revalidate	= kernfs_dop_revalidate,
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
560
  	.d_release	= kernfs_dop_release,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
561
  };
0c23b2259   Tejun Heo   kernfs: implement...
562
563
564
565
566
567
568
569
570
571
572
573
574
  /**
   * kernfs_node_from_dentry - determine kernfs_node associated with a dentry
   * @dentry: the dentry in question
   *
   * Return the kernfs_node associated with @dentry.  If @dentry is not a
   * kernfs one, %NULL is returned.
   *
   * While the returned kernfs_node will stay accessible as long as @dentry
   * is accessible, the returned node can be in any state and the caller is
   * fully responsible for determining what's accessible.
   */
  struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry)
  {
f41c59345   Li Zefan   kernfs: fix kernf...
575
  	if (dentry->d_sb->s_op == &kernfs_sops)
0c23b2259   Tejun Heo   kernfs: implement...
576
577
578
  		return dentry->d_fsdata;
  	return NULL;
  }
db4aad209   Tejun Heo   kernfs: associate...
579
580
581
  static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
  					     const char *name, umode_t mode,
  					     unsigned flags)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
582
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
583
  	struct kernfs_node *kn;
bc755553d   Tejun Heo   sysfs, kernfs: ma...
584
  	int ret;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
585

dfeb0750b   Tejun Heo   kernfs: remove KE...
586
587
588
  	name = kstrdup_const(name, GFP_KERNEL);
  	if (!name)
  		return NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
589

a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
590
  	kn = kmem_cache_zalloc(kernfs_node_cache, GFP_KERNEL);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
591
  	if (!kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
592
  		goto err_out1;
b2a209ffa   Vladimir Davydov   Revert "kernfs: d...
593
  	ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL);
bc755553d   Tejun Heo   sysfs, kernfs: ma...
594
  	if (ret < 0)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
595
  		goto err_out2;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
596
  	kn->ino = ret;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
597

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
598
  	atomic_set(&kn->count, 1);
81c173cb5   Tejun Heo   kernfs: remove KE...
599
  	atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
35beab063   Tejun Heo   kernfs: restructu...
600
  	RB_CLEAR_NODE(&kn->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
601

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
602
603
  	kn->name = name;
  	kn->mode = mode;
81c173cb5   Tejun Heo   kernfs: remove KE...
604
  	kn->flags = flags;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
605

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
606
  	return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
607
608
  
   err_out2:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
609
  	kmem_cache_free(kernfs_node_cache, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
610
   err_out1:
dfeb0750b   Tejun Heo   kernfs: remove KE...
611
  	kfree_const(name);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
612
613
  	return NULL;
  }
db4aad209   Tejun Heo   kernfs: associate...
614
615
616
617
618
619
620
621
622
623
624
625
626
  struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
  				    const char *name, umode_t mode,
  				    unsigned flags)
  {
  	struct kernfs_node *kn;
  
  	kn = __kernfs_new_node(kernfs_root(parent), name, mode, flags);
  	if (kn) {
  		kernfs_get(parent);
  		kn->parent = parent;
  	}
  	return kn;
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
627
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
628
   *	kernfs_add_one - add kernfs_node to parent without warning
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
629
   *	@kn: kernfs_node to be added
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
630
   *
db4aad209   Tejun Heo   kernfs: associate...
631
632
633
   *	The caller must already have initialized @kn->parent.  This
   *	function increments nlink of the parent's inode if @kn is a
   *	directory and link into the children list of the parent.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
634
   *
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
635
636
637
638
   *	RETURNS:
   *	0 on success, -EEXIST if entry with the given name already
   *	exists.
   */
988cd7afb   Tejun Heo   kernfs: remove ke...
639
  int kernfs_add_one(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
640
  {
db4aad209   Tejun Heo   kernfs: associate...
641
  	struct kernfs_node *parent = kn->parent;
c525aaddc   Tejun Heo   kernfs: s/sysfs/k...
642
  	struct kernfs_iattrs *ps_iattr;
988cd7afb   Tejun Heo   kernfs: remove ke...
643
  	bool has_ns;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
644
  	int ret;
988cd7afb   Tejun Heo   kernfs: remove ke...
645
646
647
648
649
650
651
652
  	mutex_lock(&kernfs_mutex);
  
  	ret = -EINVAL;
  	has_ns = kernfs_ns_enabled(parent);
  	if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'
  ",
  		 has_ns ? "required" : "invalid", parent->name, kn->name))
  		goto out_unlock;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
653

df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
654
  	if (kernfs_type(parent) != KERNFS_DIR)
988cd7afb   Tejun Heo   kernfs: remove ke...
655
  		goto out_unlock;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
656

988cd7afb   Tejun Heo   kernfs: remove ke...
657
  	ret = -ENOENT;
ea015218f   Eric W. Biederman   kernfs: Add suppo...
658
659
  	if (parent->flags & KERNFS_EMPTY_DIR)
  		goto out_unlock;
d35258ef7   Tejun Heo   kernfs: allow nod...
660
  	if ((parent->flags & KERNFS_ACTIVATED) && !kernfs_active(parent))
988cd7afb   Tejun Heo   kernfs: remove ke...
661
  		goto out_unlock;
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
662

c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
663
  	kn->hash = kernfs_name_hash(kn->name, kn->ns);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
664

c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
665
  	ret = kernfs_link_sibling(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
666
  	if (ret)
988cd7afb   Tejun Heo   kernfs: remove ke...
667
  		goto out_unlock;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
668
669
  
  	/* Update timestamps on the parent */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
670
  	ps_iattr = parent->iattr;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
671
672
  	if (ps_iattr) {
  		struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
3a3a5fece   Deepa Dinamani   fs: kernfs: Repla...
673
674
  		ktime_get_real_ts(&ps_iattrs->ia_ctime);
  		ps_iattrs->ia_mtime = ps_iattrs->ia_ctime;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
675
  	}
d35258ef7   Tejun Heo   kernfs: allow nod...
676
677
678
679
680
681
682
683
684
685
686
687
  	mutex_unlock(&kernfs_mutex);
  
  	/*
  	 * Activate the new node unless CREATE_DEACTIVATED is requested.
  	 * If not activated here, the kernfs user is responsible for
  	 * activating the node with kernfs_activate().  A node which hasn't
  	 * been activated is not visible to userland and its removal won't
  	 * trigger deactivation.
  	 */
  	if (!(kernfs_root(kn)->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
  		kernfs_activate(kn);
  	return 0;
988cd7afb   Tejun Heo   kernfs: remove ke...
688
  out_unlock:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
689
  	mutex_unlock(&kernfs_mutex);
988cd7afb   Tejun Heo   kernfs: remove ke...
690
  	return ret;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
691
692
693
  }
  
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
694
695
   * kernfs_find_ns - find kernfs_node with the given name
   * @parent: kernfs_node to search under
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
696
697
698
   * @name: name to look for
   * @ns: the namespace tag to use
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
699
700
   * Look for kernfs_node with name @name under @parent.  Returns pointer to
   * the found kernfs_node on success, %NULL on failure.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
701
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
702
703
704
  static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
  					  const unsigned char *name,
  					  const void *ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
705
  {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
706
  	struct rb_node *node = parent->dir.children.rb_node;
ac9bba031   Tejun Heo   sysfs, kernfs: im...
707
  	bool has_ns = kernfs_ns_enabled(parent);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
708
  	unsigned int hash;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
709
  	lockdep_assert_held(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
710
711
  
  	if (has_ns != (bool)ns) {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
712
713
  		WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'
  ",
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
714
  		     has_ns ? "required" : "invalid", parent->name, name);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
715
716
  		return NULL;
  	}
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
717
  	hash = kernfs_name_hash(name, ns);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
718
  	while (node) {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
719
  		struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
720
  		int result;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
721
  		kn = rb_to_kn(node);
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
722
  		result = kernfs_name_compare(hash, name, ns, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
723
724
725
726
727
  		if (result < 0)
  			node = node->rb_left;
  		else if (result > 0)
  			node = node->rb_right;
  		else
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
728
  			return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
729
730
731
  	}
  	return NULL;
  }
bd96f76a2   Tejun Heo   kernfs: implement...
732
733
734
735
  static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
  					  const unsigned char *path,
  					  const void *ns)
  {
e56ed358a   Tejun Heo   kernfs: make kern...
736
737
  	size_t len;
  	char *p, *name;
bd96f76a2   Tejun Heo   kernfs: implement...
738
739
  
  	lockdep_assert_held(&kernfs_mutex);
e56ed358a   Tejun Heo   kernfs: make kern...
740
741
742
743
744
745
746
  	/* grab kernfs_rename_lock to piggy back on kernfs_pr_cont_buf */
  	spin_lock_irq(&kernfs_rename_lock);
  
  	len = strlcpy(kernfs_pr_cont_buf, path, sizeof(kernfs_pr_cont_buf));
  
  	if (len >= sizeof(kernfs_pr_cont_buf)) {
  		spin_unlock_irq(&kernfs_rename_lock);
bd96f76a2   Tejun Heo   kernfs: implement...
747
  		return NULL;
e56ed358a   Tejun Heo   kernfs: make kern...
748
749
750
  	}
  
  	p = kernfs_pr_cont_buf;
bd96f76a2   Tejun Heo   kernfs: implement...
751
752
753
754
755
756
  
  	while ((name = strsep(&p, "/")) && parent) {
  		if (*name == '\0')
  			continue;
  		parent = kernfs_find_ns(parent, name, ns);
  	}
e56ed358a   Tejun Heo   kernfs: make kern...
757
  	spin_unlock_irq(&kernfs_rename_lock);
bd96f76a2   Tejun Heo   kernfs: implement...
758
759
  	return parent;
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
760
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
761
762
   * kernfs_find_and_get_ns - find and get kernfs_node with the given name
   * @parent: kernfs_node to search under
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
763
764
765
   * @name: name to look for
   * @ns: the namespace tag to use
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
766
   * Look for kernfs_node with name @name under @parent and get a reference
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
767
   * if found.  This function may sleep and returns pointer to the found
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
768
   * kernfs_node on success, %NULL on failure.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
769
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
770
771
  struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
  					   const char *name, const void *ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
772
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
773
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
774

a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
775
  	mutex_lock(&kernfs_mutex);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
776
777
  	kn = kernfs_find_ns(parent, name, ns);
  	kernfs_get(kn);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
778
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
779

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
780
  	return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
781
782
783
784
  }
  EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
  
  /**
bd96f76a2   Tejun Heo   kernfs: implement...
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
   * kernfs_walk_and_get_ns - find and get kernfs_node with the given path
   * @parent: kernfs_node to search under
   * @path: path to look for
   * @ns: the namespace tag to use
   *
   * Look for kernfs_node with path @path under @parent and get a reference
   * if found.  This function may sleep and returns pointer to the found
   * kernfs_node on success, %NULL on failure.
   */
  struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
  					   const char *path, const void *ns)
  {
  	struct kernfs_node *kn;
  
  	mutex_lock(&kernfs_mutex);
  	kn = kernfs_walk_ns(parent, path, ns);
  	kernfs_get(kn);
  	mutex_unlock(&kernfs_mutex);
  
  	return kn;
  }
  
  /**
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
808
   * kernfs_create_root - create a new kernfs hierarchy
90c07c895   Tejun Heo   kernfs: rename ke...
809
   * @scops: optional syscall operations for the hierarchy
d35258ef7   Tejun Heo   kernfs: allow nod...
810
   * @flags: KERNFS_ROOT_* flags
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
811
812
813
814
815
   * @priv: opaque data associated with the new directory
   *
   * Returns the root of the new hierarchy on success, ERR_PTR() value on
   * failure.
   */
90c07c895   Tejun Heo   kernfs: rename ke...
816
  struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
d35258ef7   Tejun Heo   kernfs: allow nod...
817
  				       unsigned int flags, void *priv)
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
818
819
  {
  	struct kernfs_root *root;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
820
  	struct kernfs_node *kn;
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
821
822
823
824
  
  	root = kzalloc(sizeof(*root), GFP_KERNEL);
  	if (!root)
  		return ERR_PTR(-ENOMEM);
bc755553d   Tejun Heo   sysfs, kernfs: ma...
825
  	ida_init(&root->ino_ida);
7d568a838   Tejun Heo   kernfs: implement...
826
  	INIT_LIST_HEAD(&root->supers);
bc755553d   Tejun Heo   sysfs, kernfs: ma...
827

db4aad209   Tejun Heo   kernfs: associate...
828
829
  	kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO,
  			       KERNFS_DIR);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
830
  	if (!kn) {
bc755553d   Tejun Heo   sysfs, kernfs: ma...
831
  		ida_destroy(&root->ino_ida);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
832
833
834
  		kfree(root);
  		return ERR_PTR(-ENOMEM);
  	}
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
835
  	kn->priv = priv;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
836
  	kn->dir.root = root;
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
837

90c07c895   Tejun Heo   kernfs: rename ke...
838
  	root->syscall_ops = scops;
d35258ef7   Tejun Heo   kernfs: allow nod...
839
  	root->flags = flags;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
840
  	root->kn = kn;
abd54f028   Tejun Heo   kernfs: replace k...
841
  	init_waitqueue_head(&root->deactivate_waitq);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
842

d35258ef7   Tejun Heo   kernfs: allow nod...
843
844
  	if (!(root->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
  		kernfs_activate(kn);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
845
846
847
848
849
850
851
852
853
854
855
856
  	return root;
  }
  
  /**
   * kernfs_destroy_root - destroy a kernfs hierarchy
   * @root: root of the hierarchy to destroy
   *
   * Destroy the hierarchy anchored at @root by removing all existing
   * directories and destroying @root.
   */
  void kernfs_destroy_root(struct kernfs_root *root)
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
857
  	kernfs_remove(root->kn);	/* will also free @root */
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
858
859
860
  }
  
  /**
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
861
862
863
   * kernfs_create_dir_ns - create a directory
   * @parent: parent in which to create a new directory
   * @name: name of the new directory
bb8b9d095   Tejun Heo   kernfs: add @mode...
864
   * @mode: mode of the new directory
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
865
866
867
868
869
   * @priv: opaque data associated with the new directory
   * @ns: optional namespace tag of the directory
   *
   * Returns the created node on success, ERR_PTR() value on failure.
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
870
  struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
bb8b9d095   Tejun Heo   kernfs: add @mode...
871
872
  					 const char *name, umode_t mode,
  					 void *priv, const void *ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
873
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
874
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
875
876
877
  	int rc;
  
  	/* allocate */
db4aad209   Tejun Heo   kernfs: associate...
878
  	kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
879
  	if (!kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
880
  		return ERR_PTR(-ENOMEM);
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
881
882
  	kn->dir.root = parent->dir.root;
  	kn->ns = ns;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
883
  	kn->priv = priv;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
884
885
  
  	/* link in */
988cd7afb   Tejun Heo   kernfs: remove ke...
886
  	rc = kernfs_add_one(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
887
  	if (!rc)
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
888
  		return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
889

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
890
  	kernfs_put(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
891
892
  	return ERR_PTR(rc);
  }
ea015218f   Eric W. Biederman   kernfs: Add suppo...
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
  /**
   * kernfs_create_empty_dir - create an always empty directory
   * @parent: parent in which to create a new directory
   * @name: name of the new directory
   *
   * Returns the created node on success, ERR_PTR() value on failure.
   */
  struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
  					    const char *name)
  {
  	struct kernfs_node *kn;
  	int rc;
  
  	/* allocate */
  	kn = kernfs_new_node(parent, name, S_IRUGO|S_IXUGO|S_IFDIR, KERNFS_DIR);
  	if (!kn)
  		return ERR_PTR(-ENOMEM);
  
  	kn->flags |= KERNFS_EMPTY_DIR;
  	kn->dir.root = parent->dir.root;
  	kn->ns = NULL;
  	kn->priv = NULL;
  
  	/* link in */
  	rc = kernfs_add_one(kn);
  	if (!rc)
  		return kn;
  
  	kernfs_put(kn);
  	return ERR_PTR(rc);
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
924
925
926
  static struct dentry *kernfs_iop_lookup(struct inode *dir,
  					struct dentry *dentry,
  					unsigned int flags)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
927
  {
19bbb9262   Tejun Heo   kernfs: allow neg...
928
  	struct dentry *ret;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
929
930
  	struct kernfs_node *parent = dentry->d_parent->d_fsdata;
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
931
932
  	struct inode *inode;
  	const void *ns = NULL;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
933
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
934

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
935
  	if (kernfs_ns_enabled(parent))
c525aaddc   Tejun Heo   kernfs: s/sysfs/k...
936
  		ns = kernfs_info(dir->i_sb)->ns;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
937

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
938
  	kn = kernfs_find_ns(parent, dentry->d_name.name, ns);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
939
940
  
  	/* no such entry */
b9c9dad0c   Tejun Heo   kernfs: add missi...
941
  	if (!kn || !kernfs_active(kn)) {
19bbb9262   Tejun Heo   kernfs: allow neg...
942
  		ret = NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
943
944
  		goto out_unlock;
  	}
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
945
946
  	kernfs_get(kn);
  	dentry->d_fsdata = kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
947
948
  
  	/* attach dentry and inode */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
949
  	inode = kernfs_get_inode(dir->i_sb, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
950
951
952
953
954
955
  	if (!inode) {
  		ret = ERR_PTR(-ENOMEM);
  		goto out_unlock;
  	}
  
  	/* instantiate and hash dentry */
41d28bca2   Al Viro   switch d_material...
956
  	ret = d_splice_alias(inode, dentry);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
957
   out_unlock:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
958
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
959
960
  	return ret;
  }
80b9bbefc   Tejun Heo   kernfs: add kernf...
961
962
963
964
  static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry,
  			    umode_t mode)
  {
  	struct kernfs_node *parent = dir->i_private;
90c07c895   Tejun Heo   kernfs: rename ke...
965
  	struct kernfs_syscall_ops *scops = kernfs_root(parent)->syscall_ops;
07c7530dd   Tejun Heo   kernfs: invoke di...
966
  	int ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
967

90c07c895   Tejun Heo   kernfs: rename ke...
968
  	if (!scops || !scops->mkdir)
80b9bbefc   Tejun Heo   kernfs: add kernf...
969
  		return -EPERM;
07c7530dd   Tejun Heo   kernfs: invoke di...
970
971
  	if (!kernfs_get_active(parent))
  		return -ENODEV;
90c07c895   Tejun Heo   kernfs: rename ke...
972
  	ret = scops->mkdir(parent, dentry->d_name.name, mode);
07c7530dd   Tejun Heo   kernfs: invoke di...
973
974
975
  
  	kernfs_put_active(parent);
  	return ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
976
977
978
979
980
  }
  
  static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
  {
  	struct kernfs_node *kn  = dentry->d_fsdata;
90c07c895   Tejun Heo   kernfs: rename ke...
981
  	struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
07c7530dd   Tejun Heo   kernfs: invoke di...
982
  	int ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
983

90c07c895   Tejun Heo   kernfs: rename ke...
984
  	if (!scops || !scops->rmdir)
80b9bbefc   Tejun Heo   kernfs: add kernf...
985
  		return -EPERM;
07c7530dd   Tejun Heo   kernfs: invoke di...
986
987
  	if (!kernfs_get_active(kn))
  		return -ENODEV;
90c07c895   Tejun Heo   kernfs: rename ke...
988
  	ret = scops->rmdir(kn);
07c7530dd   Tejun Heo   kernfs: invoke di...
989
990
991
  
  	kernfs_put_active(kn);
  	return ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
992
993
994
  }
  
  static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
1cd66c93b   Miklos Szeredi   fs: make remainin...
995
996
  			     struct inode *new_dir, struct dentry *new_dentry,
  			     unsigned int flags)
80b9bbefc   Tejun Heo   kernfs: add kernf...
997
998
999
  {
  	struct kernfs_node *kn  = old_dentry->d_fsdata;
  	struct kernfs_node *new_parent = new_dir->i_private;
90c07c895   Tejun Heo   kernfs: rename ke...
1000
  	struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
07c7530dd   Tejun Heo   kernfs: invoke di...
1001
  	int ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1002

1cd66c93b   Miklos Szeredi   fs: make remainin...
1003
1004
  	if (flags)
  		return -EINVAL;
90c07c895   Tejun Heo   kernfs: rename ke...
1005
  	if (!scops || !scops->rename)
80b9bbefc   Tejun Heo   kernfs: add kernf...
1006
  		return -EPERM;
07c7530dd   Tejun Heo   kernfs: invoke di...
1007
1008
1009
1010
1011
1012
1013
  	if (!kernfs_get_active(kn))
  		return -ENODEV;
  
  	if (!kernfs_get_active(new_parent)) {
  		kernfs_put_active(kn);
  		return -ENODEV;
  	}
90c07c895   Tejun Heo   kernfs: rename ke...
1014
  	ret = scops->rename(kn, new_parent, new_dentry->d_name.name);
07c7530dd   Tejun Heo   kernfs: invoke di...
1015
1016
1017
1018
  
  	kernfs_put_active(new_parent);
  	kernfs_put_active(kn);
  	return ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1019
  }
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1020
  const struct inode_operations kernfs_dir_iops = {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1021
1022
1023
1024
  	.lookup		= kernfs_iop_lookup,
  	.permission	= kernfs_iop_permission,
  	.setattr	= kernfs_iop_setattr,
  	.getattr	= kernfs_iop_getattr,
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1025
  	.listxattr	= kernfs_iop_listxattr,
80b9bbefc   Tejun Heo   kernfs: add kernf...
1026
1027
1028
1029
  
  	.mkdir		= kernfs_iop_mkdir,
  	.rmdir		= kernfs_iop_rmdir,
  	.rename		= kernfs_iop_rename,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1030
  };
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1031
  static struct kernfs_node *kernfs_leftmost_descendant(struct kernfs_node *pos)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1032
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1033
  	struct kernfs_node *last;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1034
1035
1036
1037
1038
  
  	while (true) {
  		struct rb_node *rbn;
  
  		last = pos;
df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
1039
  		if (kernfs_type(pos) != KERNFS_DIR)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1040
  			break;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1041
  		rbn = rb_first(&pos->dir.children);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1042
1043
  		if (!rbn)
  			break;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1044
  		pos = rb_to_kn(rbn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1045
1046
1047
1048
1049
1050
  	}
  
  	return last;
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1051
   * kernfs_next_descendant_post - find the next descendant for post-order walk
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1052
   * @pos: the current position (%NULL to initiate traversal)
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1053
   * @root: kernfs_node whose descendants to walk
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1054
1055
1056
1057
1058
   *
   * Find the next descendant to visit for post-order traversal of @root's
   * descendants.  @root is included in the iteration and the last node to be
   * visited.
   */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1059
1060
  static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
  						       struct kernfs_node *root)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1061
1062
  {
  	struct rb_node *rbn;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1063
  	lockdep_assert_held(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1064
1065
1066
  
  	/* if first iteration, visit leftmost descendant which may be root */
  	if (!pos)
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1067
  		return kernfs_leftmost_descendant(root);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1068
1069
1070
1071
1072
1073
  
  	/* if we visited @root, we're done */
  	if (pos == root)
  		return NULL;
  
  	/* if there's an unvisited sibling, visit its leftmost descendant */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1074
  	rbn = rb_next(&pos->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1075
  	if (rbn)
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1076
  		return kernfs_leftmost_descendant(rb_to_kn(rbn));
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1077
1078
  
  	/* no sibling left, visit parent */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1079
  	return pos->parent;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1080
  }
d35258ef7   Tejun Heo   kernfs: allow nod...
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
  /**
   * kernfs_activate - activate a node which started deactivated
   * @kn: kernfs_node whose subtree is to be activated
   *
   * If the root has KERNFS_ROOT_CREATE_DEACTIVATED set, a newly created node
   * needs to be explicitly activated.  A node which hasn't been activated
   * isn't visible to userland and deactivation is skipped during its
   * removal.  This is useful to construct atomic init sequences where
   * creation of multiple nodes should either succeed or fail atomically.
   *
   * The caller is responsible for ensuring that this function is not called
   * after kernfs_remove*() is invoked on @kn.
   */
  void kernfs_activate(struct kernfs_node *kn)
  {
  	struct kernfs_node *pos;
  
  	mutex_lock(&kernfs_mutex);
  
  	pos = NULL;
  	while ((pos = kernfs_next_descendant_post(pos, kn))) {
  		if (!pos || (pos->flags & KERNFS_ACTIVATED))
  			continue;
  
  		WARN_ON_ONCE(pos->parent && RB_EMPTY_NODE(&pos->rb));
  		WARN_ON_ONCE(atomic_read(&pos->active) != KN_DEACTIVATED_BIAS);
  
  		atomic_sub(KN_DEACTIVATED_BIAS, &pos->active);
  		pos->flags |= KERNFS_ACTIVATED;
  	}
  
  	mutex_unlock(&kernfs_mutex);
  }
988cd7afb   Tejun Heo   kernfs: remove ke...
1114
  static void __kernfs_remove(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1115
  {
35beab063   Tejun Heo   kernfs: restructu...
1116
1117
1118
  	struct kernfs_node *pos;
  
  	lockdep_assert_held(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1119

6b0afc2a2   Tejun Heo   kernfs, sysfs, dr...
1120
1121
1122
1123
1124
1125
  	/*
  	 * Short-circuit if non-root @kn has already finished removal.
  	 * This is for kernfs_remove_self() which plays with active ref
  	 * after removal.
  	 */
  	if (!kn || (kn->parent && RB_EMPTY_NODE(&kn->rb)))
ce9b499c9   Greg Kroah-Hartman   Revert "kernfs: r...
1126
  		return;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1127
1128
  	pr_debug("kernfs %s: removing
  ", kn->name);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1129

81c173cb5   Tejun Heo   kernfs: remove KE...
1130
  	/* prevent any new usage under @kn by deactivating all nodes */
35beab063   Tejun Heo   kernfs: restructu...
1131
1132
  	pos = NULL;
  	while ((pos = kernfs_next_descendant_post(pos, kn)))
81c173cb5   Tejun Heo   kernfs: remove KE...
1133
1134
  		if (kernfs_active(pos))
  			atomic_add(KN_DEACTIVATED_BIAS, &pos->active);
35beab063   Tejun Heo   kernfs: restructu...
1135
1136
  
  	/* deactivate and unlink the subtree node-by-node */
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1137
  	do {
35beab063   Tejun Heo   kernfs: restructu...
1138
1139
1140
  		pos = kernfs_leftmost_descendant(kn);
  
  		/*
81c173cb5   Tejun Heo   kernfs: remove KE...
1141
1142
1143
1144
  		 * kernfs_drain() drops kernfs_mutex temporarily and @pos's
  		 * base ref could have been put by someone else by the time
  		 * the function returns.  Make sure it doesn't go away
  		 * underneath us.
35beab063   Tejun Heo   kernfs: restructu...
1145
1146
  		 */
  		kernfs_get(pos);
d35258ef7   Tejun Heo   kernfs: allow nod...
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
  		/*
  		 * Drain iff @kn was activated.  This avoids draining and
  		 * its lockdep annotations for nodes which have never been
  		 * activated and allows embedding kernfs_remove() in create
  		 * error paths without worrying about draining.
  		 */
  		if (kn->flags & KERNFS_ACTIVATED)
  			kernfs_drain(pos);
  		else
  			WARN_ON_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS);
35beab063   Tejun Heo   kernfs: restructu...
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
  
  		/*
  		 * kernfs_unlink_sibling() succeeds once per node.  Use it
  		 * to decide who's responsible for cleanups.
  		 */
  		if (!pos->parent || kernfs_unlink_sibling(pos)) {
  			struct kernfs_iattrs *ps_iattr =
  				pos->parent ? pos->parent->iattr : NULL;
  
  			/* update timestamps on the parent */
  			if (ps_iattr) {
3a3a5fece   Deepa Dinamani   fs: kernfs: Repla...
1168
1169
1170
  				ktime_get_real_ts(&ps_iattr->ia_iattr.ia_ctime);
  				ps_iattr->ia_iattr.ia_mtime =
  					ps_iattr->ia_iattr.ia_ctime;
35beab063   Tejun Heo   kernfs: restructu...
1171
  			}
988cd7afb   Tejun Heo   kernfs: remove ke...
1172
  			kernfs_put(pos);
35beab063   Tejun Heo   kernfs: restructu...
1173
1174
1175
1176
  		}
  
  		kernfs_put(pos);
  	} while (pos != kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1177
1178
1179
  }
  
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1180
1181
   * kernfs_remove - remove a kernfs_node recursively
   * @kn: the kernfs_node to remove
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1182
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1183
   * Remove @kn along with all its subdirectories and files.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1184
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1185
  void kernfs_remove(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1186
  {
988cd7afb   Tejun Heo   kernfs: remove ke...
1187
1188
1189
  	mutex_lock(&kernfs_mutex);
  	__kernfs_remove(kn);
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1190
1191
1192
  }
  
  /**
6b0afc2a2   Tejun Heo   kernfs, sysfs, dr...
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
   * kernfs_break_active_protection - break out of active protection
   * @kn: the self kernfs_node
   *
   * The caller must be running off of a kernfs operation which is invoked
   * with an active reference - e.g. one of kernfs_ops.  Each invocation of
   * this function must also be matched with an invocation of
   * kernfs_unbreak_active_protection().
   *
   * This function releases the active reference of @kn the caller is
   * holding.  Once this function is called, @kn may be removed at any point
   * and the caller is solely responsible for ensuring that the objects it
   * dereferences are accessible.
   */
  void kernfs_break_active_protection(struct kernfs_node *kn)
  {
  	/*
  	 * Take out ourself out of the active ref dependency chain.  If
  	 * we're called without an active ref, lockdep will complain.
  	 */
  	kernfs_put_active(kn);
  }
  
  /**
   * kernfs_unbreak_active_protection - undo kernfs_break_active_protection()
   * @kn: the self kernfs_node
   *
   * If kernfs_break_active_protection() was called, this function must be
   * invoked before finishing the kernfs operation.  Note that while this
   * function restores the active reference, it doesn't and can't actually
   * restore the active protection - @kn may already or be in the process of
   * being removed.  Once kernfs_break_active_protection() is invoked, that
   * protection is irreversibly gone for the kernfs operation instance.
   *
   * While this function may be called at any point after
   * kernfs_break_active_protection() is invoked, its most useful location
   * would be right before the enclosing kernfs operation returns.
   */
  void kernfs_unbreak_active_protection(struct kernfs_node *kn)
  {
  	/*
  	 * @kn->active could be in any state; however, the increment we do
  	 * here will be undone as soon as the enclosing kernfs operation
  	 * finishes and this temporary bump can't break anything.  If @kn
  	 * is alive, nothing changes.  If @kn is being deactivated, the
  	 * soon-to-follow put will either finish deactivation or restore
  	 * deactivated state.  If @kn is already removed, the temporary
  	 * bump is guaranteed to be gone before @kn is released.
  	 */
  	atomic_inc(&kn->active);
  	if (kernfs_lockdep(kn))
  		rwsem_acquire(&kn->dep_map, 0, 1, _RET_IP_);
  }
  
  /**
   * kernfs_remove_self - remove a kernfs_node from its own method
   * @kn: the self kernfs_node to remove
   *
   * The caller must be running off of a kernfs operation which is invoked
   * with an active reference - e.g. one of kernfs_ops.  This can be used to
   * implement a file operation which deletes itself.
   *
   * For example, the "delete" file for a sysfs device directory can be
   * implemented by invoking kernfs_remove_self() on the "delete" file
   * itself.  This function breaks the circular dependency of trying to
   * deactivate self while holding an active ref itself.  It isn't necessary
   * to modify the usual removal path to use kernfs_remove_self().  The
   * "delete" implementation can simply invoke kernfs_remove_self() on self
   * before proceeding with the usual removal path.  kernfs will ignore later
   * kernfs_remove() on self.
   *
   * kernfs_remove_self() can be called multiple times concurrently on the
   * same kernfs_node.  Only the first one actually performs removal and
   * returns %true.  All others will wait until the kernfs operation which
   * won self-removal finishes and return %false.  Note that the losers wait
   * for the completion of not only the winning kernfs_remove_self() but also
   * the whole kernfs_ops which won the arbitration.  This can be used to
   * guarantee, for example, all concurrent writes to a "delete" file to
   * finish only after the whole operation is complete.
   */
  bool kernfs_remove_self(struct kernfs_node *kn)
  {
  	bool ret;
  
  	mutex_lock(&kernfs_mutex);
  	kernfs_break_active_protection(kn);
  
  	/*
  	 * SUICIDAL is used to arbitrate among competing invocations.  Only
  	 * the first one will actually perform removal.  When the removal
  	 * is complete, SUICIDED is set and the active ref is restored
  	 * while holding kernfs_mutex.  The ones which lost arbitration
  	 * waits for SUICDED && drained which can happen only after the
  	 * enclosing kernfs operation which executed the winning instance
  	 * of kernfs_remove_self() finished.
  	 */
  	if (!(kn->flags & KERNFS_SUICIDAL)) {
  		kn->flags |= KERNFS_SUICIDAL;
  		__kernfs_remove(kn);
  		kn->flags |= KERNFS_SUICIDED;
  		ret = true;
  	} else {
  		wait_queue_head_t *waitq = &kernfs_root(kn)->deactivate_waitq;
  		DEFINE_WAIT(wait);
  
  		while (true) {
  			prepare_to_wait(waitq, &wait, TASK_UNINTERRUPTIBLE);
  
  			if ((kn->flags & KERNFS_SUICIDED) &&
  			    atomic_read(&kn->active) == KN_DEACTIVATED_BIAS)
  				break;
  
  			mutex_unlock(&kernfs_mutex);
  			schedule();
  			mutex_lock(&kernfs_mutex);
  		}
  		finish_wait(waitq, &wait);
  		WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb));
  		ret = false;
  	}
  
  	/*
  	 * This must be done while holding kernfs_mutex; otherwise, waiting
  	 * for SUICIDED && deactivated could finish prematurely.
  	 */
  	kernfs_unbreak_active_protection(kn);
  
  	mutex_unlock(&kernfs_mutex);
  	return ret;
  }
  
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1324
1325
1326
1327
   * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it
   * @parent: parent of the target
   * @name: name of the kernfs_node to remove
   * @ns: namespace tag of the kernfs_node to remove
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1328
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1329
1330
   * Look for the kernfs_node with @name and @ns under @parent and remove it.
   * Returns 0 on success, -ENOENT if such entry doesn't exist.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1331
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1332
  int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1333
1334
  			     const void *ns)
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1335
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1336

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1337
  	if (!parent) {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1338
1339
  		WARN(1, KERN_WARNING "kernfs: can not remove '%s', no directory
  ",
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1340
1341
1342
  			name);
  		return -ENOENT;
  	}
988cd7afb   Tejun Heo   kernfs: remove ke...
1343
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1344

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1345
1346
  	kn = kernfs_find_ns(parent, name, ns);
  	if (kn)
988cd7afb   Tejun Heo   kernfs: remove ke...
1347
  		__kernfs_remove(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1348

988cd7afb   Tejun Heo   kernfs: remove ke...
1349
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1350

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1351
  	if (kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1352
1353
1354
1355
1356
1357
1358
  		return 0;
  	else
  		return -ENOENT;
  }
  
  /**
   * kernfs_rename_ns - move and rename a kernfs_node
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1359
   * @kn: target node
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1360
1361
1362
1363
   * @new_parent: new parent to put @sd under
   * @new_name: new name
   * @new_ns: new namespace tag
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1364
  int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1365
1366
  		     const char *new_name, const void *new_ns)
  {
3eef34ad7   Tejun Heo   kernfs: implement...
1367
1368
  	struct kernfs_node *old_parent;
  	const char *old_name = NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1369
  	int error;
3eef34ad7   Tejun Heo   kernfs: implement...
1370
1371
1372
  	/* can't move or rename root */
  	if (!kn->parent)
  		return -EINVAL;
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1373
  	mutex_lock(&kernfs_mutex);
d0ae3d434   Tejun Heo   kernfs: add REMOV...
1374
  	error = -ENOENT;
ea015218f   Eric W. Biederman   kernfs: Add suppo...
1375
1376
  	if (!kernfs_active(kn) || !kernfs_active(new_parent) ||
  	    (new_parent->flags & KERNFS_EMPTY_DIR))
d0ae3d434   Tejun Heo   kernfs: add REMOV...
1377
  		goto out;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1378
  	error = 0;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1379
1380
  	if ((kn->parent == new_parent) && (kn->ns == new_ns) &&
  	    (strcmp(kn->name, new_name) == 0))
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1381
  		goto out;	/* nothing to rename */
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1382
1383
1384
  
  	error = -EEXIST;
  	if (kernfs_find_ns(new_parent, new_name, new_ns))
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1385
  		goto out;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1386

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1387
  	/* rename kernfs_node */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1388
  	if (strcmp(kn->name, new_name) != 0) {
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1389
  		error = -ENOMEM;
75287a677   Andrzej Hajda   kernfs: convert n...
1390
  		new_name = kstrdup_const(new_name, GFP_KERNEL);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1391
  		if (!new_name)
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1392
  			goto out;
3eef34ad7   Tejun Heo   kernfs: implement...
1393
1394
  	} else {
  		new_name = NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1395
1396
1397
1398
1399
  	}
  
  	/*
  	 * Move to the appropriate place in the appropriate directories rbtree.
  	 */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1400
  	kernfs_unlink_sibling(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1401
  	kernfs_get(new_parent);
3eef34ad7   Tejun Heo   kernfs: implement...
1402
1403
1404
1405
1406
  
  	/* rename_lock protects ->parent and ->name accessors */
  	spin_lock_irq(&kernfs_rename_lock);
  
  	old_parent = kn->parent;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1407
  	kn->parent = new_parent;
3eef34ad7   Tejun Heo   kernfs: implement...
1408
1409
1410
  
  	kn->ns = new_ns;
  	if (new_name) {
dfeb0750b   Tejun Heo   kernfs: remove KE...
1411
  		old_name = kn->name;
3eef34ad7   Tejun Heo   kernfs: implement...
1412
1413
1414
1415
  		kn->name = new_name;
  	}
  
  	spin_unlock_irq(&kernfs_rename_lock);
9561a8961   Tejun Heo   kernfs: fix hash ...
1416
  	kn->hash = kernfs_name_hash(kn->name, kn->ns);
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1417
  	kernfs_link_sibling(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1418

3eef34ad7   Tejun Heo   kernfs: implement...
1419
  	kernfs_put(old_parent);
75287a677   Andrzej Hajda   kernfs: convert n...
1420
  	kfree_const(old_name);
3eef34ad7   Tejun Heo   kernfs: implement...
1421

fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1422
  	error = 0;
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1423
   out:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1424
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1425
1426
  	return error;
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1427
  /* Relationship between s_mode and the DT_xxx types */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1428
  static inline unsigned char dt_type(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1429
  {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1430
  	return (kn->mode >> 12) & 15;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1431
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1432
  static int kernfs_dir_fop_release(struct inode *inode, struct file *filp)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1433
1434
1435
1436
  {
  	kernfs_put(filp->private_data);
  	return 0;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1437
  static struct kernfs_node *kernfs_dir_pos(const void *ns,
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1438
  	struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1439
1440
  {
  	if (pos) {
81c173cb5   Tejun Heo   kernfs: remove KE...
1441
  		int valid = kernfs_active(pos) &&
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1442
  			pos->parent == parent && hash == pos->hash;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1443
1444
1445
1446
1447
  		kernfs_put(pos);
  		if (!valid)
  			pos = NULL;
  	}
  	if (!pos && (hash > 1) && (hash < INT_MAX)) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1448
  		struct rb_node *node = parent->dir.children.rb_node;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1449
  		while (node) {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1450
  			pos = rb_to_kn(node);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1451

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1452
  			if (hash < pos->hash)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1453
  				node = node->rb_left;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1454
  			else if (hash > pos->hash)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1455
1456
1457
1458
1459
  				node = node->rb_right;
  			else
  				break;
  		}
  	}
b9c9dad0c   Tejun Heo   kernfs: add missi...
1460
1461
  	/* Skip over entries which are dying/dead or in the wrong namespace */
  	while (pos && (!kernfs_active(pos) || pos->ns != ns)) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1462
  		struct rb_node *node = rb_next(&pos->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1463
1464
1465
  		if (!node)
  			pos = NULL;
  		else
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1466
  			pos = rb_to_kn(node);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1467
1468
1469
  	}
  	return pos;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1470
  static struct kernfs_node *kernfs_dir_next_pos(const void *ns,
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1471
  	struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1472
  {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1473
  	pos = kernfs_dir_pos(ns, parent, ino, pos);
b9c9dad0c   Tejun Heo   kernfs: add missi...
1474
  	if (pos) {
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1475
  		do {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1476
  			struct rb_node *node = rb_next(&pos->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1477
1478
1479
  			if (!node)
  				pos = NULL;
  			else
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1480
  				pos = rb_to_kn(node);
b9c9dad0c   Tejun Heo   kernfs: add missi...
1481
1482
  		} while (pos && (!kernfs_active(pos) || pos->ns != ns));
  	}
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1483
1484
  	return pos;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1485
  static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1486
1487
  {
  	struct dentry *dentry = file->f_path.dentry;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1488
1489
  	struct kernfs_node *parent = dentry->d_fsdata;
  	struct kernfs_node *pos = file->private_data;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1490
1491
1492
1493
  	const void *ns = NULL;
  
  	if (!dir_emit_dots(file, ctx))
  		return 0;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1494
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1495

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1496
  	if (kernfs_ns_enabled(parent))
c525aaddc   Tejun Heo   kernfs: s/sysfs/k...
1497
  		ns = kernfs_info(dentry->d_sb)->ns;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1498

c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1499
  	for (pos = kernfs_dir_pos(ns, parent, ctx->pos, pos);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1500
  	     pos;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1501
  	     pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1502
  		const char *name = pos->name;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1503
1504
  		unsigned int type = dt_type(pos);
  		int len = strlen(name);
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1505
  		ino_t ino = pos->ino;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1506

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1507
  		ctx->pos = pos->hash;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1508
1509
  		file->private_data = pos;
  		kernfs_get(pos);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1510
  		mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1511
1512
  		if (!dir_emit(ctx, name, len, ino, type))
  			return 0;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1513
  		mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1514
  	}
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1515
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1516
1517
1518
1519
  	file->private_data = NULL;
  	ctx->pos = INT_MAX;
  	return 0;
  }
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1520
  const struct file_operations kernfs_dir_fops = {
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1521
  	.read		= generic_read_dir,
8cb0d2c1c   Al Viro   kernfs: no point ...
1522
  	.iterate_shared	= kernfs_fop_readdir,
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1523
  	.release	= kernfs_dir_fop_release,
8cb0d2c1c   Al Viro   kernfs: no point ...
1524
  	.llseek		= generic_file_llseek,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1525
  };