diff options
author | Louis Rilling <louis.rilling@kerlabs.com> | 2008-07-04 10:56:05 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-07-31 19:21:13 -0400 |
commit | 2a109f2a4155f168047aa2f5b3a170e279bef89a (patch) | |
tree | 8e9a080046fb1abdba10e288b89e92cc4c39f6ea /fs/configfs/symlink.c | |
parent | 9a73d78cda750f12e25eb811878f2d9dbab1bc6e (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/symlink.c')
-rw-r--r-- | fs/configfs/symlink.c | 15 |
1 files changed, 15 insertions, 0 deletions
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 | ||
106 | out: | ||
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; |