diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2015-05-09 23:09:14 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2015-07-01 11:36:39 -0400 |
commit | f9bd6733d3f11e24f3949becf277507d422ee1eb (patch) | |
tree | 79d33823e26cb5be0af93bae37b2abca3fdaa5dc | |
parent | fbabfd0f4ee2e8847bf56edf481249ad1bb8c44d (diff) |
sysctl: Allow creating permanently empty directories that serve as mountpoints.
Add a magic sysctl table sysctl_mount_point that when used to
create a directory forces that directory to be permanently empty.
Update the code to use make_empty_dir_inode when accessing permanently
empty directories.
Update the code to not allow adding to permanently empty directories.
Update /proc/sys/fs/binfmt_misc to be a permanently empty directory.
Cc: stable@vger.kernel.org
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r-- | fs/proc/proc_sysctl.c | 37 | ||||
-rw-r--r-- | include/linux/sysctl.h | 3 | ||||
-rw-r--r-- | kernel/sysctl.c | 8 |
3 files changed, 41 insertions, 7 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index fea2561d773b..fdda62e6115e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -19,6 +19,28 @@ static const struct inode_operations proc_sys_inode_operations; | |||
19 | static const struct file_operations proc_sys_dir_file_operations; | 19 | static const struct file_operations proc_sys_dir_file_operations; |
20 | static const struct inode_operations proc_sys_dir_operations; | 20 | static const struct inode_operations proc_sys_dir_operations; |
21 | 21 | ||
22 | /* Support for permanently empty directories */ | ||
23 | |||
24 | struct ctl_table sysctl_mount_point[] = { | ||
25 | { } | ||
26 | }; | ||
27 | |||
28 | static bool is_empty_dir(struct ctl_table_header *head) | ||
29 | { | ||
30 | return head->ctl_table[0].child == sysctl_mount_point; | ||
31 | } | ||
32 | |||
33 | static void set_empty_dir(struct ctl_dir *dir) | ||
34 | { | ||
35 | dir->header.ctl_table[0].child = sysctl_mount_point; | ||
36 | } | ||
37 | |||
38 | static void clear_empty_dir(struct ctl_dir *dir) | ||
39 | |||
40 | { | ||
41 | dir->header.ctl_table[0].child = NULL; | ||
42 | } | ||
43 | |||
22 | void proc_sys_poll_notify(struct ctl_table_poll *poll) | 44 | void proc_sys_poll_notify(struct ctl_table_poll *poll) |
23 | { | 45 | { |
24 | if (!poll) | 46 | if (!poll) |
@@ -187,6 +209,17 @@ static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header) | |||
187 | struct ctl_table *entry; | 209 | struct ctl_table *entry; |
188 | int err; | 210 | int err; |
189 | 211 | ||
212 | /* Is this a permanently empty directory? */ | ||
213 | if (is_empty_dir(&dir->header)) | ||
214 | return -EROFS; | ||
215 | |||
216 | /* Am I creating a permanently empty directory? */ | ||
217 | if (header->ctl_table == sysctl_mount_point) { | ||
218 | if (!RB_EMPTY_ROOT(&dir->root)) | ||
219 | return -EINVAL; | ||
220 | set_empty_dir(dir); | ||
221 | } | ||
222 | |||
190 | dir->header.nreg++; | 223 | dir->header.nreg++; |
191 | header->parent = dir; | 224 | header->parent = dir; |
192 | err = insert_links(header); | 225 | err = insert_links(header); |
@@ -202,6 +235,8 @@ fail: | |||
202 | erase_header(header); | 235 | erase_header(header); |
203 | put_links(header); | 236 | put_links(header); |
204 | fail_links: | 237 | fail_links: |
238 | if (header->ctl_table == sysctl_mount_point) | ||
239 | clear_empty_dir(dir); | ||
205 | header->parent = NULL; | 240 | header->parent = NULL; |
206 | drop_sysctl_table(&dir->header); | 241 | drop_sysctl_table(&dir->header); |
207 | return err; | 242 | return err; |
@@ -419,6 +454,8 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, | |||
419 | inode->i_mode |= S_IFDIR; | 454 | inode->i_mode |= S_IFDIR; |
420 | inode->i_op = &proc_sys_dir_operations; | 455 | inode->i_op = &proc_sys_dir_operations; |
421 | inode->i_fop = &proc_sys_dir_file_operations; | 456 | inode->i_fop = &proc_sys_dir_file_operations; |
457 | if (is_empty_dir(head)) | ||
458 | make_empty_dir_inode(inode); | ||
422 | } | 459 | } |
423 | out: | 460 | out: |
424 | return inode; | 461 | return inode; |
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 795d5fea5697..fa7bc29925c9 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
@@ -188,6 +188,9 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | |||
188 | void unregister_sysctl_table(struct ctl_table_header * table); | 188 | void unregister_sysctl_table(struct ctl_table_header * table); |
189 | 189 | ||
190 | extern int sysctl_init(void); | 190 | extern int sysctl_init(void); |
191 | |||
192 | extern struct ctl_table sysctl_mount_point[]; | ||
193 | |||
191 | #else /* CONFIG_SYSCTL */ | 194 | #else /* CONFIG_SYSCTL */ |
192 | static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table) | 195 | static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table) |
193 | { | 196 | { |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2082b1a88fb9..c3eee4c6d6c1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -1531,12 +1531,6 @@ static struct ctl_table vm_table[] = { | |||
1531 | { } | 1531 | { } |
1532 | }; | 1532 | }; |
1533 | 1533 | ||
1534 | #if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE) | ||
1535 | static struct ctl_table binfmt_misc_table[] = { | ||
1536 | { } | ||
1537 | }; | ||
1538 | #endif | ||
1539 | |||
1540 | static struct ctl_table fs_table[] = { | 1534 | static struct ctl_table fs_table[] = { |
1541 | { | 1535 | { |
1542 | .procname = "inode-nr", | 1536 | .procname = "inode-nr", |
@@ -1690,7 +1684,7 @@ static struct ctl_table fs_table[] = { | |||
1690 | { | 1684 | { |
1691 | .procname = "binfmt_misc", | 1685 | .procname = "binfmt_misc", |
1692 | .mode = 0555, | 1686 | .mode = 0555, |
1693 | .child = binfmt_misc_table, | 1687 | .child = sysctl_mount_point, |
1694 | }, | 1688 | }, |
1695 | #endif | 1689 | #endif |
1696 | { | 1690 | { |