Blame view

fs/kernfs/dir.c 42.4 KB
55716d264   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
b8441ed27   Tejun Heo   sysfs, kernfs: ad...
2
3
4
5
6
7
  /*
   * 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>
b8441ed27   Tejun Heo   sysfs, kernfs: ad...
8
   */
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
9

abd54f028   Tejun Heo   kernfs: replace k...
10
  #include <linux/sched.h>
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
11
12
13
14
15
16
17
18
  #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...
19
  DEFINE_MUTEX(kernfs_mutex);
3eef34ad7   Tejun Heo   kernfs: implement...
20
21
  static DEFINE_SPINLOCK(kernfs_rename_lock);	/* kn->parent and ->name */
  static char kernfs_pr_cont_buf[PATH_MAX];	/* protected by rename_lock */
7d35079f8   Shaohua Li   kernfs: use idr i...
22
  static DEFINE_SPINLOCK(kernfs_idr_lock);	/* root->ino_idr */
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
  static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen)
  {
17627157c   Konstantin Khlebnikov   kernfs: handle nu...
41
42
  	if (!kn)
  		return strlcpy(buf, "(null)", buflen);
3eef34ad7   Tejun Heo   kernfs: implement...
43
44
  	return strlcpy(buf, kn->parent ? kn->name : "/", buflen);
  }
9f6df573a   Aditya Kali   kernfs: Add API t...
45
46
  /* 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...
47
  {
9f6df573a   Aditya Kali   kernfs: Add API t...
48
  	size_t depth = 0;
3eef34ad7   Tejun Heo   kernfs: implement...
49

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

9f6df573a   Aditya Kali   kernfs: Add API t...
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
109
110
  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:  /../..
   *
17627157c   Konstantin Khlebnikov   kernfs: handle nu...
111
112
   * [3] when @kn_to is NULL result will be "(null)"
   *
3abb1d90f   Tejun Heo   kernfs: make kern...
113
114
115
   * 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...
116
117
118
119
120
121
122
   */
  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...
123
124
  	size_t depth_from, depth_to, len = 0;
  	int i, j;
9f6df573a   Aditya Kali   kernfs: Add API t...
125

17627157c   Konstantin Khlebnikov   kernfs: handle nu...
126
127
  	if (!kn_to)
  		return strlcpy(buf, "(null)", buflen);
9f6df573a   Aditya Kali   kernfs: Add API t...
128
129
130
131
132
  	if (!kn_from)
  		kn_from = kernfs_root(kn_to)->kn;
  
  	if (kn_from == kn_to)
  		return strlcpy(buf, "/", buflen);
bbe70e4e4   Jia-Ju Bai   fs: kernfs: Fix p...
133
134
  	if (!buf)
  		return -EINVAL;
9f6df573a   Aditya Kali   kernfs: Add API t...
135
136
  	common = kernfs_common_ancestor(kn_from, kn_to);
  	if (WARN_ON(!common))
3abb1d90f   Tejun Heo   kernfs: make kern...
137
  		return -EINVAL;
9f6df573a   Aditya Kali   kernfs: Add API t...
138
139
140
  
  	depth_to = kernfs_depth(common, kn_to);
  	depth_from = kernfs_depth(common, kn_from);
bbe70e4e4   Jia-Ju Bai   fs: kernfs: Fix p...
141
  	buf[0] = '\0';
9f6df573a   Aditya Kali   kernfs: Add API t...
142
143
144
145
146
147
  
  	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...
148
149
150
151
152
153
154
  	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...
155
  	}
3eef34ad7   Tejun Heo   kernfs: implement...
156

3abb1d90f   Tejun Heo   kernfs: make kern...
157
  	return len;
3eef34ad7   Tejun Heo   kernfs: implement...
158
159
160
161
162
163
164
165
166
167
168
169
  }
  
  /**
   * 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.
   *
17627157c   Konstantin Khlebnikov   kernfs: handle nu...
170
171
   * Fills buffer with "(null)" if @kn is NULL.
   *
3eef34ad7   Tejun Heo   kernfs: implement...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
   * 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...
186
187
188
189
190
191
192
193
194
195
196
   * 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...
197
198
199
   * 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...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
   */
  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...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
   * 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...
241
  	int sz;
3eef34ad7   Tejun Heo   kernfs: implement...
242
243
  
  	spin_lock_irqsave(&kernfs_rename_lock, flags);
9f6df573a   Aditya Kali   kernfs: Add API t...
244
245
246
247
248
249
250
251
252
253
254
255
256
  	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...
257

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

fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
353
  	/* add new node and rebalance the tree */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
354
355
  	rb_link_node(&kn->rb, parent, node);
  	rb_insert_color(&kn->rb, &kn->parent->dir.children);
c1befb885   Jianyu Zhan   kernfs: fix a sub...
356
357
358
359
  
  	/* successfully added, account subdir number */
  	if (kernfs_type(kn) == KERNFS_DIR)
  		kn->parent->dir.subdirs++;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
360
361
362
363
  	return 0;
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
364
   *	kernfs_unlink_sibling - unlink kernfs_node from sibling rbtree
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
365
   *	@kn: kernfs_node of interest
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
366
   *
35beab063   Tejun Heo   kernfs: restructu...
367
368
369
   *	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...
370
371
   *
   *	Locking:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
372
   *	mutex_lock(kernfs_mutex)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
373
   */
35beab063   Tejun Heo   kernfs: restructu...
374
  static bool kernfs_unlink_sibling(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
375
  {
35beab063   Tejun Heo   kernfs: restructu...
376
377
  	if (RB_EMPTY_NODE(&kn->rb))
  		return false;
df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
378
  	if (kernfs_type(kn) == KERNFS_DIR)
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
379
  		kn->parent->dir.subdirs--;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
380

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

182fd64b6   Tejun Heo   kernfs: remove KE...
403
  	if (kernfs_lockdep(kn))
f4b3e631b   Greg Kroah-Hartman   Revert "kernfs: m...
404
405
  		rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_);
  	return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
406
407
408
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
409
   *	kernfs_put_active - put an active reference to kernfs_node
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
410
   *	@kn: kernfs_node to put an active reference to
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
411
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
412
   *	Put an active reference to @kn.  This function is noop if @kn
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
413
414
   *	is NULL.
   */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
415
  void kernfs_put_active(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
416
417
  {
  	int v;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
418
  	if (unlikely(!kn))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
419
  		return;
182fd64b6   Tejun Heo   kernfs: remove KE...
420
  	if (kernfs_lockdep(kn))
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
421
  		rwsem_release(&kn->dep_map, 1, _RET_IP_);
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
422
  	v = atomic_dec_return(&kn->active);
df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
423
  	if (likely(v != KN_DEACTIVATED_BIAS))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
424
  		return;
2fd60da46   Peng Wang   kernfs: fix poten...
425
  	wake_up_all(&kernfs_root(kn)->deactivate_waitq);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
426
427
428
  }
  
  /**
81c173cb5   Tejun Heo   kernfs: remove KE...
429
430
   * kernfs_drain - drain kernfs_node
   * @kn: kernfs_node to drain
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
431
   *
81c173cb5   Tejun Heo   kernfs: remove KE...
432
433
434
   * 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...
435
   */
81c173cb5   Tejun Heo   kernfs: remove KE...
436
  static void kernfs_drain(struct kernfs_node *kn)
35beab063   Tejun Heo   kernfs: restructu...
437
  	__releases(&kernfs_mutex) __acquires(&kernfs_mutex)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
438
  {
abd54f028   Tejun Heo   kernfs: replace k...
439
  	struct kernfs_root *root = kernfs_root(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
440

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

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

182fd64b6   Tejun Heo   kernfs: remove KE...
446
  	if (kernfs_lockdep(kn)) {
35beab063   Tejun Heo   kernfs: restructu...
447
448
449
450
  		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...
451

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

182fd64b6   Tejun Heo   kernfs: remove KE...
456
  	if (kernfs_lockdep(kn)) {
a6607930b   Tejun Heo   kernfs: make kern...
457
458
459
  		lock_acquired(&kn->dep_map, _RET_IP_);
  		rwsem_release(&kn->dep_map, 1, _RET_IP_);
  	}
35beab063   Tejun Heo   kernfs: restructu...
460

0e67db2f9   Tejun Heo   kernfs: add kernf...
461
  	kernfs_drain_open_files(kn);
ccf02aaf8   Tejun Heo   kernfs: invoke ke...
462

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

ba16b2846   Shaohua Li   kernfs: add an AP...
489
490
491
492
  	/*
  	 * kernfs_node is freed with ->count 0, kernfs_find_and_get_node_by_ino
  	 * depends on this to filter reused stale node
  	 */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
493
  	if (!kn || !atomic_dec_and_test(&kn->count))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
494
  		return;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
495
  	root = kernfs_root(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
496
   repeat:
81c173cb5   Tejun Heo   kernfs: remove KE...
497
498
  	/*
  	 * Moving/renaming is always done while holding reference.
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
499
  	 * kn->parent won't change beneath us.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
500
  	 */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
501
  	parent = kn->parent;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
502

81c173cb5   Tejun Heo   kernfs: remove KE...
503
504
505
506
  	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...
507

df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
508
  	if (kernfs_type(kn) == KERNFS_LINK)
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
509
  		kernfs_put(kn->symlink.target_kn);
dfeb0750b   Tejun Heo   kernfs: remove KE...
510
511
  
  	kfree_const(kn->name);
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
512
  	if (kn->iattr) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
513
  		simple_xattrs_free(&kn->iattr->xattrs);
26e28d68b   Ayush Mittal   kernfs: Allocatin...
514
  		kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
2322392b0   Tejun Heo   kernfs: implement...
515
  	}
7d35079f8   Shaohua Li   kernfs: use idr i...
516
  	spin_lock(&kernfs_idr_lock);
c53cd490b   Shaohua Li   kernfs: introduce...
517
  	idr_remove(&root->ino_idr, kn->id.ino);
7d35079f8   Shaohua Li   kernfs: use idr i...
518
  	spin_unlock(&kernfs_idr_lock);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
519
  	kmem_cache_free(kernfs_node_cache, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
520

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
521
522
  	kn = parent;
  	if (kn) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
523
  		if (atomic_dec_and_test(&kn->count))
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
524
525
  			goto repeat;
  	} else {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
526
  		/* just released the root kn, free @root too */
7d35079f8   Shaohua Li   kernfs: use idr i...
527
  		idr_destroy(&root->ino_idr);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
528
529
  		kfree(root);
  	}
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
530
531
  }
  EXPORT_SYMBOL_GPL(kernfs_put);
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
532
  static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
533
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
534
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
535
536
537
  
  	if (flags & LOOKUP_RCU)
  		return -ECHILD;
19bbb9262   Tejun Heo   kernfs: allow neg...
538
  	/* Always perform fresh lookup for negatives */
2b0143b5c   David Howells   VFS: normal files...
539
  	if (d_really_is_negative(dentry))
19bbb9262   Tejun Heo   kernfs: allow neg...
540
  		goto out_bad_unlocked;
319ba91d3   Shaohua Li   kernfs: don't set...
541
  	kn = kernfs_dentry_node(dentry);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
542
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
543

81c173cb5   Tejun Heo   kernfs: remove KE...
544
545
  	/* The kernfs node has been deactivated */
  	if (!kernfs_active(kn))
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
546
  		goto out_bad;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
547
  	/* The kernfs node has been moved? */
319ba91d3   Shaohua Li   kernfs: don't set...
548
  	if (kernfs_dentry_node(dentry->d_parent) != kn->parent)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
549
  		goto out_bad;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
550
  	/* The kernfs node has been renamed */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
551
  	if (strcmp(dentry->d_name.name, kn->name) != 0)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
552
  		goto out_bad;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
553
  	/* The kernfs node has been moved to a different namespace */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
554
  	if (kn->parent && kernfs_ns_enabled(kn->parent) &&
c525aaddc   Tejun Heo   kernfs: s/sysfs/k...
555
  	    kernfs_info(dentry->d_sb)->ns != kn->ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
556
  		goto out_bad;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
557
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
558
559
  	return 1;
  out_bad:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
560
  	mutex_unlock(&kernfs_mutex);
19bbb9262   Tejun Heo   kernfs: allow neg...
561
  out_bad_unlocked:
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
562
563
  	return 0;
  }
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
564
  const struct dentry_operations kernfs_dops = {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
565
  	.d_revalidate	= kernfs_dop_revalidate,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
566
  };
0c23b2259   Tejun Heo   kernfs: implement...
567
568
569
570
571
572
573
574
575
576
577
578
579
  /**
   * 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)
  {
319ba91d3   Shaohua Li   kernfs: don't set...
580
581
582
  	if (dentry->d_sb->s_op == &kernfs_sops &&
  	    !d_really_is_negative(dentry))
  		return kernfs_dentry_node(dentry);
0c23b2259   Tejun Heo   kernfs: implement...
583
584
  	return NULL;
  }
db4aad209   Tejun Heo   kernfs: associate...
585
  static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
e19dfdc83   Ondrej Mosnacek   kernfs: initializ...
586
  					     struct kernfs_node *parent,
db4aad209   Tejun Heo   kernfs: associate...
587
  					     const char *name, umode_t mode,
488dee96b   Dmitry Torokhov   kernfs: allow cre...
588
  					     kuid_t uid, kgid_t gid,
db4aad209   Tejun Heo   kernfs: associate...
589
  					     unsigned flags)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
590
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
591
  	struct kernfs_node *kn;
4a3ef68ac   Shaohua Li   kernfs: implement...
592
  	u32 gen;
bc755553d   Tejun Heo   sysfs, kernfs: ma...
593
  	int ret;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
594

dfeb0750b   Tejun Heo   kernfs: remove KE...
595
596
597
  	name = kstrdup_const(name, GFP_KERNEL);
  	if (!name)
  		return NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
598

a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
599
  	kn = kmem_cache_zalloc(kernfs_node_cache, GFP_KERNEL);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
600
  	if (!kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
601
  		goto err_out1;
7d35079f8   Shaohua Li   kernfs: use idr i...
602
603
  	idr_preload(GFP_KERNEL);
  	spin_lock(&kernfs_idr_lock);
4a3ef68ac   Shaohua Li   kernfs: implement...
604
  	ret = idr_alloc_cyclic(&root->ino_idr, kn, 1, 0, GFP_ATOMIC);
01e7ab5b9   Tejun Heo   kernfs: fix ino w...
605
  	if (ret >= 0 && ret < root->last_ino)
4a3ef68ac   Shaohua Li   kernfs: implement...
606
607
  		root->next_generation++;
  	gen = root->next_generation;
01e7ab5b9   Tejun Heo   kernfs: fix ino w...
608
  	root->last_ino = ret;
7d35079f8   Shaohua Li   kernfs: use idr i...
609
610
  	spin_unlock(&kernfs_idr_lock);
  	idr_preload_end();
bc755553d   Tejun Heo   sysfs, kernfs: ma...
611
  	if (ret < 0)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
612
  		goto err_out2;
c53cd490b   Shaohua Li   kernfs: introduce...
613
614
  	kn->id.ino = ret;
  	kn->id.generation = gen;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
615

ba16b2846   Shaohua Li   kernfs: add an AP...
616
  	/*
998267900   Andrea Parri   kernfs: fix barri...
617
  	 * set ino first. This RELEASE is paired with atomic_inc_not_zero in
ba16b2846   Shaohua Li   kernfs: add an AP...
618
619
  	 * kernfs_find_and_get_node_by_ino
  	 */
998267900   Andrea Parri   kernfs: fix barri...
620
  	atomic_set_release(&kn->count, 1);
81c173cb5   Tejun Heo   kernfs: remove KE...
621
  	atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
35beab063   Tejun Heo   kernfs: restructu...
622
  	RB_CLEAR_NODE(&kn->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
623

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
624
625
  	kn->name = name;
  	kn->mode = mode;
81c173cb5   Tejun Heo   kernfs: remove KE...
626
  	kn->flags = flags;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
627

488dee96b   Dmitry Torokhov   kernfs: allow cre...
628
629
630
631
632
633
634
635
636
637
638
  	if (!uid_eq(uid, GLOBAL_ROOT_UID) || !gid_eq(gid, GLOBAL_ROOT_GID)) {
  		struct iattr iattr = {
  			.ia_valid = ATTR_UID | ATTR_GID,
  			.ia_uid = uid,
  			.ia_gid = gid,
  		};
  
  		ret = __kernfs_setattr(kn, &iattr);
  		if (ret < 0)
  			goto err_out3;
  	}
e19dfdc83   Ondrej Mosnacek   kernfs: initializ...
639
640
641
642
643
  	if (parent) {
  		ret = security_kernfs_init_security(parent, kn);
  		if (ret)
  			goto err_out3;
  	}
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
644
  	return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
645

488dee96b   Dmitry Torokhov   kernfs: allow cre...
646
647
   err_out3:
  	idr_remove(&root->ino_idr, kn->id.ino);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
648
   err_out2:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
649
  	kmem_cache_free(kernfs_node_cache, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
650
   err_out1:
dfeb0750b   Tejun Heo   kernfs: remove KE...
651
  	kfree_const(name);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
652
653
  	return NULL;
  }
db4aad209   Tejun Heo   kernfs: associate...
654
655
  struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
  				    const char *name, umode_t mode,
488dee96b   Dmitry Torokhov   kernfs: allow cre...
656
  				    kuid_t uid, kgid_t gid,
db4aad209   Tejun Heo   kernfs: associate...
657
658
659
  				    unsigned flags)
  {
  	struct kernfs_node *kn;
e19dfdc83   Ondrej Mosnacek   kernfs: initializ...
660
  	kn = __kernfs_new_node(kernfs_root(parent), parent,
488dee96b   Dmitry Torokhov   kernfs: allow cre...
661
  			       name, mode, uid, gid, flags);
db4aad209   Tejun Heo   kernfs: associate...
662
663
664
665
666
667
  	if (kn) {
  		kernfs_get(parent);
  		kn->parent = parent;
  	}
  	return kn;
  }
ba16b2846   Shaohua Li   kernfs: add an AP...
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  /*
   * kernfs_find_and_get_node_by_ino - get kernfs_node from inode number
   * @root: the kernfs root
   * @ino: inode number
   *
   * RETURNS:
   * NULL on failure. Return a kernfs node with reference counter incremented
   */
  struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root,
  						    unsigned int ino)
  {
  	struct kernfs_node *kn;
  
  	rcu_read_lock();
  	kn = idr_find(&root->ino_idr, ino);
  	if (!kn)
  		goto out;
  
  	/*
  	 * Since kernfs_node is freed in RCU, it's possible an old node for ino
  	 * is freed, but reused before RCU grace period. But a freed node (see
  	 * kernfs_put) or an incompletedly initialized node (see
  	 * __kernfs_new_node) should have 'count' 0. We can use this fact to
  	 * filter out such node.
  	 */
  	if (!atomic_inc_not_zero(&kn->count)) {
  		kn = NULL;
  		goto out;
  	}
  
  	/*
  	 * The node could be a new node or a reused node. If it's a new node,
  	 * we are ok. If it's reused because of RCU (because of
  	 * SLAB_TYPESAFE_BY_RCU), the __kernfs_new_node always sets its 'ino'
  	 * before 'count'. So if 'count' is uptodate, 'ino' should be uptodate,
  	 * hence we can use 'ino' to filter stale node.
  	 */
c53cd490b   Shaohua Li   kernfs: introduce...
705
  	if (kn->id.ino != ino)
ba16b2846   Shaohua Li   kernfs: add an AP...
706
707
708
709
710
711
712
713
714
  		goto out;
  	rcu_read_unlock();
  
  	return kn;
  out:
  	rcu_read_unlock();
  	kernfs_put(kn);
  	return NULL;
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
715
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
716
   *	kernfs_add_one - add kernfs_node to parent without warning
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
717
   *	@kn: kernfs_node to be added
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
718
   *
db4aad209   Tejun Heo   kernfs: associate...
719
720
721
   *	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...
722
   *
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
723
724
725
726
   *	RETURNS:
   *	0 on success, -EEXIST if entry with the given name already
   *	exists.
   */
988cd7afb   Tejun Heo   kernfs: remove ke...
727
  int kernfs_add_one(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
728
  {
db4aad209   Tejun Heo   kernfs: associate...
729
  	struct kernfs_node *parent = kn->parent;
c525aaddc   Tejun Heo   kernfs: s/sysfs/k...
730
  	struct kernfs_iattrs *ps_iattr;
988cd7afb   Tejun Heo   kernfs: remove ke...
731
  	bool has_ns;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
732
  	int ret;
988cd7afb   Tejun Heo   kernfs: remove ke...
733
734
735
736
737
738
739
740
  	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...
741

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

988cd7afb   Tejun Heo   kernfs: remove ke...
745
  	ret = -ENOENT;
ea015218f   Eric W. Biederman   kernfs: Add suppo...
746
747
  	if (parent->flags & KERNFS_EMPTY_DIR)
  		goto out_unlock;
d35258ef7   Tejun Heo   kernfs: allow nod...
748
  	if ((parent->flags & KERNFS_ACTIVATED) && !kernfs_active(parent))
988cd7afb   Tejun Heo   kernfs: remove ke...
749
  		goto out_unlock;
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
750

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

c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
753
  	ret = kernfs_link_sibling(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
754
  	if (ret)
988cd7afb   Tejun Heo   kernfs: remove ke...
755
  		goto out_unlock;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
756
757
  
  	/* Update timestamps on the parent */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
758
  	ps_iattr = parent->iattr;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
759
  	if (ps_iattr) {
058952196   Ondrej Mosnacek   kernfs: clean up ...
760
761
  		ktime_get_real_ts64(&ps_iattr->ia_ctime);
  		ps_iattr->ia_mtime = ps_iattr->ia_ctime;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
762
  	}
d35258ef7   Tejun Heo   kernfs: allow nod...
763
764
765
766
767
768
769
770
771
772
773
774
  	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...
775
  out_unlock:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
776
  	mutex_unlock(&kernfs_mutex);
988cd7afb   Tejun Heo   kernfs: remove ke...
777
  	return ret;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
778
779
780
  }
  
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
781
782
   * kernfs_find_ns - find kernfs_node with the given name
   * @parent: kernfs_node to search under
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
783
784
785
   * @name: name to look for
   * @ns: the namespace tag to use
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
786
787
   * 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...
788
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
789
790
791
  static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
  					  const unsigned char *name,
  					  const void *ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
792
  {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
793
  	struct rb_node *node = parent->dir.children.rb_node;
ac9bba031   Tejun Heo   sysfs, kernfs: im...
794
  	bool has_ns = kernfs_ns_enabled(parent);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
795
  	unsigned int hash;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
796
  	lockdep_assert_held(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
797
798
  
  	if (has_ns != (bool)ns) {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
799
800
  		WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'
  ",
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
801
  		     has_ns ? "required" : "invalid", parent->name, name);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
802
803
  		return NULL;
  	}
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
804
  	hash = kernfs_name_hash(name, ns);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
805
  	while (node) {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
806
  		struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
807
  		int result;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
808
  		kn = rb_to_kn(node);
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
809
  		result = kernfs_name_compare(hash, name, ns, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
810
811
812
813
814
  		if (result < 0)
  			node = node->rb_left;
  		else if (result > 0)
  			node = node->rb_right;
  		else
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
815
  			return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
816
817
818
  	}
  	return NULL;
  }
bd96f76a2   Tejun Heo   kernfs: implement...
819
820
821
822
  static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
  					  const unsigned char *path,
  					  const void *ns)
  {
e56ed358a   Tejun Heo   kernfs: make kern...
823
824
  	size_t len;
  	char *p, *name;
bd96f76a2   Tejun Heo   kernfs: implement...
825
826
  
  	lockdep_assert_held(&kernfs_mutex);
e56ed358a   Tejun Heo   kernfs: make kern...
827
828
829
830
831
832
833
  	/* 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...
834
  		return NULL;
e56ed358a   Tejun Heo   kernfs: make kern...
835
836
837
  	}
  
  	p = kernfs_pr_cont_buf;
bd96f76a2   Tejun Heo   kernfs: implement...
838
839
840
841
842
843
  
  	while ((name = strsep(&p, "/")) && parent) {
  		if (*name == '\0')
  			continue;
  		parent = kernfs_find_ns(parent, name, ns);
  	}
e56ed358a   Tejun Heo   kernfs: make kern...
844
  	spin_unlock_irq(&kernfs_rename_lock);
bd96f76a2   Tejun Heo   kernfs: implement...
845
846
  	return parent;
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
847
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
848
849
   * 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...
850
851
852
   * @name: name to look for
   * @ns: the namespace tag to use
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
853
   * Look for kernfs_node with name @name under @parent and get a reference
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
854
   * if found.  This function may sleep and returns pointer to the found
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
855
   * kernfs_node on success, %NULL on failure.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
856
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
857
858
  struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
  					   const char *name, const void *ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
859
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
860
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
861

a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
862
  	mutex_lock(&kernfs_mutex);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
863
864
  	kn = kernfs_find_ns(parent, name, ns);
  	kernfs_get(kn);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
865
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
866

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
867
  	return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
868
869
870
871
  }
  EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
  
  /**
bd96f76a2   Tejun Heo   kernfs: implement...
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
   * 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...
895
   * kernfs_create_root - create a new kernfs hierarchy
90c07c895   Tejun Heo   kernfs: rename ke...
896
   * @scops: optional syscall operations for the hierarchy
d35258ef7   Tejun Heo   kernfs: allow nod...
897
   * @flags: KERNFS_ROOT_* flags
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
898
899
900
901
902
   * @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...
903
  struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
d35258ef7   Tejun Heo   kernfs: allow nod...
904
  				       unsigned int flags, void *priv)
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
905
906
  {
  	struct kernfs_root *root;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
907
  	struct kernfs_node *kn;
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
908
909
910
911
  
  	root = kzalloc(sizeof(*root), GFP_KERNEL);
  	if (!root)
  		return ERR_PTR(-ENOMEM);
7d35079f8   Shaohua Li   kernfs: use idr i...
912
  	idr_init(&root->ino_idr);
7d568a838   Tejun Heo   kernfs: implement...
913
  	INIT_LIST_HEAD(&root->supers);
4a3ef68ac   Shaohua Li   kernfs: implement...
914
  	root->next_generation = 1;
bc755553d   Tejun Heo   sysfs, kernfs: ma...
915

e19dfdc83   Ondrej Mosnacek   kernfs: initializ...
916
  	kn = __kernfs_new_node(root, NULL, "", S_IFDIR | S_IRUGO | S_IXUGO,
488dee96b   Dmitry Torokhov   kernfs: allow cre...
917
  			       GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
db4aad209   Tejun Heo   kernfs: associate...
918
  			       KERNFS_DIR);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
919
  	if (!kn) {
7d35079f8   Shaohua Li   kernfs: use idr i...
920
  		idr_destroy(&root->ino_idr);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
921
922
923
  		kfree(root);
  		return ERR_PTR(-ENOMEM);
  	}
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
924
  	kn->priv = priv;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
925
  	kn->dir.root = root;
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
926

90c07c895   Tejun Heo   kernfs: rename ke...
927
  	root->syscall_ops = scops;
d35258ef7   Tejun Heo   kernfs: allow nod...
928
  	root->flags = flags;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
929
  	root->kn = kn;
abd54f028   Tejun Heo   kernfs: replace k...
930
  	init_waitqueue_head(&root->deactivate_waitq);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
931

d35258ef7   Tejun Heo   kernfs: allow nod...
932
933
  	if (!(root->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
  		kernfs_activate(kn);
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
934
935
936
937
938
939
940
941
942
943
944
945
  	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...
946
  	kernfs_remove(root->kn);	/* will also free @root */
ba7443bc6   Tejun Heo   sysfs, kernfs: im...
947
948
949
  }
  
  /**
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
950
951
952
   * 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...
953
   * @mode: mode of the new directory
488dee96b   Dmitry Torokhov   kernfs: allow cre...
954
955
   * @uid: uid of the new directory
   * @gid: gid of the new directory
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
956
957
958
959
960
   * @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...
961
  struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
bb8b9d095   Tejun Heo   kernfs: add @mode...
962
  					 const char *name, umode_t mode,
488dee96b   Dmitry Torokhov   kernfs: allow cre...
963
  					 kuid_t uid, kgid_t gid,
bb8b9d095   Tejun Heo   kernfs: add @mode...
964
  					 void *priv, const void *ns)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
965
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
966
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
967
968
969
  	int rc;
  
  	/* allocate */
488dee96b   Dmitry Torokhov   kernfs: allow cre...
970
971
  	kn = kernfs_new_node(parent, name, mode | S_IFDIR,
  			     uid, gid, KERNFS_DIR);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
972
  	if (!kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
973
  		return ERR_PTR(-ENOMEM);
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
974
975
  	kn->dir.root = parent->dir.root;
  	kn->ns = ns;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
976
  	kn->priv = priv;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
977
978
  
  	/* link in */
988cd7afb   Tejun Heo   kernfs: remove ke...
979
  	rc = kernfs_add_one(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
980
  	if (!rc)
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
981
  		return kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
982

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
983
  	kernfs_put(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
984
985
  	return ERR_PTR(rc);
  }
ea015218f   Eric W. Biederman   kernfs: Add suppo...
986
987
988
989
990
991
992
993
994
995
996
997
998
999
  /**
   * 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 */
488dee96b   Dmitry Torokhov   kernfs: allow cre...
1000
1001
  	kn = kernfs_new_node(parent, name, S_IRUGO|S_IXUGO|S_IFDIR,
  			     GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, KERNFS_DIR);
ea015218f   Eric W. Biederman   kernfs: Add suppo...
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
  	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...
1018
1019
1020
  static struct dentry *kernfs_iop_lookup(struct inode *dir,
  					struct dentry *dentry,
  					unsigned int flags)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1021
  {
19bbb9262   Tejun Heo   kernfs: allow neg...
1022
  	struct dentry *ret;
319ba91d3   Shaohua Li   kernfs: don't set...
1023
  	struct kernfs_node *parent = dir->i_private;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1024
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1025
1026
  	struct inode *inode;
  	const void *ns = NULL;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1027
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1028

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

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1032
  	kn = kernfs_find_ns(parent, dentry->d_name.name, ns);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1033
1034
  
  	/* no such entry */
b9c9dad0c   Tejun Heo   kernfs: add missi...
1035
  	if (!kn || !kernfs_active(kn)) {
19bbb9262   Tejun Heo   kernfs: allow neg...
1036
  		ret = NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1037
1038
  		goto out_unlock;
  	}
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1039
1040
  
  	/* attach dentry and inode */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1041
  	inode = kernfs_get_inode(dir->i_sb, kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1042
1043
1044
1045
1046
1047
  	if (!inode) {
  		ret = ERR_PTR(-ENOMEM);
  		goto out_unlock;
  	}
  
  	/* instantiate and hash dentry */
41d28bca2   Al Viro   switch d_material...
1048
  	ret = d_splice_alias(inode, dentry);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1049
   out_unlock:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1050
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1051
1052
  	return ret;
  }
80b9bbefc   Tejun Heo   kernfs: add kernf...
1053
1054
1055
1056
  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...
1057
  	struct kernfs_syscall_ops *scops = kernfs_root(parent)->syscall_ops;
07c7530dd   Tejun Heo   kernfs: invoke di...
1058
  	int ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1059

90c07c895   Tejun Heo   kernfs: rename ke...
1060
  	if (!scops || !scops->mkdir)
80b9bbefc   Tejun Heo   kernfs: add kernf...
1061
  		return -EPERM;
07c7530dd   Tejun Heo   kernfs: invoke di...
1062
1063
  	if (!kernfs_get_active(parent))
  		return -ENODEV;
90c07c895   Tejun Heo   kernfs: rename ke...
1064
  	ret = scops->mkdir(parent, dentry->d_name.name, mode);
07c7530dd   Tejun Heo   kernfs: invoke di...
1065
1066
1067
  
  	kernfs_put_active(parent);
  	return ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1068
1069
1070
1071
  }
  
  static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
  {
319ba91d3   Shaohua Li   kernfs: don't set...
1072
  	struct kernfs_node *kn  = kernfs_dentry_node(dentry);
90c07c895   Tejun Heo   kernfs: rename ke...
1073
  	struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
07c7530dd   Tejun Heo   kernfs: invoke di...
1074
  	int ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1075

90c07c895   Tejun Heo   kernfs: rename ke...
1076
  	if (!scops || !scops->rmdir)
80b9bbefc   Tejun Heo   kernfs: add kernf...
1077
  		return -EPERM;
07c7530dd   Tejun Heo   kernfs: invoke di...
1078
1079
  	if (!kernfs_get_active(kn))
  		return -ENODEV;
90c07c895   Tejun Heo   kernfs: rename ke...
1080
  	ret = scops->rmdir(kn);
07c7530dd   Tejun Heo   kernfs: invoke di...
1081
1082
1083
  
  	kernfs_put_active(kn);
  	return ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1084
1085
1086
  }
  
  static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
1cd66c93b   Miklos Szeredi   fs: make remainin...
1087
1088
  			     struct inode *new_dir, struct dentry *new_dentry,
  			     unsigned int flags)
80b9bbefc   Tejun Heo   kernfs: add kernf...
1089
  {
319ba91d3   Shaohua Li   kernfs: don't set...
1090
  	struct kernfs_node *kn = kernfs_dentry_node(old_dentry);
80b9bbefc   Tejun Heo   kernfs: add kernf...
1091
  	struct kernfs_node *new_parent = new_dir->i_private;
90c07c895   Tejun Heo   kernfs: rename ke...
1092
  	struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
07c7530dd   Tejun Heo   kernfs: invoke di...
1093
  	int ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1094

1cd66c93b   Miklos Szeredi   fs: make remainin...
1095
1096
  	if (flags)
  		return -EINVAL;
90c07c895   Tejun Heo   kernfs: rename ke...
1097
  	if (!scops || !scops->rename)
80b9bbefc   Tejun Heo   kernfs: add kernf...
1098
  		return -EPERM;
07c7530dd   Tejun Heo   kernfs: invoke di...
1099
1100
1101
1102
1103
1104
1105
  	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...
1106
  	ret = scops->rename(kn, new_parent, new_dentry->d_name.name);
07c7530dd   Tejun Heo   kernfs: invoke di...
1107
1108
1109
1110
  
  	kernfs_put_active(new_parent);
  	kernfs_put_active(kn);
  	return ret;
80b9bbefc   Tejun Heo   kernfs: add kernf...
1111
  }
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1112
  const struct inode_operations kernfs_dir_iops = {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1113
1114
1115
1116
  	.lookup		= kernfs_iop_lookup,
  	.permission	= kernfs_iop_permission,
  	.setattr	= kernfs_iop_setattr,
  	.getattr	= kernfs_iop_getattr,
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1117
  	.listxattr	= kernfs_iop_listxattr,
80b9bbefc   Tejun Heo   kernfs: add kernf...
1118
1119
1120
1121
  
  	.mkdir		= kernfs_iop_mkdir,
  	.rmdir		= kernfs_iop_rmdir,
  	.rename		= kernfs_iop_rename,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1122
  };
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1123
  static struct kernfs_node *kernfs_leftmost_descendant(struct kernfs_node *pos)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1124
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1125
  	struct kernfs_node *last;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1126
1127
1128
1129
1130
  
  	while (true) {
  		struct rb_node *rbn;
  
  		last = pos;
df23fc39b   Tejun Heo   kernfs: s/sysfs/k...
1131
  		if (kernfs_type(pos) != KERNFS_DIR)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1132
  			break;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1133
  		rbn = rb_first(&pos->dir.children);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1134
1135
  		if (!rbn)
  			break;
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1136
  		pos = rb_to_kn(rbn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1137
1138
1139
1140
1141
1142
  	}
  
  	return last;
  }
  
  /**
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1143
   * kernfs_next_descendant_post - find the next descendant for post-order walk
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1144
   * @pos: the current position (%NULL to initiate traversal)
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1145
   * @root: kernfs_node whose descendants to walk
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1146
1147
1148
1149
1150
   *
   * 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...
1151
1152
  static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
  						       struct kernfs_node *root)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1153
1154
  {
  	struct rb_node *rbn;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1155
  	lockdep_assert_held(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1156
1157
1158
  
  	/* if first iteration, visit leftmost descendant which may be root */
  	if (!pos)
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1159
  		return kernfs_leftmost_descendant(root);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1160
1161
1162
1163
1164
1165
  
  	/* 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...
1166
  	rbn = rb_next(&pos->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1167
  	if (rbn)
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1168
  		return kernfs_leftmost_descendant(rb_to_kn(rbn));
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1169
1170
  
  	/* no sibling left, visit parent */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1171
  	return pos->parent;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1172
  }
d35258ef7   Tejun Heo   kernfs: allow nod...
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
  /**
   * 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...
1206
  static void __kernfs_remove(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1207
  {
35beab063   Tejun Heo   kernfs: restructu...
1208
1209
1210
  	struct kernfs_node *pos;
  
  	lockdep_assert_held(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1211

6b0afc2a2   Tejun Heo   kernfs, sysfs, dr...
1212
1213
1214
1215
1216
1217
  	/*
  	 * 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...
1218
  		return;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1219
1220
  	pr_debug("kernfs %s: removing
  ", kn->name);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1221

81c173cb5   Tejun Heo   kernfs: remove KE...
1222
  	/* prevent any new usage under @kn by deactivating all nodes */
35beab063   Tejun Heo   kernfs: restructu...
1223
1224
  	pos = NULL;
  	while ((pos = kernfs_next_descendant_post(pos, kn)))
81c173cb5   Tejun Heo   kernfs: remove KE...
1225
1226
  		if (kernfs_active(pos))
  			atomic_add(KN_DEACTIVATED_BIAS, &pos->active);
35beab063   Tejun Heo   kernfs: restructu...
1227
1228
  
  	/* deactivate and unlink the subtree node-by-node */
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1229
  	do {
35beab063   Tejun Heo   kernfs: restructu...
1230
1231
1232
  		pos = kernfs_leftmost_descendant(kn);
  
  		/*
81c173cb5   Tejun Heo   kernfs: remove KE...
1233
1234
1235
1236
  		 * 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...
1237
1238
  		 */
  		kernfs_get(pos);
d35258ef7   Tejun Heo   kernfs: allow nod...
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
  		/*
  		 * 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...
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
  
  		/*
  		 * 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) {
058952196   Ondrej Mosnacek   kernfs: clean up ...
1260
1261
  				ktime_get_real_ts64(&ps_iattr->ia_ctime);
  				ps_iattr->ia_mtime = ps_iattr->ia_ctime;
35beab063   Tejun Heo   kernfs: restructu...
1262
  			}
988cd7afb   Tejun Heo   kernfs: remove ke...
1263
  			kernfs_put(pos);
35beab063   Tejun Heo   kernfs: restructu...
1264
1265
1266
1267
  		}
  
  		kernfs_put(pos);
  	} while (pos != kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1268
1269
1270
  }
  
  /**
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1271
1272
   * kernfs_remove - remove a kernfs_node recursively
   * @kn: the kernfs_node to remove
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1273
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1274
   * Remove @kn along with all its subdirectories and files.
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1275
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1276
  void kernfs_remove(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1277
  {
988cd7afb   Tejun Heo   kernfs: remove ke...
1278
1279
1280
  	mutex_lock(&kernfs_mutex);
  	__kernfs_remove(kn);
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1281
1282
1283
  }
  
  /**
6b0afc2a2   Tejun Heo   kernfs, sysfs, dr...
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
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
   * 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...
1415
1416
1417
1418
   * 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...
1419
   *
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1420
1421
   * 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...
1422
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1423
  int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1424
1425
  			     const void *ns)
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1426
  	struct kernfs_node *kn;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1427

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1428
  	if (!parent) {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1429
1430
  		WARN(1, KERN_WARNING "kernfs: can not remove '%s', no directory
  ",
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1431
1432
1433
  			name);
  		return -ENOENT;
  	}
988cd7afb   Tejun Heo   kernfs: remove ke...
1434
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1435

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1436
1437
  	kn = kernfs_find_ns(parent, name, ns);
  	if (kn)
988cd7afb   Tejun Heo   kernfs: remove ke...
1438
  		__kernfs_remove(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1439

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

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1442
  	if (kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1443
1444
1445
1446
1447
1448
1449
  		return 0;
  	else
  		return -ENOENT;
  }
  
  /**
   * kernfs_rename_ns - move and rename a kernfs_node
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1450
   * @kn: target node
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1451
1452
1453
1454
   * @new_parent: new parent to put @sd under
   * @new_name: new name
   * @new_ns: new namespace tag
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1455
  int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1456
1457
  		     const char *new_name, const void *new_ns)
  {
3eef34ad7   Tejun Heo   kernfs: implement...
1458
1459
  	struct kernfs_node *old_parent;
  	const char *old_name = NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1460
  	int error;
3eef34ad7   Tejun Heo   kernfs: implement...
1461
1462
1463
  	/* can't move or rename root */
  	if (!kn->parent)
  		return -EINVAL;
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1464
  	mutex_lock(&kernfs_mutex);
d0ae3d434   Tejun Heo   kernfs: add REMOV...
1465
  	error = -ENOENT;
ea015218f   Eric W. Biederman   kernfs: Add suppo...
1466
1467
  	if (!kernfs_active(kn) || !kernfs_active(new_parent) ||
  	    (new_parent->flags & KERNFS_EMPTY_DIR))
d0ae3d434   Tejun Heo   kernfs: add REMOV...
1468
  		goto out;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1469
  	error = 0;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1470
1471
  	if ((kn->parent == new_parent) && (kn->ns == new_ns) &&
  	    (strcmp(kn->name, new_name) == 0))
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1472
  		goto out;	/* nothing to rename */
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1473
1474
1475
  
  	error = -EEXIST;
  	if (kernfs_find_ns(new_parent, new_name, new_ns))
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1476
  		goto out;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1477

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1478
  	/* rename kernfs_node */
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1479
  	if (strcmp(kn->name, new_name) != 0) {
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1480
  		error = -ENOMEM;
75287a677   Andrzej Hajda   kernfs: convert n...
1481
  		new_name = kstrdup_const(new_name, GFP_KERNEL);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1482
  		if (!new_name)
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1483
  			goto out;
3eef34ad7   Tejun Heo   kernfs: implement...
1484
1485
  	} else {
  		new_name = NULL;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1486
1487
1488
1489
1490
  	}
  
  	/*
  	 * Move to the appropriate place in the appropriate directories rbtree.
  	 */
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1491
  	kernfs_unlink_sibling(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1492
  	kernfs_get(new_parent);
3eef34ad7   Tejun Heo   kernfs: implement...
1493
1494
1495
1496
1497
  
  	/* rename_lock protects ->parent and ->name accessors */
  	spin_lock_irq(&kernfs_rename_lock);
  
  	old_parent = kn->parent;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1498
  	kn->parent = new_parent;
3eef34ad7   Tejun Heo   kernfs: implement...
1499
1500
1501
  
  	kn->ns = new_ns;
  	if (new_name) {
dfeb0750b   Tejun Heo   kernfs: remove KE...
1502
  		old_name = kn->name;
3eef34ad7   Tejun Heo   kernfs: implement...
1503
1504
1505
1506
  		kn->name = new_name;
  	}
  
  	spin_unlock_irq(&kernfs_rename_lock);
9561a8961   Tejun Heo   kernfs: fix hash ...
1507
  	kn->hash = kernfs_name_hash(kn->name, kn->ns);
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1508
  	kernfs_link_sibling(kn);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1509

3eef34ad7   Tejun Heo   kernfs: implement...
1510
  	kernfs_put(old_parent);
75287a677   Andrzej Hajda   kernfs: convert n...
1511
  	kfree_const(old_name);
3eef34ad7   Tejun Heo   kernfs: implement...
1512

fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1513
  	error = 0;
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1514
   out:
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1515
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1516
1517
  	return error;
  }
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1518
  /* Relationship between s_mode and the DT_xxx types */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1519
  static inline unsigned char dt_type(struct kernfs_node *kn)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1520
  {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1521
  	return (kn->mode >> 12) & 15;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1522
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1523
  static int kernfs_dir_fop_release(struct inode *inode, struct file *filp)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1524
1525
1526
1527
  {
  	kernfs_put(filp->private_data);
  	return 0;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1528
  static struct kernfs_node *kernfs_dir_pos(const void *ns,
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1529
  	struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1530
1531
  {
  	if (pos) {
81c173cb5   Tejun Heo   kernfs: remove KE...
1532
  		int valid = kernfs_active(pos) &&
798c75a0d   Greg Kroah-Hartman   Revert "kernfs: r...
1533
  			pos->parent == parent && hash == pos->hash;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1534
1535
1536
1537
1538
  		kernfs_put(pos);
  		if (!valid)
  			pos = NULL;
  	}
  	if (!pos && (hash > 1) && (hash < INT_MAX)) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1539
  		struct rb_node *node = parent->dir.children.rb_node;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1540
  		while (node) {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1541
  			pos = rb_to_kn(node);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1542

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1543
  			if (hash < pos->hash)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1544
  				node = node->rb_left;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1545
  			else if (hash > pos->hash)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1546
1547
1548
1549
1550
  				node = node->rb_right;
  			else
  				break;
  		}
  	}
b9c9dad0c   Tejun Heo   kernfs: add missi...
1551
1552
  	/* 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...
1553
  		struct rb_node *node = rb_next(&pos->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1554
1555
1556
  		if (!node)
  			pos = NULL;
  		else
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1557
  			pos = rb_to_kn(node);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1558
1559
1560
  	}
  	return pos;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1561
  static struct kernfs_node *kernfs_dir_next_pos(const void *ns,
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1562
  	struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1563
  {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1564
  	pos = kernfs_dir_pos(ns, parent, ino, pos);
b9c9dad0c   Tejun Heo   kernfs: add missi...
1565
  	if (pos) {
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1566
  		do {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1567
  			struct rb_node *node = rb_next(&pos->rb);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1568
1569
1570
  			if (!node)
  				pos = NULL;
  			else
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1571
  				pos = rb_to_kn(node);
b9c9dad0c   Tejun Heo   kernfs: add missi...
1572
1573
  		} while (pos && (!kernfs_active(pos) || pos->ns != ns));
  	}
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1574
1575
  	return pos;
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1576
  static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1577
1578
  {
  	struct dentry *dentry = file->f_path.dentry;
319ba91d3   Shaohua Li   kernfs: don't set...
1579
  	struct kernfs_node *parent = kernfs_dentry_node(dentry);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
1580
  	struct kernfs_node *pos = file->private_data;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1581
1582
1583
1584
  	const void *ns = NULL;
  
  	if (!dir_emit_dots(file, ctx))
  		return 0;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1585
  	mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1586

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

c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1590
  	for (pos = kernfs_dir_pos(ns, parent, ctx->pos, pos);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1591
  	     pos;
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1592
  	     pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) {
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1593
  		const char *name = pos->name;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1594
1595
  		unsigned int type = dt_type(pos);
  		int len = strlen(name);
c53cd490b   Shaohua Li   kernfs: introduce...
1596
  		ino_t ino = pos->id.ino;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1597

adc5e8b58   Tejun Heo   kernfs: drop s_ p...
1598
  		ctx->pos = pos->hash;
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1599
1600
  		file->private_data = pos;
  		kernfs_get(pos);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1601
  		mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1602
1603
  		if (!dir_emit(ctx, name, len, ino, type))
  			return 0;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1604
  		mutex_lock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1605
  	}
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1606
  	mutex_unlock(&kernfs_mutex);
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1607
1608
1609
1610
  	file->private_data = NULL;
  	ctx->pos = INT_MAX;
  	return 0;
  }
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
1611
  const struct file_operations kernfs_dir_fops = {
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1612
  	.read		= generic_read_dir,
8cb0d2c1c   Al Viro   kernfs: no point ...
1613
  	.iterate_shared	= kernfs_fop_readdir,
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
1614
  	.release	= kernfs_dir_fop_release,
8cb0d2c1c   Al Viro   kernfs: no point ...
1615
  	.llseek		= generic_file_llseek,
fd7b9f7b9   Tejun Heo   sysfs, kernfs: mo...
1616
  };