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