aboutsummaryrefslogtreecommitdiffstats
path: root/fs/configfs
diff options
context:
space:
mode:
authorLouis Rilling <louis.rilling@kerlabs.com>2008-07-04 10:56:05 -0400
committerMark Fasheh <mfasheh@suse.com>2008-07-31 19:21:13 -0400
commit2a109f2a4155f168047aa2f5b3a170e279bef89a (patch)
tree8e9a080046fb1abdba10e288b89e92cc4c39f6ea /fs/configfs
parent9a73d78cda750f12e25eb811878f2d9dbab1bc6e (diff)
[PATCH] configfs: Prevent userspace from creating new entries under attaching directories
process 1: process 2: configfs_mkdir("A") attach_group("A") attach_item("A") d_instantiate("A") populate_groups("A") mutex_lock("A") attach_group("A/B") attach_item("A") d_instantiate("A/B") mkdir("A/B/C") do_path_lookup("A/B/C", LOOKUP_PARENT) ok lookup_create("A/B/C") mutex_lock("A/B") ok configfs_mkdir("A/B/C") ok attach_group("A/C") attach_item("A/C") d_instantiate("A/C") populate_groups("A/C") mutex_lock("A/C") attach_group("A/C/D") attach_item("A/C/D") failure mutex_unlock("A/C") detach_groups("A/C") nothing to do mkdir("A/C/E") do_path_lookup("A/C/E", LOOKUP_PARENT) ok lookup_create("A/C/E") mutex_lock("A/C") ok configfs_mkdir("A/C/E") ok detach_item("A/C") d_delete("A/C") mutex_unlock("A") detach_groups("A") mutex_lock("A/B") detach_group("A/B") detach_groups("A/B") nothing since no _default_ group detach_item("A/B") mutex_unlock("A/B") d_delete("A/B") detach_item("A") d_delete("A") Two bugs: 1/ "A/B/C" and "A/C/E" are created, but never removed while their parent are removed in the end. The same could happen with symlink() instead of mkdir(). 2/ "A" and "A/C" inodes are not locked while detach_item() is called on them, which may probably confuse VFS. This commit fixes 1/, tagging new directories with CONFIGFS_USET_CREATING before building the inode and instantiating the dentry, and validating the whole group+default groups hierarchy in a second pass by clearing CONFIGFS_USET_CREATING. mkdir(), symlink(), lookup(), and dir_open() simply return -ENOENT if called in (or linking to) a directory tagged with CONFIGFS_USET_CREATING. This does not prevent userspace from calling stat() successfuly on such directories, but this prevents userspace from adding (children to | symlinking from/to | read/write attributes of | listing the contents of) not validated items. In other words, userspace will not interact with the subsystem on a new item until the new item creation completes correctly. It was first proposed to re-use CONFIGFS_USET_IN_MKDIR instead of a new flag CONFIGFS_USET_CREATING, but this generated conflicts when checking the target of a new symlink: a valid target directory in the middle of attaching a new user-created child item could be wrongly detected as being attached. 2/ is fixed by next commit. Signed-off-by: Louis Rilling <louis.rilling@kerlabs.com> Signed-off-by: Joel Becker <joel.becker@oracle.com> Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs/configfs')
-rw-r--r--fs/configfs/configfs_internal.h2
-rw-r--r--fs/configfs/dir.c91
-rw-r--r--fs/configfs/symlink.c15
3 files changed, 103 insertions, 5 deletions
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index 5f61b26eba9e..762d287123ca 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -49,6 +49,7 @@ struct configfs_dirent {
49#define CONFIGFS_USET_DEFAULT 0x0080 49#define CONFIGFS_USET_DEFAULT 0x0080
50#define CONFIGFS_USET_DROPPING 0x0100 50#define CONFIGFS_USET_DROPPING 0x0100
51#define CONFIGFS_USET_IN_MKDIR 0x0200 51#define CONFIGFS_USET_IN_MKDIR 0x0200
52#define CONFIGFS_USET_CREATING 0x0400
52#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) 53#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR)
53 54
54extern struct mutex configfs_symlink_mutex; 55extern struct mutex configfs_symlink_mutex;
@@ -67,6 +68,7 @@ extern void configfs_inode_exit(void);
67extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); 68extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
68extern int configfs_make_dirent(struct configfs_dirent *, 69extern int configfs_make_dirent(struct configfs_dirent *,
69 struct dentry *, void *, umode_t, int); 70 struct dentry *, void *, umode_t, int);
71extern int configfs_dirent_is_ready(struct configfs_dirent *);
70 72
71extern int configfs_add_file(struct dentry *, const struct configfs_attribute *, int); 73extern int configfs_add_file(struct dentry *, const struct configfs_attribute *, int);
72extern void configfs_hash_and_remove(struct dentry * dir, const char * name); 74extern void configfs_hash_and_remove(struct dentry * dir, const char * name);
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 4e228c80fe9f..647499a3479b 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -185,7 +185,7 @@ static int create_dir(struct config_item * k, struct dentry * p,
185 error = configfs_dirent_exists(p->d_fsdata, d->d_name.name); 185 error = configfs_dirent_exists(p->d_fsdata, d->d_name.name);
186 if (!error) 186 if (!error)
187 error = configfs_make_dirent(p->d_fsdata, d, k, mode, 187 error = configfs_make_dirent(p->d_fsdata, d, k, mode,
188 CONFIGFS_DIR); 188 CONFIGFS_DIR | CONFIGFS_USET_CREATING);
189 if (!error) { 189 if (!error) {
190 error = configfs_create(d, mode, init_dir); 190 error = configfs_create(d, mode, init_dir);
191 if (!error) { 191 if (!error) {
@@ -209,6 +209,9 @@ static int create_dir(struct config_item * k, struct dentry * p,
209 * configfs_create_dir - create a directory for an config_item. 209 * configfs_create_dir - create a directory for an config_item.
210 * @item: config_itemwe're creating directory for. 210 * @item: config_itemwe're creating directory for.
211 * @dentry: config_item's dentry. 211 * @dentry: config_item's dentry.
212 *
213 * Note: user-created entries won't be allowed under this new directory
214 * until it is validated by configfs_dir_set_ready()
212 */ 215 */
213 216
214static int configfs_create_dir(struct config_item * item, struct dentry *dentry) 217static int configfs_create_dir(struct config_item * item, struct dentry *dentry)
@@ -231,6 +234,44 @@ static int configfs_create_dir(struct config_item * item, struct dentry *dentry)
231 return error; 234 return error;
232} 235}
233 236
237/*
238 * Allow userspace to create new entries under a new directory created with
239 * configfs_create_dir(), and under all of its chidlren directories recursively.
240 * @sd configfs_dirent of the new directory to validate
241 *
242 * Caller must hold configfs_dirent_lock.
243 */
244static void configfs_dir_set_ready(struct configfs_dirent *sd)
245{
246 struct configfs_dirent *child_sd;
247
248 sd->s_type &= ~CONFIGFS_USET_CREATING;
249 list_for_each_entry(child_sd, &sd->s_children, s_sibling)
250 if (child_sd->s_type & CONFIGFS_USET_CREATING)
251 configfs_dir_set_ready(child_sd);
252}
253
254/*
255 * Check that a directory does not belong to a directory hierarchy being
256 * attached and not validated yet.
257 * @sd configfs_dirent of the directory to check
258 *
259 * @return non-zero iff the directory was validated
260 *
261 * Note: takes configfs_dirent_lock, so the result may change from false to true
262 * in two consecutive calls, but never from true to false.
263 */
264int configfs_dirent_is_ready(struct configfs_dirent *sd)
265{
266 int ret;
267
268 spin_lock(&configfs_dirent_lock);
269 ret = !(sd->s_type & CONFIGFS_USET_CREATING);
270 spin_unlock(&configfs_dirent_lock);
271
272 return ret;
273}
274
234int configfs_create_link(struct configfs_symlink *sl, 275int configfs_create_link(struct configfs_symlink *sl,
235 struct dentry *parent, 276 struct dentry *parent,
236 struct dentry *dentry) 277 struct dentry *dentry)
@@ -330,7 +371,19 @@ static struct dentry * configfs_lookup(struct inode *dir,
330 struct configfs_dirent * parent_sd = dentry->d_parent->d_fsdata; 371 struct configfs_dirent * parent_sd = dentry->d_parent->d_fsdata;
331 struct configfs_dirent * sd; 372 struct configfs_dirent * sd;
332 int found = 0; 373 int found = 0;
333 int err = 0; 374 int err;
375
376 /*
377 * Fake invisibility if dir belongs to a group/default groups hierarchy
378 * being attached
379 *
380 * This forbids userspace to read/write attributes of items which may
381 * not complete their initialization, since the dentries of the
382 * attributes won't be instantiated.
383 */
384 err = -ENOENT;
385 if (!configfs_dirent_is_ready(parent_sd))
386 goto out;
334 387
335 list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { 388 list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
336 if (sd->s_type & CONFIGFS_NOT_PINNED) { 389 if (sd->s_type & CONFIGFS_NOT_PINNED) {
@@ -353,6 +406,7 @@ static struct dentry * configfs_lookup(struct inode *dir,
353 return simple_lookup(dir, dentry, nd); 406 return simple_lookup(dir, dentry, nd);
354 } 407 }
355 408
409out:
356 return ERR_PTR(err); 410 return ERR_PTR(err);
357} 411}
358 412
@@ -1044,6 +1098,16 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
1044 } 1098 }
1045 1099
1046 sd = dentry->d_parent->d_fsdata; 1100 sd = dentry->d_parent->d_fsdata;
1101
1102 /*
1103 * Fake invisibility if dir belongs to a group/default groups hierarchy
1104 * being attached
1105 */
1106 if (!configfs_dirent_is_ready(sd)) {
1107 ret = -ENOENT;
1108 goto out;
1109 }
1110
1047 if (!(sd->s_type & CONFIGFS_USET_DIR)) { 1111 if (!(sd->s_type & CONFIGFS_USET_DIR)) {
1048 ret = -EPERM; 1112 ret = -EPERM;
1049 goto out; 1113 goto out;
@@ -1142,6 +1206,8 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
1142 1206
1143 spin_lock(&configfs_dirent_lock); 1207 spin_lock(&configfs_dirent_lock);
1144 sd->s_type &= ~CONFIGFS_USET_IN_MKDIR; 1208 sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
1209 if (!ret)
1210 configfs_dir_set_ready(dentry->d_fsdata);
1145 spin_unlock(&configfs_dirent_lock); 1211 spin_unlock(&configfs_dirent_lock);
1146 1212
1147out_unlink: 1213out_unlink:
@@ -1322,13 +1388,24 @@ static int configfs_dir_open(struct inode *inode, struct file *file)
1322{ 1388{
1323 struct dentry * dentry = file->f_path.dentry; 1389 struct dentry * dentry = file->f_path.dentry;
1324 struct configfs_dirent * parent_sd = dentry->d_fsdata; 1390 struct configfs_dirent * parent_sd = dentry->d_fsdata;
1391 int err;
1325 1392
1326 mutex_lock(&dentry->d_inode->i_mutex); 1393 mutex_lock(&dentry->d_inode->i_mutex);
1327 file->private_data = configfs_new_dirent(parent_sd, NULL); 1394 /*
1395 * Fake invisibility if dir belongs to a group/default groups hierarchy
1396 * being attached
1397 */
1398 err = -ENOENT;
1399 if (configfs_dirent_is_ready(parent_sd)) {
1400 file->private_data = configfs_new_dirent(parent_sd, NULL);
1401 if (IS_ERR(file->private_data))
1402 err = PTR_ERR(file->private_data);
1403 else
1404 err = 0;
1405 }
1328 mutex_unlock(&dentry->d_inode->i_mutex); 1406 mutex_unlock(&dentry->d_inode->i_mutex);
1329 1407
1330 return IS_ERR(file->private_data) ? PTR_ERR(file->private_data) : 0; 1408 return err;
1331
1332} 1409}
1333 1410
1334static int configfs_dir_close(struct inode *inode, struct file *file) 1411static int configfs_dir_close(struct inode *inode, struct file *file)
@@ -1499,6 +1576,10 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
1499 if (err) { 1576 if (err) {
1500 d_delete(dentry); 1577 d_delete(dentry);
1501 dput(dentry); 1578 dput(dentry);
1579 } else {
1580 spin_lock(&configfs_dirent_lock);
1581 configfs_dir_set_ready(dentry->d_fsdata);
1582 spin_unlock(&configfs_dirent_lock);
1502 } 1583 }
1503 } 1584 }
1504 1585
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index 61a886dbd601..bf74973b0492 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -76,6 +76,9 @@ static int create_link(struct config_item *parent_item,
76 struct configfs_symlink *sl; 76 struct configfs_symlink *sl;
77 int ret; 77 int ret;
78 78
79 ret = -ENOENT;
80 if (!configfs_dirent_is_ready(target_sd))
81 goto out;
79 ret = -ENOMEM; 82 ret = -ENOMEM;
80 sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); 83 sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL);
81 if (sl) { 84 if (sl) {
@@ -100,6 +103,7 @@ static int create_link(struct config_item *parent_item,
100 } 103 }
101 } 104 }
102 105
106out:
103 return ret; 107 return ret;
104} 108}
105 109
@@ -129,6 +133,7 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
129{ 133{
130 int ret; 134 int ret;
131 struct nameidata nd; 135 struct nameidata nd;
136 struct configfs_dirent *sd;
132 struct config_item *parent_item; 137 struct config_item *parent_item;
133 struct config_item *target_item; 138 struct config_item *target_item;
134 struct config_item_type *type; 139 struct config_item_type *type;
@@ -137,9 +142,19 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
137 if (dentry->d_parent == configfs_sb->s_root) 142 if (dentry->d_parent == configfs_sb->s_root)
138 goto out; 143 goto out;
139 144
145 sd = dentry->d_parent->d_fsdata;
146 /*
147 * Fake invisibility if dir belongs to a group/default groups hierarchy
148 * being attached
149 */
150 ret = -ENOENT;
151 if (!configfs_dirent_is_ready(sd))
152 goto out;
153
140 parent_item = configfs_get_config_item(dentry->d_parent); 154 parent_item = configfs_get_config_item(dentry->d_parent);
141 type = parent_item->ci_type; 155 type = parent_item->ci_type;
142 156
157 ret = -EPERM;
143 if (!type || !type->ct_item_ops || 158 if (!type || !type->ct_item_ops ||
144 !type->ct_item_ops->allow_link) 159 !type->ct_item_ops->allow_link)
145 goto out_put; 160 goto out_put;