github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/vfs2/mount.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package vfs2 16 17 import ( 18 "github.com/SagerNet/gvisor/pkg/abi/linux" 19 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 20 "github.com/SagerNet/gvisor/pkg/hostarch" 21 "github.com/SagerNet/gvisor/pkg/sentry/arch" 22 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 23 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 24 ) 25 26 // Mount implements Linux syscall mount(2). 27 func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 28 sourceAddr := args[0].Pointer() 29 targetAddr := args[1].Pointer() 30 typeAddr := args[2].Pointer() 31 flags := args[3].Uint64() 32 dataAddr := args[4].Pointer() 33 34 // For null-terminated strings related to mount(2), Linux copies in at most 35 // a page worth of data. See fs/namespace.c:copy_mount_string(). 36 fsType, err := t.CopyInString(typeAddr, hostarch.PageSize) 37 if err != nil { 38 return 0, nil, err 39 } 40 source, err := t.CopyInString(sourceAddr, hostarch.PageSize) 41 if err != nil { 42 return 0, nil, err 43 } 44 45 targetPath, err := copyInPath(t, targetAddr) 46 if err != nil { 47 return 0, nil, err 48 } 49 50 data := "" 51 if dataAddr != 0 { 52 // In Linux, a full page is always copied in regardless of null 53 // character placement, and the address is passed to each file system. 54 // Most file systems always treat this data as a string, though, and so 55 // do all of the ones we implement. 56 data, err = t.CopyInString(dataAddr, hostarch.PageSize) 57 if err != nil { 58 return 0, nil, err 59 } 60 } 61 62 // Ignore magic value that was required before Linux 2.4. 63 if flags&linux.MS_MGC_MSK == linux.MS_MGC_VAL { 64 flags = flags &^ linux.MS_MGC_MSK 65 } 66 67 // Must have CAP_SYS_ADMIN in the current mount namespace's associated user 68 // namespace. 69 creds := t.Credentials() 70 if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespaceVFS2().Owner) { 71 return 0, nil, linuxerr.EPERM 72 } 73 74 const unsupportedOps = linux.MS_REMOUNT | linux.MS_BIND | 75 linux.MS_SHARED | linux.MS_PRIVATE | linux.MS_SLAVE | 76 linux.MS_UNBINDABLE | linux.MS_MOVE 77 78 // Silently allow MS_NOSUID, since we don't implement set-id bits 79 // anyway. 80 const unsupportedFlags = linux.MS_NODIRATIME | linux.MS_STRICTATIME 81 82 // Linux just allows passing any flags to mount(2) - it won't fail when 83 // unknown or unsupported flags are passed. Since we don't implement 84 // everything, we fail explicitly on flags that are unimplemented. 85 if flags&(unsupportedOps|unsupportedFlags) != 0 { 86 return 0, nil, linuxerr.EINVAL 87 } 88 89 var opts vfs.MountOptions 90 if flags&linux.MS_NOATIME == linux.MS_NOATIME { 91 opts.Flags.NoATime = true 92 } 93 if flags&linux.MS_NOEXEC == linux.MS_NOEXEC { 94 opts.Flags.NoExec = true 95 } 96 if flags&linux.MS_NODEV == linux.MS_NODEV { 97 opts.Flags.NoDev = true 98 } 99 if flags&linux.MS_NOSUID == linux.MS_NOSUID { 100 opts.Flags.NoSUID = true 101 } 102 if flags&linux.MS_RDONLY == linux.MS_RDONLY { 103 opts.ReadOnly = true 104 } 105 opts.GetFilesystemOptions.Data = data 106 107 target, err := getTaskPathOperation(t, linux.AT_FDCWD, targetPath, disallowEmptyPath, nofollowFinalSymlink) 108 if err != nil { 109 return 0, nil, err 110 } 111 defer target.Release(t) 112 _, err = t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts) 113 return 0, nil, err 114 } 115 116 // Umount2 implements Linux syscall umount2(2). 117 func Umount2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 118 addr := args[0].Pointer() 119 flags := args[1].Int() 120 121 // Must have CAP_SYS_ADMIN in the mount namespace's associated user 122 // namespace. 123 // 124 // Currently, this is always the init task's user namespace. 125 creds := t.Credentials() 126 if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespaceVFS2().Owner) { 127 return 0, nil, linuxerr.EPERM 128 } 129 130 const unsupported = linux.MNT_FORCE | linux.MNT_EXPIRE 131 if flags&unsupported != 0 { 132 return 0, nil, linuxerr.EINVAL 133 } 134 135 path, err := copyInPath(t, addr) 136 if err != nil { 137 return 0, nil, err 138 } 139 tpop, err := getTaskPathOperation(t, linux.AT_FDCWD, path, disallowEmptyPath, nofollowFinalSymlink) 140 if err != nil { 141 return 0, nil, err 142 } 143 defer tpop.Release(t) 144 145 opts := vfs.UmountOptions{ 146 Flags: uint32(flags), 147 } 148 149 return 0, nil, t.Kernel().VFS().UmountAt(t, creds, &tpop.pop, &opts) 150 }