aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/kernfs/dir.c')
-rw-r--r--fs/kernfs/dir.c210
1 files changed, 174 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
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; 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 */
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 }
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 */
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 }
177 303
304 pr_cont("%s", kernfs_pr_cont_buf);
305
306out:
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