aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-01-17 09:58:25 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-17 14:50:07 -0500
commitdb4aad209bc9aefd91f0a9aeb9e37364088b39ad (patch)
tree28cc941640469a11111808a6e77be61e892e5041 /fs/kernfs
parent917f56caaabc215f9658006dad28a9665ec0ce19 (diff)
kernfs: associate a new kernfs_node with its parent on creation
Once created, a kernfs_node is always destroyed by kernfs_put(). Since ba7443bc656e ("sysfs, kernfs: implement kernfs_create/destroy_root()"), kernfs_put() depends on kernfs_root() to locate the ino_ida. kernfs_root() in turn depends on kernfs_node->parent being set for !dir nodes. This means that kernfs_put() of a !dir node requires its ->parent to be initialized. This leads to oops when a newly created !dir node is destroyed without going through kernfs_add_one() or after failing kernfs_add_one() before ->parent is set. kernfs_root() invoked from kernfs_put() will try to dereference NULL parent. Fix it by moving parent association to kernfs_new_node() from kernfs_add_one(). kernfs_new_node() now takes @parent instead of @root and determines the root from the parent and also sets the new node's parent properly. @parent parameter is removed from kernfs_add_one(). As there's no parent when creating the root node, __kernfs_new_node() which takes @root as before and doesn't set the parent is used in that case. This ensures that a kernfs_node in any stage in its life has its parent associated and thus can be put. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs')
-rw-r--r--fs/kernfs/dir.c40
-rw-r--r--fs/kernfs/file.c5
-rw-r--r--fs/kernfs/kernfs-internal.h8
-rw-r--r--fs/kernfs/symlink.c5
4 files changed, 34 insertions, 24 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 510b5062ef30..5104cf5d25c5 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -324,8 +324,9 @@ const struct dentry_operations kernfs_dops = {
324 .d_release = kernfs_dop_release, 324 .d_release = kernfs_dop_release,
325}; 325};
326 326
327struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, 327static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
328 umode_t mode, unsigned flags) 328 const char *name, umode_t mode,
329 unsigned flags)
329{ 330{
330 char *dup_name = NULL; 331 char *dup_name = NULL;
331 struct kernfs_node *kn; 332 struct kernfs_node *kn;
@@ -362,6 +363,20 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name,
362 return NULL; 363 return NULL;
363} 364}
364 365
366struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
367 const char *name, umode_t mode,
368 unsigned flags)
369{
370 struct kernfs_node *kn;
371
372 kn = __kernfs_new_node(kernfs_root(parent), name, mode, flags);
373 if (kn) {
374 kernfs_get(parent);
375 kn->parent = parent;
376 }
377 return kn;
378}
379
365/** 380/**
366 * kernfs_addrm_start - prepare for kernfs_node add/remove 381 * kernfs_addrm_start - prepare for kernfs_node add/remove
367 * @acxt: pointer to kernfs_addrm_cxt to be used 382 * @acxt: pointer to kernfs_addrm_cxt to be used
@@ -386,11 +401,10 @@ void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt)
386 * kernfs_add_one - add kernfs_node to parent without warning 401 * kernfs_add_one - add kernfs_node to parent without warning
387 * @acxt: addrm context to use 402 * @acxt: addrm context to use
388 * @kn: kernfs_node to be added 403 * @kn: kernfs_node to be added
389 * @parent: the parent kernfs_node to add @kn to
390 * 404 *
391 * Get @parent and set @kn->parent to it and increment nlink of the 405 * The caller must already have initialized @kn->parent. This
392 * parent inode if @kn is a directory and link into the children list 406 * function increments nlink of the parent's inode if @kn is a
393 * of the parent. 407 * directory and link into the children list of the parent.
394 * 408 *
395 * This function should be called between calls to 409 * This function should be called between calls to
396 * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed 410 * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed
@@ -403,9 +417,9 @@ void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt)
403 * 0 on success, -EEXIST if entry with the given name already 417 * 0 on success, -EEXIST if entry with the given name already
404 * exists. 418 * exists.
405 */ 419 */
406int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, 420int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn)
407 struct kernfs_node *parent)
408{ 421{
422 struct kernfs_node *parent = kn->parent;
409 bool has_ns = kernfs_ns_enabled(parent); 423 bool has_ns = kernfs_ns_enabled(parent);
410 struct kernfs_iattrs *ps_iattr; 424 struct kernfs_iattrs *ps_iattr;
411 int ret; 425 int ret;
@@ -423,8 +437,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
423 return -ENOENT; 437 return -ENOENT;
424 438
425 kn->hash = kernfs_name_hash(kn->name, kn->ns); 439 kn->hash = kernfs_name_hash(kn->name, kn->ns);
426 kn->parent = parent;
427 kernfs_get(parent);
428 440
429 ret = kernfs_link_sibling(kn); 441 ret = kernfs_link_sibling(kn);
430 if (ret) 442 if (ret)
@@ -600,7 +612,8 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
600 612
601 ida_init(&root->ino_ida); 613 ida_init(&root->ino_ida);
602 614
603 kn = kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO, KERNFS_DIR); 615 kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO,
616 KERNFS_DIR);
604 if (!kn) { 617 if (!kn) {
605 ida_destroy(&root->ino_ida); 618 ida_destroy(&root->ino_ida);
606 kfree(root); 619 kfree(root);
@@ -648,8 +661,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
648 int rc; 661 int rc;
649 662
650 /* allocate */ 663 /* allocate */
651 kn = kernfs_new_node(kernfs_root(parent), name, mode | S_IFDIR, 664 kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR);
652 KERNFS_DIR);
653 if (!kn) 665 if (!kn)
654 return ERR_PTR(-ENOMEM); 666 return ERR_PTR(-ENOMEM);
655 667
@@ -659,7 +671,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
659 671
660 /* link in */ 672 /* link in */
661 kernfs_addrm_start(&acxt); 673 kernfs_addrm_start(&acxt);
662 rc = kernfs_add_one(&acxt, kn, parent); 674 rc = kernfs_add_one(&acxt, kn);
663 kernfs_addrm_finish(&acxt); 675 kernfs_addrm_finish(&acxt);
664 676
665 if (!rc) 677 if (!rc)
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index bdd38854ef65..dbf397bfdff2 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -829,8 +829,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
829 if (name_is_static) 829 if (name_is_static)
830 flags |= KERNFS_STATIC_NAME; 830 flags |= KERNFS_STATIC_NAME;
831 831
832 kn = kernfs_new_node(kernfs_root(parent), name, 832 kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags);
833 (mode & S_IALLUGO) | S_IFREG, flags);
834 if (!kn) 833 if (!kn)
835 return ERR_PTR(-ENOMEM); 834 return ERR_PTR(-ENOMEM);
836 835
@@ -857,7 +856,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
857 kn->flags |= KERNFS_HAS_MMAP; 856 kn->flags |= KERNFS_HAS_MMAP;
858 857
859 kernfs_addrm_start(&acxt); 858 kernfs_addrm_start(&acxt);
860 rc = kernfs_add_one(&acxt, kn, parent); 859 rc = kernfs_add_one(&acxt, kn);
861 kernfs_addrm_finish(&acxt); 860 kernfs_addrm_finish(&acxt);
862 861
863 if (rc) { 862 if (rc) {
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index c6ba5bc37a98..eb536b76374a 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -101,11 +101,11 @@ extern const struct inode_operations kernfs_dir_iops;
101struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); 101struct kernfs_node *kernfs_get_active(struct kernfs_node *kn);
102void kernfs_put_active(struct kernfs_node *kn); 102void kernfs_put_active(struct kernfs_node *kn);
103void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt); 103void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt);
104int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, 104int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn);
105 struct kernfs_node *parent);
106void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt); 105void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt);
107struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, 106struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
108 umode_t mode, unsigned flags); 107 const char *name, umode_t mode,
108 unsigned flags);
109 109
110/* 110/*
111 * file.c 111 * file.c
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index a03e26036ef9..4d457055acb9 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -30,8 +30,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
30 struct kernfs_addrm_cxt acxt; 30 struct kernfs_addrm_cxt acxt;
31 int error; 31 int error;
32 32
33 kn = kernfs_new_node(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, 33 kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, KERNFS_LINK);
34 KERNFS_LINK);
35 if (!kn) 34 if (!kn)
36 return ERR_PTR(-ENOMEM); 35 return ERR_PTR(-ENOMEM);
37 36
@@ -41,7 +40,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
41 kernfs_get(target); /* ref owned by symlink */ 40 kernfs_get(target); /* ref owned by symlink */
42 41
43 kernfs_addrm_start(&acxt); 42 kernfs_addrm_start(&acxt);
44 error = kernfs_add_one(&acxt, kn, parent); 43 error = kernfs_add_one(&acxt, kn);
45 kernfs_addrm_finish(&acxt); 44 kernfs_addrm_finish(&acxt);
46 45
47 if (!error) 46 if (!error)