github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/specutils/fs.go (about) 1 // Copyright 2018 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 specutils 16 17 import ( 18 "fmt" 19 "math/bits" 20 "path" 21 "strings" 22 23 specs "github.com/opencontainers/runtime-spec/specs-go" 24 "golang.org/x/sys/unix" 25 ) 26 27 type mapping struct { 28 set bool 29 val uint32 30 } 31 32 // optionsMap maps mount propagation-related OCI filesystem options to mount(2) 33 // syscall flags. 34 var optionsMap = map[string]mapping{ 35 "acl": {set: true, val: unix.MS_POSIXACL}, 36 "async": {set: false, val: unix.MS_SYNCHRONOUS}, 37 "atime": {set: false, val: unix.MS_NOATIME}, 38 "bind": {set: true, val: unix.MS_BIND}, 39 "defaults": {set: true, val: 0}, 40 "dev": {set: false, val: unix.MS_NODEV}, 41 "diratime": {set: false, val: unix.MS_NODIRATIME}, 42 "dirsync": {set: true, val: unix.MS_DIRSYNC}, 43 "exec": {set: false, val: unix.MS_NOEXEC}, 44 "noexec": {set: true, val: unix.MS_NOEXEC}, 45 "iversion": {set: true, val: unix.MS_I_VERSION}, 46 "loud": {set: false, val: unix.MS_SILENT}, 47 "mand": {set: true, val: unix.MS_MANDLOCK}, 48 "noacl": {set: false, val: unix.MS_POSIXACL}, 49 "noatime": {set: true, val: unix.MS_NOATIME}, 50 "nodev": {set: true, val: unix.MS_NODEV}, 51 "nodiratime": {set: true, val: unix.MS_NODIRATIME}, 52 "noiversion": {set: false, val: unix.MS_I_VERSION}, 53 "nomand": {set: false, val: unix.MS_MANDLOCK}, 54 "norelatime": {set: false, val: unix.MS_RELATIME}, 55 "nostrictatime": {set: false, val: unix.MS_STRICTATIME}, 56 "nosuid": {set: true, val: unix.MS_NOSUID}, 57 "rbind": {set: true, val: unix.MS_BIND | unix.MS_REC}, 58 "relatime": {set: true, val: unix.MS_RELATIME}, 59 "remount": {set: true, val: unix.MS_REMOUNT}, 60 "ro": {set: true, val: unix.MS_RDONLY}, 61 "rw": {set: false, val: unix.MS_RDONLY}, 62 "silent": {set: true, val: unix.MS_SILENT}, 63 "strictatime": {set: true, val: unix.MS_STRICTATIME}, 64 "suid": {set: false, val: unix.MS_NOSUID}, 65 "sync": {set: true, val: unix.MS_SYNCHRONOUS}, 66 } 67 68 // verityMountOptions is the set of valid verity mount option keys. 69 var verityMountOptions = map[string]struct{}{ 70 "verity.roothash": struct{}{}, 71 "verity.action": struct{}{}, 72 } 73 74 // propOptionsMap is similar to optionsMap, but it lists propagation options 75 // that cannot be used together with other flags. 76 var propOptionsMap = map[string]mapping{ 77 "private": {set: true, val: unix.MS_PRIVATE}, 78 "rprivate": {set: true, val: unix.MS_PRIVATE | unix.MS_REC}, 79 "slave": {set: true, val: unix.MS_SLAVE}, 80 "rslave": {set: true, val: unix.MS_SLAVE | unix.MS_REC}, 81 "unbindable": {set: true, val: unix.MS_UNBINDABLE}, 82 "runbindable": {set: true, val: unix.MS_UNBINDABLE | unix.MS_REC}, 83 } 84 85 // invalidOptions list options not allowed. 86 // - shared: sandbox must be isolated from the host. Propagating mount changes 87 // from the sandbox to the host breaks the isolation. 88 var invalidOptions = []string{"shared", "rshared"} 89 90 // OptionsToFlags converts mount options to syscall flags. 91 func OptionsToFlags(opts []string) uint32 { 92 return optionsToFlags(opts, optionsMap) 93 } 94 95 // PropOptionsToFlags converts propagation mount options to syscall flags. 96 // Propagation options cannot be set other with other options and must be 97 // handled separately. 98 func PropOptionsToFlags(opts []string) uint32 { 99 return optionsToFlags(opts, propOptionsMap) 100 } 101 102 func optionsToFlags(opts []string, source map[string]mapping) uint32 { 103 var rv uint32 104 for _, opt := range opts { 105 if m, ok := source[opt]; ok { 106 if m.set { 107 rv |= m.val 108 } else { 109 rv ^= m.val 110 } 111 } 112 } 113 return rv 114 } 115 116 // validateMount validates that spec mounts are correct. 117 func validateMount(mnt *specs.Mount) error { 118 if !path.IsAbs(mnt.Destination) { 119 return fmt.Errorf("Mount.Destination must be an absolute path: %v", mnt) 120 } 121 if mnt.Type == "bind" { 122 return ValidateMountOptions(mnt.Options) 123 } 124 return nil 125 } 126 127 func moptKey(opt string) string { 128 if len(opt) == 0 { 129 return opt 130 } 131 // Guaranteed to have at least one token, since opt is not empty. 132 return strings.SplitN(opt, "=", 2)[0] 133 } 134 135 // ValidateMountOptions validates that mount options are correct. 136 func ValidateMountOptions(opts []string) error { 137 for _, o := range opts { 138 if ContainsStr(invalidOptions, o) { 139 return fmt.Errorf("mount option %q is not supported", o) 140 } 141 _, ok1 := optionsMap[o] 142 _, ok2 := propOptionsMap[o] 143 _, ok3 := verityMountOptions[moptKey(o)] 144 if !ok1 && !ok2 && !ok3 { 145 return fmt.Errorf("unknown mount option %q", o) 146 } 147 if err := validatePropagation(o); err != nil { 148 return err 149 } 150 } 151 return nil 152 } 153 154 // ValidateRootfsPropagation validates that rootfs propagation options are 155 // correct. 156 func validateRootfsPropagation(opt string) error { 157 flags := PropOptionsToFlags([]string{opt}) 158 if flags&(unix.MS_SLAVE|unix.MS_PRIVATE) == 0 { 159 return fmt.Errorf("root mount propagation option must specify private or slave: %q", opt) 160 } 161 return validatePropagation(opt) 162 } 163 164 func validatePropagation(opt string) error { 165 flags := PropOptionsToFlags([]string{opt}) 166 exclusive := flags & (unix.MS_SLAVE | unix.MS_PRIVATE | unix.MS_SHARED | unix.MS_UNBINDABLE) 167 if bits.OnesCount32(exclusive) > 1 { 168 return fmt.Errorf("mount propagation options are mutually exclusive: %q", opt) 169 } 170 return nil 171 }