Port patch diff-ve-fs-process-mount-options-check-and-insert by Maxim Patlasov:
The patch implements two kinds of processing mount options: check and insert.
Check is OK if and only if each option supplied by CT-user is present
among options listed in allowed_options.
Insert transforms mount options supplied by CT-user like this:
<mount_options> = <hidden_options> + <user_supplied_mount_options>
Check is performed both for mount and remount. Insert - only for mount. All
this happens only for mount/remount inside CT and if proper ve_devmnt struct
is found in ve->devmnt_list (searched by 'dev').
Signed-off-by: Kirill Tkhai <ktk...@odin.com>
---
fs/namespace.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/super.c | 17 ++++++-
include/linux/fs.h | 2 +
3 files changed, 153 insertions(+), 2 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index aff3577..0330418 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -23,6 +23,7 @@
#include <linux/uaccess.h>
#include <linux/proc_ns.h>
#include <linux/magic.h>
+#include <linux/ve.h>
#include "pnode.h"
#include "internal.h"
@@ -1790,6 +1791,139 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
return error;
}
+#ifdef CONFIG_VE
+/*
+ * Returns first occurrence of needle in haystack separated by sep,
+ * or NULL if not found
+ */
+static char *strstr_separated(char *haystack, char *needle, char sep)
+{
+ int needle_len = strlen(needle);
+
+ while (haystack) {
+ if (!strncmp(haystack, needle, needle_len) &&
+ (haystack[needle_len] == 0 || /* end-of-line or */
+ haystack[needle_len] == sep)) /* separator */
+ return haystack;
+
+ haystack = strchr(haystack, sep);
+ if (haystack)
+ haystack++;
+ }
+
+ return NULL;
+}
+
+static int ve_devmnt_check(char *options, char *allowed)
+{
+ char *p;
+
+ if (!options || !*options)
+ return 0;
+
+ if (!allowed)
+ return -EPERM;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ if (!*p)
+ continue;
+
+ if (!strstr_separated(allowed, p, ','))
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int ve_devmnt_insert(char *options, char *hidden)
+{
+ int options_len;
+ int hidden_len;
+
+ if (!hidden)
+ return 0;
+
+ if (!options)
+ return -EAGAIN;
+
+ options_len = strlen(options);
+ hidden_len = strlen(hidden);
+
+ if (hidden_len + options_len + 2 > PAGE_SIZE)
+ return -EPERM;
+
+ memmove(options + hidden_len + 1, options, options_len);
+ memcpy(options, hidden, hidden_len);
+
+ options[hidden_len] = ',';
+ options[hidden_len + options_len + 1] = 0;
+
+ return 0;
+}
+
+int ve_devmnt_process(struct ve_struct *ve, dev_t dev, void **data_pp, int
remount)
+{
+ void *data = *data_pp;
+ struct ve_devmnt *devmnt;
+ int err;
+again:
+ err = 1;
+ mutex_lock(&ve->devmnt_mutex);
+ list_for_each_entry(devmnt, &ve->devmnt_list, link) {
+ if (devmnt->dev == dev) {
+ err = ve_devmnt_check(data, devmnt->allowed_options);
+
+ if (!err && !remount)
+ err = ve_devmnt_insert(data,
devmnt->hidden_options);
+
+ break;
+ }
+ }
+ mutex_unlock(&ve->devmnt_mutex);
+
+ switch (err) {
+ case -EAGAIN:
+ if (!(data = (void *)__get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+ *(char *)data = 0; /* the string must be zero-terminated */
+ goto again;
+ case 1:
+ if (*data_pp) {
+ ve_printk(VE_LOG_BOTH, KERN_WARNING "VE%u: no allowed "
+ "mount options found for device %u:%u\n",
+ ve->veid, MAJOR(dev), MINOR(dev));
+ err = -EPERM;
+ } else
+ err = 0;
+ break;
+ case 0:
+ *data_pp = data;
+ break;
+ }
+
+ if (data && data != *data_pp)
+ free_page((unsigned long)data);
+
+ return err;
+}
+#endif
+
+static int do_check_and_remount_sb(struct super_block *sb, int flags, void
*data)
+{
+#ifdef CONFIG_VE
+ struct ve_struct *ve = get_exec_env();
+
+ if (sb->s_bdev && data && !ve_is_super(ve)) {
+ int err;
+
+ err = ve_devmnt_process(ve, sb->s_bdev->bd_dev, &data, 1);
+ if (err)
+ return err;
+ }
+#endif
+ return do_remount_sb(sb, flags, data, 0);
+}
+
/*
* change filesystem flags. dir should be a physical root of filesystem.
* If you've mounted a non-root directory somewhere and want to do remount
@@ -1818,7 +1952,7 @@ static int do_remount(struct path *path, int flags, int
mnt_flags,
else if (!capable(CAP_SYS_ADMIN))
err = -EPERM;
else
- err = do_remount_sb(sb, flags, data, 0);
+ err = do_check_and_remount_sb(sb, flags, data);
if (!err) {
br_write_lock(&vfsmount_lock);
mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK;
diff --git a/fs/super.c b/fs/super.c
index 5314868..e3ff0bf 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1034,11 +1034,26 @@ struct dentry *mount_bdev(struct file_system_type
*fs_type,
down_write(&s->s_umount);
} else {
char b[BDEVNAME_SIZE];
-
+#ifdef CONFIG_VE
+ void *data_orig = data;
+ struct ve_struct *ve = get_exec_env();
+
+ if (!ve_is_super(ve)) {
+ error = ve_devmnt_process(ve, bdev->bd_dev, &data, 0);
+ if (error) {
+ deactivate_locked_super(s);
+ goto error;
+ }
+ }
+#endif
s->s_mode = mode;
strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
sb_set_blocksize(s, block_size(bdev));
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
+#ifdef CONFIG_VE
+ if (data_orig != data)
+ free_page((unsigned long)data);
+#endif
if (error) {
deactivate_locked_super(s);
goto error;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 9efd253..a2044f6 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2033,6 +2033,8 @@ extern bool our_mnt(struct vfsmount *mnt);
extern int current_umask(void);
+extern int ve_devmnt_process(struct ve_struct *, dev_t, void **, int);
+
extern void ihold(struct inode * inode);
extern void iput(struct inode *);