History

vfs: syscall: Add move_mount(2) to move mounts around

Add a move_mount() system call that will move a mount from one place to another and, in the next commit, allow to attach an unattached mount tree. The new system call looks like the following: int move_mount(int from_dfd, const char *from_path, int to_dfd, const char *to_path, unsigned int flags);

David Howells committed 8 years ago

Commit 2db154b1 parent a07b2008 files · +130 −32Browse files

Unified · Split

8 files
  1. arch/x86/entry/syscalls/syscall_32.tbl +2 −1
  2. arch/x86/entry/syscalls/syscall_64.tbl +1 −0
  3. fs/namespace.c +95 −31
  4. include/linux/lsm_hooks.h +6 −0
  5. include/linux/security.h +7 −0
  6. include/linux/syscalls.h +3 −0
  7. include/uapi/linux/mount.h +11 −0
  8. security/security.c +5 −0
arch/x86/entry/syscalls/syscall_32.tbl+2−1
@@ -399,7 +399,8 @@ · 398 unmodified lines
399399 385 i386 io_pgetevents sys_io_pgetevents_time32 __ia32_compat_sys_io_pgetevents
400400 386 i386 rseq sys_rseq __ia32_sys_rseq
401401 387 i386 open_tree sys_open_tree __ia32_sys_open_tree
402−# don't use numbers 388 through 392, add new calls at the end
402+388 i386 move_mount sys_move_mount __ia32_sys_move_mount
403+# don't use numbers 389 through 392, add new calls at the end
403404 393 i386 semget sys_semget __ia32_sys_semget
404405 394 i386 semctl sys_semctl __ia32_compat_sys_semctl
405406 395 i386 shmget sys_shmget __ia32_sys_shmget
arch/x86/entry/syscalls/syscall_64.tbl+1−0
@@ -344,6 +344,7 @@ · 343 unmodified lines
344344 333 common io_pgetevents __x64_sys_io_pgetevents
345345 334 common rseq __x64_sys_rseq
346346 335 common open_tree __x64_sys_open_tree
347+336 common move_mount __x64_sys_move_mount
347348 # don't use numbers 387 through 423, add new calls after the last
348349 # 'common' entry
349350 424 common pidfd_send_signal __x64_sys_pidfd_send_signal
fs/namespace.c+95−31
@@ -2539,72 +2539,81 @@ static inline int tree_contains_unbindable(struct mount *mnt) · 2538 unmodified lines
25392539 return 0;
25402540 }
25412541
2542−static int do_move_mount(struct path *path, const char *old_name)
2542+static int do_move_mount(struct path *old_path, struct path *new_path)
25432543 {
2544− struct path old_path, parent_path;
2544+ struct path parent_path = {.mnt = NULL, .dentry = NULL};
25452545 struct mount *p;
25462546 struct mount *old;
25472547 struct mountpoint *mp;
25482548 int err;
2549− if (!old_name || !*old_name)
2550− return -EINVAL;
2551− err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
2552− if (err)
2553− return err;
25542549
2555− mp = lock_mount(path);
2556− err = PTR_ERR(mp);
2550+ mp = lock_mount(new_path);
25572551 if (IS_ERR(mp))
2558− goto out;
2552+ return PTR_ERR(mp);
25592553
2560− old = real_mount(old_path.mnt);
2561− p = real_mount(path->mnt);
2554+ old = real_mount(old_path->mnt);
2555+ p = real_mount(new_path->mnt);
25622556
25632557 err = -EINVAL;
25642558 if (!check_mnt(p) || !check_mnt(old))
2565− goto out1;
2559+ goto out;
25662560
2567− if (old->mnt.mnt_flags & MNT_LOCKED)
2568− goto out1;
2561+ if (!mnt_has_parent(old))
2562+ goto out;
25692563
2570− err = -EINVAL;
2571− if (old_path.dentry != old_path.mnt->mnt_root)
2572− goto out1;
2564+ if (old->mnt.mnt_flags & MNT_LOCKED)
2565+ goto out;
25732566
2574− if (!mnt_has_parent(old))
2575− goto out1;
2567+ if (old_path->dentry != old_path->mnt->mnt_root)
2568+ goto out;
25762569
2577− if (d_is_dir(path->dentry) !=
2578− d_is_dir(old_path.dentry))
2579− goto out1;
2570+ if (d_is_dir(new_path->dentry) !=
2571+ d_is_dir(old_path->dentry))
2572+ goto out;
25802573 /*
25812574 * Don't move a mount residing in a shared parent.
25822575 */
25832576 if (IS_MNT_SHARED(old->mnt_parent))
2584− goto out1;
2577+ goto out;
25852578 /*
25862579 * Don't move a mount tree containing unbindable mounts to a destination
25872580 * mount which is shared.
25882581 */
25892582 if (IS_MNT_SHARED(p) && tree_contains_unbindable(old))
2590− goto out1;
2583+ goto out;
25912584 err = -ELOOP;
25922585 for (; mnt_has_parent(p); p = p->mnt_parent)
25932586 if (p == old)
2594− goto out1;
2587+ goto out;
25952588
2596− err = attach_recursive_mnt(old, real_mount(path->mnt), mp, &parent_path);
2589+ err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp,
2590+ &parent_path);
25972591 if (err)
2598− goto out1;
2592+ goto out;
25992593
26002594 /* if the mount is moved, it should no longer be expire
26012595 * automatically */
26022596 list_del_init(&old->mnt_expire);
2603−out1:
2604− unlock_mount(mp);
26052597 out:
2598+ unlock_mount(mp);
26062599 if (!err)
26072600 path_put(&parent_path);
2601+ return err;
2602+}
2603+
2604+static int do_move_mount_old(struct path *path, const char *old_name)
2605+{
2606+ struct path old_path;
2607+ int err;
2608+
2609+ if (!old_name || !*old_name)
2610+ return -EINVAL;
2611+
2612+ err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
2613+ if (err)
2614+ return err;
2615+
2616+ err = do_move_mount(&old_path, path);
26082617 path_put(&old_path);
26092618 return err;
26102619 }
@@ -3050,7 +3059,7 @@ long do_mount(const char *dev_name, const char __user *dir_name, · 439 unmodified lines
30503059 else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
30513060 retval = do_change_type(&path, flags);
30523061 else if (flags & MS_MOVE)
3053− retval = do_move_mount(&path, dev_name);
3062+ retval = do_move_mount_old(&path, dev_name);
30543063 else
30553064 retval = do_new_mount(&path, type_page, sb_flags, mnt_flags,
30563065 dev_name, data_page);
@@ -3278,6 +3287,61 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, · 221 unmodified lines
32783287 return ksys_mount(dev_name, dir_name, type, flags, data);
32793288 }
32803289
3290+/*
3291+ * Move a mount from one place to another.
3292+ *
3293+ * Note the flags value is a combination of MOVE_MOUNT_* flags.
3294+ */
3295+SYSCALL_DEFINE5(move_mount,
3296+ int, from_dfd, const char *, from_pathname,
3297+ int, to_dfd, const char *, to_pathname,
3298+ unsigned int, flags)
3299+{
3300+ struct path from_path, to_path;
3301+ unsigned int lflags;
3302+ int ret = 0;
3303+
3304+ if (!may_mount())
3305+ return -EPERM;
3306+
3307+ if (flags & ~MOVE_MOUNT__MASK)
3308+ return -EINVAL;
3309+
3310+ /* If someone gives a pathname, they aren't permitted to move
3311+ * from an fd that requires unmount as we can't get at the flag
3312+ * to clear it afterwards.
3313+ */
3314+ lflags = 0;
3315+ if (flags & MOVE_MOUNT_F_SYMLINKS) lflags |= LOOKUP_FOLLOW;
3316+ if (flags & MOVE_MOUNT_F_AUTOMOUNTS) lflags |= LOOKUP_AUTOMOUNT;
3317+ if (flags & MOVE_MOUNT_F_EMPTY_PATH) lflags |= LOOKUP_EMPTY;
3318+
3319+ ret = user_path_at(from_dfd, from_pathname, lflags, &from_path);
3320+ if (ret < 0)
3321+ return ret;
3322+
3323+ lflags = 0;
3324+ if (flags & MOVE_MOUNT_T_SYMLINKS) lflags |= LOOKUP_FOLLOW;
3325+ if (flags & MOVE_MOUNT_T_AUTOMOUNTS) lflags |= LOOKUP_AUTOMOUNT;
3326+ if (flags & MOVE_MOUNT_T_EMPTY_PATH) lflags |= LOOKUP_EMPTY;
3327+
3328+ ret = user_path_at(to_dfd, to_pathname, lflags, &to_path);
3329+ if (ret < 0)
3330+ goto out_from;
3331+
3332+ ret = security_move_mount(&from_path, &to_path);
3333+ if (ret < 0)
3334+ goto out_to;
3335+
3336+ ret = do_move_mount(&from_path, &to_path);
3337+
3338+out_to:
3339+ path_put(&to_path);
3340+out_from:
3341+ path_put(&from_path);
3342+ return ret;
3343+}
3344+
32813345 /*
32823346 * Return true if path is reachable from root
32833347 *
include/linux/lsm_hooks.h+6−0
@@ -160,6 +160,10 @@ · 159 unmodified lines
160160 * Parse a string of security data filling in the opts structure
161161 * @options string containing all mount options known by the LSM
162162 * @opts binary data structure usable by the LSM
163+ * @move_mount:
164+ * Check permission before a mount is moved.
165+ * @from_path indicates the mount that is going to be moved.
166+ * @to_path indicates the mountpoint that will be mounted upon.
163167 * @dentry_init_security:
164168 * Compute a context for a dentry as the inode is not yet available
165169 * since NFSv4 has no label backed by an EA anyway.
@@ -1501,6 +1505,7 @@ union security_list_options { · 1335 unmodified lines
15011505 unsigned long *set_kern_flags);
15021506 int (*sb_add_mnt_opt)(const char *option, const char *val, int len,
15031507 void **mnt_opts);
1508+ int (*move_mount)(const struct path *from_path, const struct path *to_path);
15041509 int (*dentry_init_security)(struct dentry *dentry, int mode,
15051510 const struct qstr *name, void **ctx,
15061511 u32 *ctxlen);
@@ -1835,6 +1840,7 @@ struct security_hook_heads { · 328 unmodified lines
18351840 struct hlist_head sb_set_mnt_opts;
18361841 struct hlist_head sb_clone_mnt_opts;
18371842 struct hlist_head sb_add_mnt_opt;
1843+ struct hlist_head move_mount;
18381844 struct hlist_head dentry_init_security;
18391845 struct hlist_head dentry_create_files_as;
18401846 #ifdef CONFIG_SECURITY_PATH
include/linux/security.h+7−0
@@ -250,6 +250,7 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb, · 249 unmodified lines
250250 unsigned long *set_kern_flags);
251251 int security_add_mnt_opt(const char *option, const char *val,
252252 int len, void **mnt_opts);
253+int security_move_mount(const struct path *from_path, const struct path *to_path);
253254 int security_dentry_init_security(struct dentry *dentry, int mode,
254255 const struct qstr *name, void **ctx,
255256 u32 *ctxlen);
@@ -611,6 +612,12 @@ static inline int security_add_mnt_opt(const char *option, const char *val, · 355 unmodified lines
611612 return 0;
612613 }
613614
615+static inline int security_move_mount(const struct path *from_path,
616+ const struct path *to_path)
617+{
618+ return 0;
619+}
620+
614621 static inline int security_inode_alloc(struct inode *inode)
615622 {
616623 return 0;
include/linux/syscalls.h+3−0
@@ -986,6 +986,9 @@ asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags, · 985 unmodified lines
986986 asmlinkage long sys_rseq(struct rseq __user *rseq, uint32_t rseq_len,
987987 int flags, uint32_t sig);
988988 asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags);
989+asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path,
990+ int to_dfd, const char __user *to_path,
991+ unsigned int ms_flags);
989992 asmlinkage long sys_pidfd_send_signal(int pidfd, int sig,
990993 siginfo_t __user *info,
991994 unsigned int flags);
include/uapi/linux/mount.h+11−0
@@ -61,4 +61,15 @@ · 60 unmodified lines
6161 #define OPEN_TREE_CLONE 1 /* Clone the target tree and attach the clone */
6262 #define OPEN_TREE_CLOEXEC O_CLOEXEC /* Close the file on execve() */
6363
64+/*
65+ * move_mount() flags.
66+ */
67+#define MOVE_MOUNT_F_SYMLINKS 0x00000001 /* Follow symlinks on from path */
68+#define MOVE_MOUNT_F_AUTOMOUNTS 0x00000002 /* Follow automounts on from path */
69+#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
70+#define MOVE_MOUNT_T_SYMLINKS 0x00000010 /* Follow symlinks on to path */
71+#define MOVE_MOUNT_T_AUTOMOUNTS 0x00000020 /* Follow automounts on to path */
72+#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
73+#define MOVE_MOUNT__MASK 0x00000077
74+
6475 #endif /* _UAPI_LINUX_MOUNT_H */
security/security.c+5−0
@@ -866,6 +866,11 @@ int security_add_mnt_opt(const char *option, const char *val, int len, · 865 unmodified lines
866866 }
867867 EXPORT_SYMBOL(security_add_mnt_opt);
868868
869+int security_move_mount(const struct path *from_path, const struct path *to_path)
870+{
871+ return call_int_hook(move_mount, 0, from_path, to_path);
872+}
873+
869874 int security_inode_alloc(struct inode *inode)
870875 {
871876 int rc = lsm_inode_alloc(inode);