diff options
author | Aditya Kali <adityakali@google.com> | 2016-01-29 03:54:04 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2016-02-16 13:04:58 -0500 |
commit | 9f6df573a4041f896cbf51f1b3743494196620a7 (patch) | |
tree | 4ff0db9f1e815317630b24fdd32154eb72187d9e | |
parent | 223ffb29f9723a4b485cacf6dc7e6d639fffc322 (diff) |
kernfs: Add API to generate relative kernfs path
The new function kernfs_path_from_node() generates and returns kernfs
path of a given kernfs_node relative to a given parent kernfs_node.
Signed-off-by: Aditya Kali <adityakali@google.com>
Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | fs/kernfs/dir.c | 191 | ||||
-rw-r--r-- | include/linux/kernfs.h | 9 |
2 files changed, 165 insertions, 35 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 821973853340..25d71a53cf43 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; | ||
61 | } | ||
62 | p -= len; | ||
63 | memcpy(p, kn->name, len); | ||
64 | *--p = '/'; | ||
65 | kn = kn->parent; | ||
66 | } while (kn && kn->parent); | ||
67 | 64 | ||
68 | return p; | 65 | if (ra != rb) |
66 | return NULL; | ||
67 | |||
68 | da = kernfs_depth(ra->kn, a); | ||
69 | db = kernfs_depth(rb->kn, b); | ||
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 | } | ||
161 | |||
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 | } | ||
303 | |||
304 | pr_cont("%s", kernfs_pr_cont_buf); | ||
177 | 305 | ||
306 | out: | ||
178 | spin_unlock_irqrestore(&kernfs_rename_lock, flags); | 307 | spin_unlock_irqrestore(&kernfs_rename_lock, flags); |
179 | } | 308 | } |
180 | 309 | ||
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index af51df35d749..716bfdede5f5 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h | |||
@@ -267,8 +267,9 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn) | |||
267 | 267 | ||
268 | int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen); | 268 | int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen); |
269 | size_t kernfs_path_len(struct kernfs_node *kn); | 269 | size_t kernfs_path_len(struct kernfs_node *kn); |
270 | char * __must_check kernfs_path(struct kernfs_node *kn, char *buf, | 270 | int kernfs_path_from_node(struct kernfs_node *root_kn, struct kernfs_node *kn, |
271 | size_t buflen); | 271 | char *buf, size_t buflen); |
272 | char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen); | ||
272 | void pr_cont_kernfs_name(struct kernfs_node *kn); | 273 | void pr_cont_kernfs_name(struct kernfs_node *kn); |
273 | void pr_cont_kernfs_path(struct kernfs_node *kn); | 274 | void pr_cont_kernfs_path(struct kernfs_node *kn); |
274 | struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn); | 275 | struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn); |
@@ -338,8 +339,8 @@ static inline int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen) | |||
338 | static inline size_t kernfs_path_len(struct kernfs_node *kn) | 339 | static inline size_t kernfs_path_len(struct kernfs_node *kn) |
339 | { return 0; } | 340 | { return 0; } |
340 | 341 | ||
341 | static inline char * __must_check kernfs_path(struct kernfs_node *kn, char *buf, | 342 | static inline char *kernfs_path(struct kernfs_node *kn, char *buf, |
342 | size_t buflen) | 343 | size_t buflen) |
343 | { return NULL; } | 344 | { return NULL; } |
344 | 345 | ||
345 | static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { } | 346 | static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { } |