aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-02-07 13:32:07 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-07 19:05:35 -0500
commit3eef34ad7dc369b7183ec383908aff3da2f6e5ec (patch)
treefac6ef1dd497adeff55fae499d953d5d51061867 /fs
parent0c23b2259a4850494e2c53e864ea840597c6cdd3 (diff)
kernfs: implement kernfs_get_parent(), kernfs_name/path() and friends
kernfs_node->parent and ->name are currently marked as "published" indicating that kernfs users may access them directly; however, those fields may get updated by kernfs_rename[_ns]() and unrestricted access may lead to erroneous values or oops. Protect ->parent and ->name updates with a irq-safe spinlock kernfs_rename_lock and implement the following accessors for these fields. * kernfs_name() - format the node's name into the specified buffer * kernfs_path() - format the node's path into the specified buffer * pr_cont_kernfs_name() - pr_cont a node's name (doesn't need buffer) * pr_cont_kernfs_path() - pr_cont a node's path (doesn't need buffer) * kernfs_get_parent() - pin and return a node's parent All can be called under any context. The recursive sysfs_pathname() in fs/sysfs/dir.c is replaced with kernfs_path() and sysfs_rename_dir_ns() is updated to use kernfs_get_parent() instead of dereferencing parent directly. v2: Dummy definition of kernfs_path() for !CONFIG_KERNFS was missing static inline making it cause a lot of build warnings. Add it. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/kernfs/dir.c175
-rw-r--r--fs/sysfs/dir.c44
2 files changed, 178 insertions, 41 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 42a250f83b98..a347792c2e5a 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -19,6 +19,8 @@
19#include "kernfs-internal.h" 19#include "kernfs-internal.h"
20 20
21DEFINE_MUTEX(kernfs_mutex); 21DEFINE_MUTEX(kernfs_mutex);
22static DEFINE_SPINLOCK(kernfs_rename_lock); /* kn->parent and ->name */
23static char kernfs_pr_cont_buf[PATH_MAX]; /* protected by rename_lock */
22 24
23#define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) 25#define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb)
24 26
@@ -37,6 +39,141 @@ static bool kernfs_lockdep(struct kernfs_node *kn)
37#endif 39#endif
38} 40}
39 41
42static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen)
43{
44 return strlcpy(buf, kn->parent ? kn->name : "/", buflen);
45}
46
47static char * __must_check kernfs_path_locked(struct kernfs_node *kn, char *buf,
48 size_t buflen)
49{
50 char *p = buf + buflen;
51 int len;
52
53 *--p = '\0';
54
55 do {
56 len = strlen(kn->name);
57 if (p - buf < len + 1) {
58 buf[0] = '\0';
59 p = NULL;
60 break;
61 }
62 p -= len;
63 memcpy(p, kn->name, len);
64 *--p = '/';
65 kn = kn->parent;
66 } while (kn && kn->parent);
67
68 return p;
69}
70
71/**
72 * kernfs_name - obtain the name of a given node
73 * @kn: kernfs_node of interest
74 * @buf: buffer to copy @kn's name into
75 * @buflen: size of @buf
76 *
77 * Copies the name of @kn into @buf of @buflen bytes. The behavior is
78 * similar to strlcpy(). It returns the length of @kn's name and if @buf
79 * isn't long enough, it's filled upto @buflen-1 and nul terminated.
80 *
81 * This function can be called from any context.
82 */
83int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
84{
85 unsigned long flags;
86 int ret;
87
88 spin_lock_irqsave(&kernfs_rename_lock, flags);
89 ret = kernfs_name_locked(kn, buf, buflen);
90 spin_unlock_irqrestore(&kernfs_rename_lock, flags);
91 return ret;
92}
93
94/**
95 * kernfs_path - build full path of a given node
96 * @kn: kernfs_node of interest
97 * @buf: buffer to copy @kn's name into
98 * @buflen: size of @buf
99 *
100 * Builds and returns the full path of @kn in @buf of @buflen bytes. The
101 * path is built from the end of @buf so the returned pointer usually
102 * doesn't match @buf. If @buf isn't long enough, @buf is nul terminated
103 * and %NULL is returned.
104 */
105char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen)
106{
107 unsigned long flags;
108 char *p;
109
110 spin_lock_irqsave(&kernfs_rename_lock, flags);
111 p = kernfs_path_locked(kn, buf, buflen);
112 spin_unlock_irqrestore(&kernfs_rename_lock, flags);
113 return p;
114}
115
116/**
117 * pr_cont_kernfs_name - pr_cont name of a kernfs_node
118 * @kn: kernfs_node of interest
119 *
120 * This function can be called from any context.
121 */
122void pr_cont_kernfs_name(struct kernfs_node *kn)
123{
124 unsigned long flags;
125
126 spin_lock_irqsave(&kernfs_rename_lock, flags);
127
128 kernfs_name_locked(kn, kernfs_pr_cont_buf, sizeof(kernfs_pr_cont_buf));
129 pr_cont("%s", kernfs_pr_cont_buf);
130
131 spin_unlock_irqrestore(&kernfs_rename_lock, flags);
132}
133
134/**
135 * pr_cont_kernfs_path - pr_cont path of a kernfs_node
136 * @kn: kernfs_node of interest
137 *
138 * This function can be called from any context.
139 */
140void pr_cont_kernfs_path(struct kernfs_node *kn)
141{
142 unsigned long flags;
143 char *p;
144
145 spin_lock_irqsave(&kernfs_rename_lock, flags);
146
147 p = kernfs_path_locked(kn, kernfs_pr_cont_buf,
148 sizeof(kernfs_pr_cont_buf));
149 if (p)
150 pr_cont("%s", p);
151 else
152 pr_cont("<name too long>");
153
154 spin_unlock_irqrestore(&kernfs_rename_lock, flags);
155}
156
157/**
158 * kernfs_get_parent - determine the parent node and pin it
159 * @kn: kernfs_node of interest
160 *
161 * Determines @kn's parent, pins and returns it. This function can be
162 * called from any context.
163 */
164struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn)
165{
166 struct kernfs_node *parent;
167 unsigned long flags;
168
169 spin_lock_irqsave(&kernfs_rename_lock, flags);
170 parent = kn->parent;
171 kernfs_get(parent);
172 spin_unlock_irqrestore(&kernfs_rename_lock, flags);
173
174 return parent;
175}
176
40/** 177/**
41 * kernfs_name_hash 178 * kernfs_name_hash
42 * @name: Null terminated string to hash 179 * @name: Null terminated string to hash
@@ -1103,8 +1240,14 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
1103int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, 1240int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
1104 const char *new_name, const void *new_ns) 1241 const char *new_name, const void *new_ns)
1105{ 1242{
1243 struct kernfs_node *old_parent;
1244 const char *old_name = NULL;
1106 int error; 1245 int error;
1107 1246
1247 /* can't move or rename root */
1248 if (!kn->parent)
1249 return -EINVAL;
1250
1108 mutex_lock(&kernfs_mutex); 1251 mutex_lock(&kernfs_mutex);
1109 1252
1110 error = -ENOENT; 1253 error = -ENOENT;
@@ -1126,13 +1269,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
1126 new_name = kstrdup(new_name, GFP_KERNEL); 1269 new_name = kstrdup(new_name, GFP_KERNEL);
1127 if (!new_name) 1270 if (!new_name)
1128 goto out; 1271 goto out;
1129 1272 } else {
1130 if (kn->flags & KERNFS_STATIC_NAME) 1273 new_name = NULL;
1131 kn->flags &= ~KERNFS_STATIC_NAME;
1132 else
1133 kfree(kn->name);
1134
1135 kn->name = new_name;
1136 } 1274 }
1137 1275
1138 /* 1276 /*
@@ -1140,12 +1278,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
1140 */ 1278 */
1141 kernfs_unlink_sibling(kn); 1279 kernfs_unlink_sibling(kn);
1142 kernfs_get(new_parent); 1280 kernfs_get(new_parent);
1143 kernfs_put(kn->parent); 1281
1144 kn->ns = new_ns; 1282 /* rename_lock protects ->parent and ->name accessors */
1145 kn->hash = kernfs_name_hash(kn->name, kn->ns); 1283 spin_lock_irq(&kernfs_rename_lock);
1284
1285 old_parent = kn->parent;
1146 kn->parent = new_parent; 1286 kn->parent = new_parent;
1287
1288 kn->ns = new_ns;
1289 if (new_name) {
1290 if (!(kn->flags & KERNFS_STATIC_NAME))
1291 old_name = kn->name;
1292 kn->flags &= ~KERNFS_STATIC_NAME;
1293 kn->name = new_name;
1294 }
1295
1296 spin_unlock_irq(&kernfs_rename_lock);
1297
1298 kn->hash = kernfs_name_hash(new_name, new_ns);
1147 kernfs_link_sibling(kn); 1299 kernfs_link_sibling(kn);
1148 1300
1301 kernfs_put(old_parent);
1302 kfree(old_name);
1303
1149 error = 0; 1304 error = 0;
1150 out: 1305 out:
1151 mutex_unlock(&kernfs_mutex); 1306 mutex_unlock(&kernfs_mutex);
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index ee0d761c3179..0b45ff42f374 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -19,39 +19,18 @@
19 19
20DEFINE_SPINLOCK(sysfs_symlink_target_lock); 20DEFINE_SPINLOCK(sysfs_symlink_target_lock);
21 21
22/**
23 * sysfs_pathname - return full path to sysfs dirent
24 * @kn: kernfs_node whose path we want
25 * @path: caller allocated buffer of size PATH_MAX
26 *
27 * Gives the name "/" to the sysfs_root entry; any path returned
28 * is relative to wherever sysfs is mounted.
29 */
30static char *sysfs_pathname(struct kernfs_node *kn, char *path)
31{
32 if (kn->parent) {
33 sysfs_pathname(kn->parent, path);
34 strlcat(path, "/", PATH_MAX);
35 }
36 strlcat(path, kn->name, PATH_MAX);
37 return path;
38}
39
40void sysfs_warn_dup(struct kernfs_node *parent, const char *name) 22void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
41{ 23{
42 char *path; 24 char *buf, *path = NULL;
43 25
44 path = kzalloc(PATH_MAX, GFP_KERNEL); 26 buf = kzalloc(PATH_MAX, GFP_KERNEL);
45 if (path) { 27 if (buf)
46 sysfs_pathname(parent, path); 28 path = kernfs_path(parent, buf, PATH_MAX);
47 strlcat(path, "/", PATH_MAX);
48 strlcat(path, name, PATH_MAX);
49 }
50 29
51 WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s'\n", 30 WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n",
52 path ? path : name); 31 path, name);
53 32
54 kfree(path); 33 kfree(buf);
55} 34}
56 35
57/** 36/**
@@ -122,9 +101,13 @@ void sysfs_remove_dir(struct kobject *kobj)
122int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, 101int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
123 const void *new_ns) 102 const void *new_ns)
124{ 103{
125 struct kernfs_node *parent = kobj->sd->parent; 104 struct kernfs_node *parent;
105 int ret;
126 106
127 return kernfs_rename_ns(kobj->sd, parent, new_name, new_ns); 107 parent = kernfs_get_parent(kobj->sd);
108 ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
109 kernfs_put(parent);
110 return ret;
128} 111}
129 112
130int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, 113int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
@@ -133,7 +116,6 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
133 struct kernfs_node *kn = kobj->sd; 116 struct kernfs_node *kn = kobj->sd;
134 struct kernfs_node *new_parent; 117 struct kernfs_node *new_parent;
135 118
136 BUG_ON(!kn->parent);
137 new_parent = new_parent_kobj && new_parent_kobj->sd ? 119 new_parent = new_parent_kobj && new_parent_kobj->sd ?
138 new_parent_kobj->sd : sysfs_root_kn; 120 new_parent_kobj->sd : sysfs_root_kn;
139 121