diff options
Diffstat (limited to 'fs/kernfs')
-rw-r--r-- | fs/kernfs/dir.c | 210 | ||||
-rw-r--r-- | fs/kernfs/mount.c | 69 |
2 files changed, 243 insertions, 36 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 996b7742c90b..03b688d19f69 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c | |||
@@ -44,28 +44,122 @@ static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen) | |||
44 | return strlcpy(buf, kn->parent ? kn->name : "/", buflen); | 44 | return strlcpy(buf, kn->parent ? kn->name : "/", buflen); |
45 | } | 45 | } |
46 | 46 | ||
47 | static char * __must_check kernfs_path_locked(struct kernfs_node *kn, char *buf, | 47 | /* kernfs_node_depth - compute depth from @from to @to */ |
48 | size_t buflen) | 48 | static size_t kernfs_depth(struct kernfs_node *from, struct kernfs_node *to) |
49 | { | 49 | { |
50 | char *p = buf + buflen; | 50 | size_t depth = 0; |
51 | int len; | ||
52 | 51 | ||
53 | *--p = '\0'; | 52 | while (to->parent && to != from) { |
53 | depth++; | ||
54 | to = to->parent; | ||
55 | } | ||
56 | return depth; | ||
57 | } | ||
54 | 58 | ||
55 | do { | 59 | static struct kernfs_node *kernfs_common_ancestor(struct kernfs_node *a, |
56 | len = strlen(kn->name); | 60 | struct kernfs_node *b) |
57 | if (p - buf < len + 1) { | 61 | { |
58 | buf[0] = '\0'; | 62 | size_t da, db; |
59 | p = NULL; | 63 | struct kernfs_root *ra = kernfs_root(a), *rb = kernfs_root(b); |
60 | break; | 64 | |
61 | } | 65 | if (ra != rb) |
62 | p -= len; | 66 | return NULL; |
63 | memcpy(p, kn->name, len); | 67 | |
64 | *--p = '/'; | 68 | da = kernfs_depth(ra->kn, a); |
65 | kn = kn->parent; | 69 | db = kernfs_depth(rb->kn, b); |
66 | } while (kn && kn->parent); | 70 | |
71 | while (da > db) { | ||
72 | a = a->parent; | ||
73 | da--; | ||
74 | } | ||
75 | while (db > da) { | ||
76 | b = b->parent; | ||
77 | db--; | ||
78 | } | ||
79 | |||
80 | /* worst case b and a will be the same at root */ | ||
81 | while (b != a) { | ||
82 | b = b->parent; | ||
83 | a = a->parent; | ||
84 | } | ||
85 | |||
86 | return a; | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * kernfs_path_from_node_locked - find a pseudo-absolute path to @kn_to, | ||
91 | * where kn_from is treated as root of the path. | ||
92 | * @kn_from: kernfs node which should be treated as root for the path | ||
93 | * @kn_to: kernfs node to which path is needed | ||
94 | * @buf: buffer to copy the path into | ||
95 | * @buflen: size of @buf | ||
96 | * | ||
97 | * We need to handle couple of scenarios here: | ||
98 | * [1] when @kn_from is an ancestor of @kn_to at some level | ||
99 | * kn_from: /n1/n2/n3 | ||
100 | * kn_to: /n1/n2/n3/n4/n5 | ||
101 | * result: /n4/n5 | ||
102 | * | ||
103 | * [2] when @kn_from is on a different hierarchy and we need to find common | ||
104 | * ancestor between @kn_from and @kn_to. | ||
105 | * kn_from: /n1/n2/n3/n4 | ||
106 | * kn_to: /n1/n2/n5 | ||
107 | * result: /../../n5 | ||
108 | * OR | ||
109 | * kn_from: /n1/n2/n3/n4/n5 [depth=5] | ||
110 | * kn_to: /n1/n2/n3 [depth=3] | ||
111 | * result: /../.. | ||
112 | * | ||
113 | * return value: length of the string. If greater than buflen, | ||
114 | * then contents of buf are undefined. On error, -1 is returned. | ||
115 | */ | ||
116 | static int kernfs_path_from_node_locked(struct kernfs_node *kn_to, | ||
117 | struct kernfs_node *kn_from, | ||
118 | char *buf, size_t buflen) | ||
119 | { | ||
120 | struct kernfs_node *kn, *common; | ||
121 | const char parent_str[] = "/.."; | ||
122 | size_t depth_from, depth_to, len = 0, nlen = 0; | ||
123 | char *p; | ||
124 | int i; | ||
125 | |||
126 | if (!kn_from) | ||
127 | kn_from = kernfs_root(kn_to)->kn; | ||
128 | |||
129 | if (kn_from == kn_to) | ||
130 | return strlcpy(buf, "/", buflen); | ||
131 | |||
132 | common = kernfs_common_ancestor(kn_from, kn_to); | ||
133 | if (WARN_ON(!common)) | ||
134 | return -1; | ||
135 | |||
136 | depth_to = kernfs_depth(common, kn_to); | ||
137 | depth_from = kernfs_depth(common, kn_from); | ||
138 | |||
139 | if (buf) | ||
140 | buf[0] = '\0'; | ||
141 | |||
142 | for (i = 0; i < depth_from; i++) | ||
143 | len += strlcpy(buf + len, parent_str, | ||
144 | len < buflen ? buflen - len : 0); | ||
145 | |||
146 | /* Calculate how many bytes we need for the rest */ | ||
147 | for (kn = kn_to; kn != common; kn = kn->parent) | ||
148 | nlen += strlen(kn->name) + 1; | ||
149 | |||
150 | if (len + nlen >= buflen) | ||
151 | return len + nlen; | ||
152 | |||
153 | p = buf + len + nlen; | ||
154 | *p = '\0'; | ||
155 | for (kn = kn_to; kn != common; kn = kn->parent) { | ||
156 | nlen = strlen(kn->name); | ||
157 | p -= nlen; | ||
158 | memcpy(p, kn->name, nlen); | ||
159 | *(--p) = '/'; | ||
160 | } | ||
67 | 161 | ||
68 | return p; | 162 | return len + nlen; |
69 | } | 163 | } |
70 | 164 | ||
71 | /** | 165 | /** |
@@ -115,6 +209,34 @@ size_t kernfs_path_len(struct kernfs_node *kn) | |||
115 | } | 209 | } |
116 | 210 | ||
117 | /** | 211 | /** |
212 | * kernfs_path_from_node - build path of node @to relative to @from. | ||
213 | * @from: parent kernfs_node relative to which we need to build the path | ||
214 | * @to: kernfs_node of interest | ||
215 | * @buf: buffer to copy @to's path into | ||
216 | * @buflen: size of @buf | ||
217 | * | ||
218 | * Builds @to's path relative to @from in @buf. @from and @to must | ||
219 | * be on the same kernfs-root. If @from is not parent of @to, then a relative | ||
220 | * path (which includes '..'s) as needed to reach from @from to @to is | ||
221 | * returned. | ||
222 | * | ||
223 | * If @buf isn't long enough, the return value will be greater than @buflen | ||
224 | * and @buf contents are undefined. | ||
225 | */ | ||
226 | int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from, | ||
227 | char *buf, size_t buflen) | ||
228 | { | ||
229 | unsigned long flags; | ||
230 | int ret; | ||
231 | |||
232 | spin_lock_irqsave(&kernfs_rename_lock, flags); | ||
233 | ret = kernfs_path_from_node_locked(to, from, buf, buflen); | ||
234 | spin_unlock_irqrestore(&kernfs_rename_lock, flags); | ||
235 | return ret; | ||
236 | } | ||
237 | EXPORT_SYMBOL_GPL(kernfs_path_from_node); | ||
238 | |||
239 | /** | ||
118 | * kernfs_path - build full path of a given node | 240 | * kernfs_path - build full path of a given node |
119 | * @kn: kernfs_node of interest | 241 | * @kn: kernfs_node of interest |
120 | * @buf: buffer to copy @kn's name into | 242 | * @buf: buffer to copy @kn's name into |
@@ -127,13 +249,12 @@ size_t kernfs_path_len(struct kernfs_node *kn) | |||
127 | */ | 249 | */ |
128 | char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen) | 250 | char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen) |
129 | { | 251 | { |
130 | unsigned long flags; | 252 | int ret; |
131 | char *p; | ||
132 | 253 | ||
133 | spin_lock_irqsave(&kernfs_rename_lock, flags); | 254 | ret = kernfs_path_from_node(kn, NULL, buf, buflen); |
134 | p = kernfs_path_locked(kn, buf, buflen); | 255 | if (ret < 0 || ret >= buflen) |
135 | spin_unlock_irqrestore(&kernfs_rename_lock, flags); | 256 | return NULL; |
136 | return p; | 257 | return buf; |
137 | } | 258 | } |
138 | EXPORT_SYMBOL_GPL(kernfs_path); | 259 | EXPORT_SYMBOL_GPL(kernfs_path); |
139 | 260 | ||
@@ -164,17 +285,25 @@ void pr_cont_kernfs_name(struct kernfs_node *kn) | |||
164 | void pr_cont_kernfs_path(struct kernfs_node *kn) | 285 | void pr_cont_kernfs_path(struct kernfs_node *kn) |
165 | { | 286 | { |
166 | unsigned long flags; | 287 | unsigned long flags; |
167 | char *p; | 288 | int sz; |
168 | 289 | ||
169 | spin_lock_irqsave(&kernfs_rename_lock, flags); | 290 | spin_lock_irqsave(&kernfs_rename_lock, flags); |
170 | 291 | ||
171 | p = kernfs_path_locked(kn, kernfs_pr_cont_buf, | 292 | sz = kernfs_path_from_node_locked(kn, NULL, kernfs_pr_cont_buf, |
172 | sizeof(kernfs_pr_cont_buf)); | 293 | sizeof(kernfs_pr_cont_buf)); |
173 | if (p) | 294 | if (sz < 0) { |
174 | pr_cont("%s", p); | 295 | pr_cont("(error)"); |
175 | else | 296 | goto out; |
176 | pr_cont("<name too long>"); | 297 | } |
298 | |||
299 | if (sz >= sizeof(kernfs_pr_cont_buf)) { | ||
300 | pr_cont("(name too long)"); | ||
301 | goto out; | ||
302 | } | ||
177 | 303 | ||
304 | pr_cont("%s", kernfs_pr_cont_buf); | ||
305 | |||
306 | out: | ||
178 | spin_unlock_irqrestore(&kernfs_rename_lock, flags); | 307 | spin_unlock_irqrestore(&kernfs_rename_lock, flags); |
179 | } | 308 | } |
180 | 309 | ||
@@ -691,15 +820,22 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent, | |||
691 | const unsigned char *path, | 820 | const unsigned char *path, |
692 | const void *ns) | 821 | const void *ns) |
693 | { | 822 | { |
694 | static char path_buf[PATH_MAX]; /* protected by kernfs_mutex */ | 823 | size_t len; |
695 | size_t len = strlcpy(path_buf, path, PATH_MAX); | 824 | char *p, *name; |
696 | char *p = path_buf; | ||
697 | char *name; | ||
698 | 825 | ||
699 | lockdep_assert_held(&kernfs_mutex); | 826 | lockdep_assert_held(&kernfs_mutex); |
700 | 827 | ||
701 | if (len >= PATH_MAX) | 828 | /* grab kernfs_rename_lock to piggy back on kernfs_pr_cont_buf */ |
829 | spin_lock_irq(&kernfs_rename_lock); | ||
830 | |||
831 | len = strlcpy(kernfs_pr_cont_buf, path, sizeof(kernfs_pr_cont_buf)); | ||
832 | |||
833 | if (len >= sizeof(kernfs_pr_cont_buf)) { | ||
834 | spin_unlock_irq(&kernfs_rename_lock); | ||
702 | return NULL; | 835 | return NULL; |
836 | } | ||
837 | |||
838 | p = kernfs_pr_cont_buf; | ||
703 | 839 | ||
704 | while ((name = strsep(&p, "/")) && parent) { | 840 | while ((name = strsep(&p, "/")) && parent) { |
705 | if (*name == '\0') | 841 | if (*name == '\0') |
@@ -707,6 +843,8 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent, | |||
707 | parent = kernfs_find_ns(parent, name, ns); | 843 | parent = kernfs_find_ns(parent, name, ns); |
708 | } | 844 | } |
709 | 845 | ||
846 | spin_unlock_irq(&kernfs_rename_lock); | ||
847 | |||
710 | return parent; | 848 | return parent; |
711 | } | 849 | } |
712 | 850 | ||
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); |