diff options
Diffstat (limited to 'fs/kernfs/mount.c')
-rw-r--r-- | fs/kernfs/mount.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 8eaf417187f1..b67dbccdaf88 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/magic.h> | 14 | #include <linux/magic.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/pagemap.h> | 16 | #include <linux/pagemap.h> |
17 | #include <linux/namei.h> | ||
17 | 18 | ||
18 | #include "kernfs-internal.h" | 19 | #include "kernfs-internal.h" |
19 | 20 | ||
@@ -62,6 +63,74 @@ struct kernfs_root *kernfs_root_from_sb(struct super_block *sb) | |||
62 | return NULL; | 63 | return NULL; |
63 | } | 64 | } |
64 | 65 | ||
66 | /* | ||
67 | * find the next ancestor in the path down to @child, where @parent was the | ||
68 | * ancestor whose descendant we want to find. | ||
69 | * | ||
70 | * Say the path is /a/b/c/d. @child is d, @parent is NULL. We return the root | ||
71 | * node. If @parent is b, then we return the node for c. | ||
72 | * Passing in d as @parent is not ok. | ||
73 | */ | ||
74 | static struct kernfs_node *find_next_ancestor(struct kernfs_node *child, | ||
75 | struct kernfs_node *parent) | ||
76 | { | ||
77 | if (child == parent) { | ||
78 | pr_crit_once("BUG in find_next_ancestor: called with parent == child"); | ||
79 | return NULL; | ||
80 | } | ||
81 | |||
82 | while (child->parent != parent) { | ||
83 | if (!child->parent) | ||
84 | return NULL; | ||
85 | child = child->parent; | ||
86 | } | ||
87 | |||
88 | return child; | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * kernfs_node_dentry - get a dentry for the given kernfs_node | ||
93 | * @kn: kernfs_node for which a dentry is needed | ||
94 | * @sb: the kernfs super_block | ||
95 | */ | ||
96 | struct dentry *kernfs_node_dentry(struct kernfs_node *kn, | ||
97 | struct super_block *sb) | ||
98 | { | ||
99 | struct dentry *dentry; | ||
100 | struct kernfs_node *knparent = NULL; | ||
101 | |||
102 | BUG_ON(sb->s_op != &kernfs_sops); | ||
103 | |||
104 | dentry = dget(sb->s_root); | ||
105 | |||
106 | /* Check if this is the root kernfs_node */ | ||
107 | if (!kn->parent) | ||
108 | return dentry; | ||
109 | |||
110 | knparent = find_next_ancestor(kn, NULL); | ||
111 | if (WARN_ON(!knparent)) | ||
112 | return ERR_PTR(-EINVAL); | ||
113 | |||
114 | do { | ||
115 | struct dentry *dtmp; | ||
116 | struct kernfs_node *kntmp; | ||
117 | |||
118 | if (kn == knparent) | ||
119 | return dentry; | ||
120 | kntmp = find_next_ancestor(kn, knparent); | ||
121 | if (WARN_ON(!kntmp)) | ||
122 | return ERR_PTR(-EINVAL); | ||
123 | mutex_lock(&d_inode(dentry)->i_mutex); | ||
124 | dtmp = lookup_one_len(kntmp->name, dentry, strlen(kntmp->name)); | ||
125 | mutex_unlock(&d_inode(dentry)->i_mutex); | ||
126 | dput(dentry); | ||
127 | if (IS_ERR(dtmp)) | ||
128 | return dtmp; | ||
129 | knparent = kntmp; | ||
130 | dentry = dtmp; | ||
131 | } while (true); | ||
132 | } | ||
133 | |||
65 | static int kernfs_fill_super(struct super_block *sb, unsigned long magic) | 134 | static int kernfs_fill_super(struct super_block *sb, unsigned long magic) |
66 | { | 135 | { |
67 | struct kernfs_super_info *info = kernfs_info(sb); | 136 | struct kernfs_super_info *info = kernfs_info(sb); |