generic.c 14.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * proc/fs/generic.c --- generic routines for the proc-fs
 *
 * This file contains generic proc-fs routines for handling
 * directories and files.
 * 
 * Copyright (C) 1991, 1992 Linus Torvalds.
 * Copyright (C) 1997 Theodore Ts'o
 */

#include <linux/errno.h>
#include <linux/time.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
C
Christoph Hellwig 已提交
15
#include <linux/mm.h>
L
Linus Torvalds 已提交
16
#include <linux/module.h>
17
#include <linux/slab.h>
A
Andrew Morton 已提交
18
#include <linux/printk.h>
L
Linus Torvalds 已提交
19 20 21 22
#include <linux/mount.h>
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/bitops.h>
23
#include <linux/spinlock.h>
24
#include <linux/completion.h>
25
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
26

27 28
#include "internal.h"

29
static DEFINE_RWLOCK(proc_subdir_lock);
30

A
Alexey Dobriyan 已提交
31
static int proc_match(const char *name, struct proc_dir_entry *de, unsigned int len)
L
Linus Torvalds 已提交
32
{
33 34 35 36 37 38 39 40 41 42
	if (len < de->namelen)
		return -1;
	if (len > de->namelen)
		return 1;

	return memcmp(name, de->name, len);
}

static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir)
{
43 44
	return rb_entry_safe(rb_first_cached(&dir->subdir),
			     struct proc_dir_entry, subdir_node);
45 46 47 48
}

static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir)
{
49 50
	return rb_entry_safe(rb_next(&dir->subdir_node), struct proc_dir_entry,
			     subdir_node);
51 52 53 54 55 56
}

static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,
					      const char *name,
					      unsigned int len)
{
57
	struct rb_node *node = dir->subdir.rb_root.rb_node;
58 59

	while (node) {
G
Geliang Tang 已提交
60 61 62
		struct proc_dir_entry *de = rb_entry(node,
						     struct proc_dir_entry,
						     subdir_node);
A
Alexey Dobriyan 已提交
63
		int result = proc_match(name, de, len);
64 65 66 67 68 69 70 71 72 73 74 75 76 77

		if (result < 0)
			node = node->rb_left;
		else if (result > 0)
			node = node->rb_right;
		else
			return de;
	}
	return NULL;
}

static bool pde_subdir_insert(struct proc_dir_entry *dir,
			      struct proc_dir_entry *de)
{
78 79 80
	struct rb_root_cached *root = &dir->subdir;
	struct rb_node **new = &root->rb_root.rb_node, *parent = NULL;
	bool leftmost = true;
81 82 83

	/* Figure out where to put new node */
	while (*new) {
G
Geliang Tang 已提交
84 85 86
		struct proc_dir_entry *this = rb_entry(*new,
						       struct proc_dir_entry,
						       subdir_node);
A
Alexey Dobriyan 已提交
87
		int result = proc_match(de->name, this, de->namelen);
88 89 90 91

		parent = *new;
		if (result < 0)
			new = &(*new)->rb_left;
92
		else if (result > 0) {
93
			new = &(*new)->rb_right;
94 95
			leftmost = false;
		} else
96 97 98 99 100
			return false;
	}

	/* Add new node and rebalance tree. */
	rb_link_node(&de->subdir_node, parent, new);
101
	rb_insert_color_cached(&de->subdir_node, root, leftmost);
102
	return true;
L
Linus Torvalds 已提交
103 104 105 106
}

static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
{
107
	struct inode *inode = d_inode(dentry);
L
Linus Torvalds 已提交
108 109 110
	struct proc_dir_entry *de = PDE(inode);
	int error;

111
	error = setattr_prepare(dentry, iattr);
L
Linus Torvalds 已提交
112
	if (error)
C
Christoph Hellwig 已提交
113
		return error;
L
Linus Torvalds 已提交
114

C
Christoph Hellwig 已提交
115 116
	setattr_copy(inode, iattr);
	mark_inode_dirty(inode);
M
Marco Stornelli 已提交
117

118
	proc_set_user(de, inode->i_uid, inode->i_gid);
L
Linus Torvalds 已提交
119
	de->mode = inode->i_mode;
C
Christoph Hellwig 已提交
120
	return 0;
L
Linus Torvalds 已提交
121 122
}

123 124
static int proc_getattr(const struct path *path, struct kstat *stat,
			u32 request_mask, unsigned int query_flags)
M
Miklos Szeredi 已提交
125
{
126
	struct inode *inode = d_inode(path->dentry);
127
	struct proc_dir_entry *de = PDE(inode);
M
Miklos Szeredi 已提交
128
	if (de && de->nlink)
M
Miklos Szeredi 已提交
129
		set_nlink(inode, de->nlink);
M
Miklos Szeredi 已提交
130 131 132 133 134

	generic_fillattr(inode, stat);
	return 0;
}

135
static const struct inode_operations proc_file_inode_operations = {
L
Linus Torvalds 已提交
136 137 138 139 140 141 142 143
	.setattr	= proc_notify_change,
};

/*
 * This function parses a name such as "tty/driver/serial", and
 * returns the struct proc_dir_entry for "/proc/tty/driver", and
 * returns "serial" in residual.
 */
144 145
static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
			     const char **residual)
L
Linus Torvalds 已提交
146 147 148
{
	const char     		*cp = name, *next;
	struct proc_dir_entry	*de;
149
	unsigned int		len;
L
Linus Torvalds 已提交
150

151 152 153 154
	de = *ret;
	if (!de)
		de = &proc_root;

L
Linus Torvalds 已提交
155 156 157 158 159 160
	while (1) {
		next = strchr(cp, '/');
		if (!next)
			break;

		len = next - cp;
161
		de = pde_subdir_find(de, cp, len);
162 163
		if (!de) {
			WARN(1, "name '%s'\n", name);
164
			return -ENOENT;
165
		}
L
Linus Torvalds 已提交
166 167 168 169
		cp += len + 1;
	}
	*residual = cp;
	*ret = de;
170 171 172 173 174 175 176 177
	return 0;
}

static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
			   const char **residual)
{
	int rv;

178
	read_lock(&proc_subdir_lock);
179
	rv = __xlate_proc_name(name, ret, residual);
180
	read_unlock(&proc_subdir_lock);
181
	return rv;
L
Linus Torvalds 已提交
182 183
}

184
static DEFINE_IDA(proc_inum_ida);
L
Linus Torvalds 已提交
185

186
#define PROC_DYNAMIC_FIRST 0xF0000000U
L
Linus Torvalds 已提交
187 188 189 190 191

/*
 * Return an inode number between PROC_DYNAMIC_FIRST and
 * 0xffffffff, or zero on failure.
 */
192
int proc_alloc_inum(unsigned int *inum)
L
Linus Torvalds 已提交
193
{
194
	int i;
L
Linus Torvalds 已提交
195

196 197 198 199
	i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1,
			   GFP_KERNEL);
	if (i < 0)
		return i;
L
Linus Torvalds 已提交
200

201
	*inum = PROC_DYNAMIC_FIRST + (unsigned int)i;
202
	return 0;
L
Linus Torvalds 已提交
203 204
}

205
void proc_free_inum(unsigned int inum)
L
Linus Torvalds 已提交
206
{
207
	ida_simple_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
L
Linus Torvalds 已提交
208 209 210 211 212 213
}

/*
 * Don't create negative dentries here, return -ENOENT by hand
 * instead.
 */
A
Alexey Dobriyan 已提交
214 215
struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
			      struct proc_dir_entry *de)
L
Linus Torvalds 已提交
216
{
217
	struct inode *inode;
L
Linus Torvalds 已提交
218

219
	read_lock(&proc_subdir_lock);
220 221 222
	de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len);
	if (de) {
		pde_get(de);
223
		read_unlock(&proc_subdir_lock);
224 225 226 227 228 229
		inode = proc_get_inode(dir->i_sb, de);
		if (!inode)
			return ERR_PTR(-ENOMEM);
		d_set_d_op(dentry, &simple_dentry_operations);
		d_add(dentry, inode);
		return NULL;
L
Linus Torvalds 已提交
230
	}
231
	read_unlock(&proc_subdir_lock);
232
	return ERR_PTR(-ENOENT);
L
Linus Torvalds 已提交
233 234
}

235
struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
A
Al Viro 已提交
236
		unsigned int flags)
237
{
A
Alexey Dobriyan 已提交
238
	return proc_lookup_de(dir, dentry, PDE(dir));
239 240
}

L
Linus Torvalds 已提交
241 242 243 244 245 246 247 248 249
/*
 * This returns non-zero if at EOF, so that the /proc
 * root directory can use this and check if it should
 * continue with the <pid> entries..
 *
 * Note that the VFS-layer doesn't care about the return
 * value of the readdir() call, as long as it's non-negative
 * for success..
 */
A
Alexey Dobriyan 已提交
250 251
int proc_readdir_de(struct file *file, struct dir_context *ctx,
		    struct proc_dir_entry *de)
L
Linus Torvalds 已提交
252 253
{
	int i;
A
Al Viro 已提交
254 255 256 257

	if (!dir_emit_dots(file, ctx))
		return 0;

258
	read_lock(&proc_subdir_lock);
259
	de = pde_subdir_first(de);
A
Al Viro 已提交
260 261 262
	i = ctx->pos - 2;
	for (;;) {
		if (!de) {
263
			read_unlock(&proc_subdir_lock);
A
Al Viro 已提交
264 265 266 267
			return 0;
		}
		if (!i)
			break;
268
		de = pde_subdir_next(de);
A
Al Viro 已提交
269
		i--;
L
Linus Torvalds 已提交
270
	}
A
Al Viro 已提交
271 272 273 274

	do {
		struct proc_dir_entry *next;
		pde_get(de);
275
		read_unlock(&proc_subdir_lock);
A
Al Viro 已提交
276 277 278 279 280
		if (!dir_emit(ctx, de->name, de->namelen,
			    de->low_ino, de->mode >> 12)) {
			pde_put(de);
			return 0;
		}
281
		read_lock(&proc_subdir_lock);
A
Al Viro 已提交
282
		ctx->pos++;
283
		next = pde_subdir_next(de);
A
Al Viro 已提交
284 285 286
		pde_put(de);
		de = next;
	} while (de);
287
	read_unlock(&proc_subdir_lock);
288
	return 1;
L
Linus Torvalds 已提交
289 290
}

A
Al Viro 已提交
291
int proc_readdir(struct file *file, struct dir_context *ctx)
292
{
A
Al Viro 已提交
293
	struct inode *inode = file_inode(file);
294

A
Alexey Dobriyan 已提交
295
	return proc_readdir_de(file, ctx, PDE(inode));
296 297
}

L
Linus Torvalds 已提交
298 299 300 301 302
/*
 * These are the generic /proc directory operations. They
 * use the in-memory "struct proc_dir_entry" tree to parse
 * the /proc directory.
 */
303
static const struct file_operations proc_dir_operations = {
A
Alexey Dobriyan 已提交
304
	.llseek			= generic_file_llseek,
L
Linus Torvalds 已提交
305
	.read			= generic_read_dir,
306
	.iterate_shared		= proc_readdir,
L
Linus Torvalds 已提交
307 308 309 310 311
};

/*
 * proc directories can do almost nothing..
 */
312
static const struct inode_operations proc_dir_inode_operations = {
L
Linus Torvalds 已提交
313
	.lookup		= proc_lookup,
M
Miklos Szeredi 已提交
314
	.getattr	= proc_getattr,
L
Linus Torvalds 已提交
315 316 317 318 319
	.setattr	= proc_notify_change,
};

static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
{
320
	int ret;
321

322 323 324
	ret = proc_alloc_inum(&dp->low_ino);
	if (ret)
		return ret;
325

326
	write_lock(&proc_subdir_lock);
C
Changli Gao 已提交
327
	dp->parent = dir;
328
	if (pde_subdir_insert(dir, dp) == false) {
329 330
		WARN(1, "proc_dir_entry '%s/%s' already registered\n",
		     dir->name, dp->name);
331
		write_unlock(&proc_subdir_lock);
332 333 334
		proc_free_inum(dp->low_ino);
		return -EEXIST;
	}
335
	write_unlock(&proc_subdir_lock);
C
Changli Gao 已提交
336

L
Linus Torvalds 已提交
337 338 339
	return 0;
}

340
static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
L
Linus Torvalds 已提交
341
					  const char *name,
A
Al Viro 已提交
342
					  umode_t mode,
L
Linus Torvalds 已提交
343 344 345
					  nlink_t nlink)
{
	struct proc_dir_entry *ent = NULL;
346 347
	const char *fn;
	struct qstr qstr;
L
Linus Torvalds 已提交
348

349
	if (xlate_proc_name(name, parent, &fn) != 0)
L
Linus Torvalds 已提交
350
		goto out;
351 352 353 354 355 356 357 358 359 360
	qstr.name = fn;
	qstr.len = strlen(fn);
	if (qstr.len == 0 || qstr.len >= 256) {
		WARN(1, "name len %u\n", qstr.len);
		return NULL;
	}
	if (*parent == &proc_root && name_to_int(&qstr) != ~0U) {
		WARN(1, "create '/proc/%s' by hand\n", qstr.name);
		return NULL;
	}
361 362 363 364
	if (is_empty_pde(*parent)) {
		WARN(1, "attempt to add to permanently empty directory");
		return NULL;
	}
L
Linus Torvalds 已提交
365

366
	ent = kzalloc(sizeof(struct proc_dir_entry) + qstr.len + 1, GFP_KERNEL);
367 368
	if (!ent)
		goto out;
L
Linus Torvalds 已提交
369

370 371
	memcpy(ent->name, fn, qstr.len + 1);
	ent->namelen = qstr.len;
L
Linus Torvalds 已提交
372 373
	ent->mode = mode;
	ent->nlink = nlink;
374
	ent->subdir = RB_ROOT_CACHED;
375
	atomic_set(&ent->count, 1);
376
	spin_lock_init(&ent->pde_unload_lock);
A
Alexey Dobriyan 已提交
377
	INIT_LIST_HEAD(&ent->pde_openers);
378 379
	proc_set_user(ent, (*parent)->uid, (*parent)->gid);

380
out:
L
Linus Torvalds 已提交
381 382 383 384 385 386 387 388
	return ent;
}

struct proc_dir_entry *proc_symlink(const char *name,
		struct proc_dir_entry *parent, const char *dest)
{
	struct proc_dir_entry *ent;

389
	ent = __proc_create(&parent, name,
L
Linus Torvalds 已提交
390 391 392 393 394 395
			  (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);

	if (ent) {
		ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
		if (ent->data) {
			strcpy((char*)ent->data,dest);
A
Al Viro 已提交
396
			ent->proc_iops = &proc_link_inode_operations;
L
Linus Torvalds 已提交
397 398 399 400 401 402 403 404 405 406 407 408
			if (proc_register(parent, ent) < 0) {
				kfree(ent->data);
				kfree(ent);
				ent = NULL;
			}
		} else {
			kfree(ent);
			ent = NULL;
		}
	}
	return ent;
}
H
Helight.Xu 已提交
409
EXPORT_SYMBOL(proc_symlink);
L
Linus Torvalds 已提交
410

D
David Howells 已提交
411 412
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
		struct proc_dir_entry *parent, void *data)
L
Linus Torvalds 已提交
413 414 415
{
	struct proc_dir_entry *ent;

D
David Howells 已提交
416 417 418
	if (mode == 0)
		mode = S_IRUGO | S_IXUGO;

419
	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
L
Linus Torvalds 已提交
420
	if (ent) {
D
David Howells 已提交
421
		ent->data = data;
A
Al Viro 已提交
422 423 424
		ent->proc_fops = &proc_dir_operations;
		ent->proc_iops = &proc_dir_inode_operations;
		parent->nlink++;
L
Linus Torvalds 已提交
425 426
		if (proc_register(parent, ent) < 0) {
			kfree(ent);
A
Al Viro 已提交
427
			parent->nlink--;
L
Linus Torvalds 已提交
428 429 430 431 432
			ent = NULL;
		}
	}
	return ent;
}
D
David Howells 已提交
433
EXPORT_SYMBOL_GPL(proc_mkdir_data);
L
Linus Torvalds 已提交
434

D
David Howells 已提交
435 436
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
				       struct proc_dir_entry *parent)
437
{
D
David Howells 已提交
438
	return proc_mkdir_data(name, mode, parent, NULL);
439
}
D
David Howells 已提交
440
EXPORT_SYMBOL(proc_mkdir_mode);
441

L
Linus Torvalds 已提交
442 443 444
struct proc_dir_entry *proc_mkdir(const char *name,
		struct proc_dir_entry *parent)
{
D
David Howells 已提交
445
	return proc_mkdir_data(name, 0, parent, NULL);
L
Linus Torvalds 已提交
446
}
H
Helight.Xu 已提交
447
EXPORT_SYMBOL(proc_mkdir);
L
Linus Torvalds 已提交
448

449 450 451 452 453 454 455 456 457 458
struct proc_dir_entry *proc_create_mount_point(const char *name)
{
	umode_t mode = S_IFDIR | S_IRUGO | S_IXUGO;
	struct proc_dir_entry *ent, *parent = NULL;

	ent = __proc_create(&parent, name, mode, 2);
	if (ent) {
		ent->data = NULL;
		ent->proc_fops = NULL;
		ent->proc_iops = NULL;
459
		parent->nlink++;
460 461 462 463 464 465 466 467
		if (proc_register(parent, ent) < 0) {
			kfree(ent);
			parent->nlink--;
			ent = NULL;
		}
	}
	return ent;
}
468
EXPORT_SYMBOL(proc_create_mount_point);
469

A
Al Viro 已提交
470
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
471 472 473
					struct proc_dir_entry *parent,
					const struct file_operations *proc_fops,
					void *data)
474 475
{
	struct proc_dir_entry *pde;
476 477
	if ((mode & S_IFMT) == 0)
		mode |= S_IFREG;
478

479 480 481
	if (!S_ISREG(mode)) {
		WARN_ON(1);	/* use proc_mkdir() */
		return NULL;
482 483
	}

A
Al Viro 已提交
484 485
	BUG_ON(proc_fops == NULL);

486 487 488
	if ((mode & S_IALLUGO) == 0)
		mode |= S_IRUGO;
	pde = __proc_create(&parent, name, mode, 1);
489 490 491
	if (!pde)
		goto out;
	pde->proc_fops = proc_fops;
492
	pde->data = data;
A
Al Viro 已提交
493
	pde->proc_iops = &proc_file_inode_operations;
494 495 496 497 498 499 500 501
	if (proc_register(parent, pde) < 0)
		goto out_free;
	return pde;
out_free:
	kfree(pde);
out:
	return NULL;
}
H
Helight.Xu 已提交
502
EXPORT_SYMBOL(proc_create_data);
503
 
A
Alexey Dobriyan 已提交
504 505 506 507 508 509 510 511
struct proc_dir_entry *proc_create(const char *name, umode_t mode,
				   struct proc_dir_entry *parent,
				   const struct file_operations *proc_fops)
{
	return proc_create_data(name, mode, parent, proc_fops, NULL);
}
EXPORT_SYMBOL(proc_create);

512 513 514 515 516 517 518 519 520 521 522 523
void proc_set_size(struct proc_dir_entry *de, loff_t size)
{
	de->size = size;
}
EXPORT_SYMBOL(proc_set_size);

void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid)
{
	de->uid = uid;
	de->gid = gid;
}
EXPORT_SYMBOL(proc_set_user);
524

525
static void free_proc_entry(struct proc_dir_entry *de)
L
Linus Torvalds 已提交
526
{
527
	proc_free_inum(de->low_ino);
L
Linus Torvalds 已提交
528

529
	if (S_ISLNK(de->mode))
L
Linus Torvalds 已提交
530 531 532 533
		kfree(de->data);
	kfree(de);
}

534 535 536 537 538 539
void pde_put(struct proc_dir_entry *pde)
{
	if (atomic_dec_and_test(&pde->count))
		free_proc_entry(pde);
}

A
Al Viro 已提交
540 541 542 543 544 545 546 547 548
/*
 * Remove a /proc entry and free it if it's not currently in use.
 */
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
	struct proc_dir_entry *de = NULL;
	const char *fn = name;
	unsigned int len;

549
	write_lock(&proc_subdir_lock);
A
Al Viro 已提交
550
	if (__xlate_proc_name(name, &parent, &fn) != 0) {
551
		write_unlock(&proc_subdir_lock);
A
Al Viro 已提交
552 553 554 555
		return;
	}
	len = strlen(fn);

556 557
	de = pde_subdir_find(parent, fn, len);
	if (de)
558
		rb_erase_cached(&de->subdir_node, &parent->subdir);
559
	write_unlock(&proc_subdir_lock);
A
Al Viro 已提交
560 561 562 563 564
	if (!de) {
		WARN(1, "name '%s'\n", name);
		return;
	}

565
	proc_entry_rundown(de);
A
Alexey Dobriyan 已提交
566

567 568 569
	if (S_ISDIR(de->mode))
		parent->nlink--;
	de->nlink = 0;
570 571 572
	WARN(pde_subdir_first(de),
	     "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
	     __func__, de->parent->name, de->name, pde_subdir_first(de)->name);
573
	pde_put(de);
L
Linus Torvalds 已提交
574
}
H
Helight.Xu 已提交
575
EXPORT_SYMBOL(remove_proc_entry);
A
Al Viro 已提交
576 577 578 579 580 581 582

int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
{
	struct proc_dir_entry *root = NULL, *de, *next;
	const char *fn = name;
	unsigned int len;

583
	write_lock(&proc_subdir_lock);
A
Al Viro 已提交
584
	if (__xlate_proc_name(name, &parent, &fn) != 0) {
585
		write_unlock(&proc_subdir_lock);
A
Al Viro 已提交
586 587 588 589
		return -ENOENT;
	}
	len = strlen(fn);

590
	root = pde_subdir_find(parent, fn, len);
A
Al Viro 已提交
591
	if (!root) {
592
		write_unlock(&proc_subdir_lock);
A
Al Viro 已提交
593 594
		return -ENOENT;
	}
595
	rb_erase_cached(&root->subdir_node, &parent->subdir);
596

A
Al Viro 已提交
597 598
	de = root;
	while (1) {
599
		next = pde_subdir_first(de);
A
Al Viro 已提交
600
		if (next) {
601
			rb_erase_cached(&next->subdir_node, &de->subdir);
A
Al Viro 已提交
602 603 604
			de = next;
			continue;
		}
605
		write_unlock(&proc_subdir_lock);
A
Al Viro 已提交
606

607
		proc_entry_rundown(de);
A
Al Viro 已提交
608 609 610 611 612 613 614 615
		next = de->parent;
		if (S_ISDIR(de->mode))
			next->nlink--;
		de->nlink = 0;
		if (de == root)
			break;
		pde_put(de);

616
		write_lock(&proc_subdir_lock);
A
Al Viro 已提交
617 618 619 620 621 622
		de = next;
	}
	pde_put(root);
	return 0;
}
EXPORT_SYMBOL(remove_proc_subtree);
623 624 625 626 627 628 629

void *proc_get_parent_data(const struct inode *inode)
{
	struct proc_dir_entry *de = PDE(inode);
	return de->parent->data;
}
EXPORT_SYMBOL_GPL(proc_get_parent_data);
630 631 632 633 634 635 636

void proc_remove(struct proc_dir_entry *de)
{
	if (de)
		remove_proc_subtree(de->name, de->parent);
}
EXPORT_SYMBOL(proc_remove);
637 638 639 640 641 642

void *PDE_DATA(const struct inode *inode)
{
	return __PDE_DATA(inode);
}
EXPORT_SYMBOL(PDE_DATA);