summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAditya Kali <adityakali@google.com>2016-01-29 03:54:04 -0500
committerTejun Heo <tj@kernel.org>2016-02-16 13:04:58 -0500
commit9f6df573a4041f896cbf51f1b3743494196620a7 (patch)
tree4ff0db9f1e815317630b24fdd32154eb72187d9e
parent223ffb29f9723a4b485cacf6dc7e6d639fffc322 (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.c191
-rw-r--r--include/linux/kernfs.h9
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
47static 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) 48static 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 { 59static 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 */
116static 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 */
226int 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}
237EXPORT_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 */
128char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen) 250char *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}
138EXPORT_SYMBOL_GPL(kernfs_path); 259EXPORT_SYMBOL_GPL(kernfs_path);
139 260
@@ -164,17 +285,25 @@ void pr_cont_kernfs_name(struct kernfs_node *kn)
164void pr_cont_kernfs_path(struct kernfs_node *kn) 285void 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
306out:
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
268int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen); 268int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen);
269size_t kernfs_path_len(struct kernfs_node *kn); 269size_t kernfs_path_len(struct kernfs_node *kn);
270char * __must_check kernfs_path(struct kernfs_node *kn, char *buf, 270int kernfs_path_from_node(struct kernfs_node *root_kn, struct kernfs_node *kn,
271 size_t buflen); 271 char *buf, size_t buflen);
272char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen);
272void pr_cont_kernfs_name(struct kernfs_node *kn); 273void pr_cont_kernfs_name(struct kernfs_node *kn);
273void pr_cont_kernfs_path(struct kernfs_node *kn); 274void pr_cont_kernfs_path(struct kernfs_node *kn);
274struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn); 275struct 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)
338static inline size_t kernfs_path_len(struct kernfs_node *kn) 339static inline size_t kernfs_path_len(struct kernfs_node *kn)
339{ return 0; } 340{ return 0; }
340 341
341static inline char * __must_check kernfs_path(struct kernfs_node *kn, char *buf, 342static 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
345static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { } 346static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { }