github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/util/mountOpts.go (about) 1 package util 2 3 import ( 4 "strings" 5 6 "github.com/pkg/errors" 7 ) 8 9 var ( 10 // ErrBadMntOption indicates that an invalid mount option was passed. 11 ErrBadMntOption = errors.Errorf("invalid mount option") 12 // ErrDupeMntOption indicates that a duplicate mount option was passed. 13 ErrDupeMntOption = errors.Errorf("duplicate mount option passed") 14 ) 15 16 type defaultMountOptions struct { 17 noexec bool 18 nosuid bool 19 nodev bool 20 } 21 22 // ProcessOptions parses the options for a bind or tmpfs mount and ensures that 23 // they are sensible and follow convention. The isTmpfs variable controls 24 // whether extra, tmpfs-specific options will be allowed. 25 // The sourcePath variable, if not empty, contains a bind mount source. 26 func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) { 27 var ( 28 foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ, foundU, foundOverlay, foundIdmap bool 29 ) 30 31 newOptions := make([]string, 0, len(options)) 32 for _, opt := range options { 33 // Some options have parameters - size, mode 34 splitOpt := strings.SplitN(opt, "=", 2) 35 36 // add advanced options such as upperdir=/path and workdir=/path, when overlay is specified 37 if foundOverlay { 38 if strings.Contains(opt, "upperdir") { 39 newOptions = append(newOptions, opt) 40 continue 41 } 42 if strings.Contains(opt, "workdir") { 43 newOptions = append(newOptions, opt) 44 continue 45 } 46 } 47 48 if strings.HasPrefix(splitOpt[0], "idmap") { 49 if foundIdmap { 50 return nil, errors.Wrapf(ErrDupeMntOption, "the 'idmap' option can only be set once") 51 } 52 foundIdmap = true 53 newOptions = append(newOptions, opt) 54 continue 55 } 56 57 switch splitOpt[0] { 58 case "O": 59 foundOverlay = true 60 case "volume-opt": 61 // Volume-opt should be relayed and processed by driver. 62 newOptions = append(newOptions, opt) 63 case "exec", "noexec": 64 if foundExec { 65 return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'noexec' and 'exec' can be used") 66 } 67 foundExec = true 68 case "suid", "nosuid": 69 if foundSuid { 70 return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nosuid' and 'suid' can be used") 71 } 72 foundSuid = true 73 case "nodev", "dev": 74 if foundDev { 75 return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'nodev' and 'dev' can be used") 76 } 77 foundDev = true 78 case "rw", "ro": 79 if foundWrite { 80 return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rw' and 'ro' can be used") 81 } 82 foundWrite = true 83 case "private", "rprivate", "slave", "rslave", "shared", "rshared", "unbindable", "runbindable": 84 if foundProp { 85 return nil, errors.Wrapf(ErrDupeMntOption, "only one root propagation mode can be used") 86 } 87 foundProp = true 88 case "size": 89 if !isTmpfs { 90 return nil, errors.Wrapf(ErrBadMntOption, "the 'size' option is only allowed with tmpfs mounts") 91 } 92 if foundSize { 93 return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs size can be specified") 94 } 95 foundSize = true 96 case "mode": 97 if !isTmpfs { 98 return nil, errors.Wrapf(ErrBadMntOption, "the 'mode' option is only allowed with tmpfs mounts") 99 } 100 if foundMode { 101 return nil, errors.Wrapf(ErrDupeMntOption, "only one tmpfs mode can be specified") 102 } 103 foundMode = true 104 case "tmpcopyup": 105 if !isTmpfs { 106 return nil, errors.Wrapf(ErrBadMntOption, "the 'tmpcopyup' option is only allowed with tmpfs mounts") 107 } 108 if foundCopyUp { 109 return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once") 110 } 111 foundCopyUp = true 112 case "consistency": 113 // Often used on MACs and mistakenly on Linux platforms. 114 // Since Docker ignores this option so shall we. 115 continue 116 case "notmpcopyup": 117 if !isTmpfs { 118 return nil, errors.Wrapf(ErrBadMntOption, "the 'notmpcopyup' option is only allowed with tmpfs mounts") 119 } 120 if foundCopyUp { 121 return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once") 122 } 123 foundCopyUp = true 124 // do not propagate notmpcopyup to the OCI runtime 125 continue 126 case "bind", "rbind": 127 if isTmpfs { 128 return nil, errors.Wrapf(ErrBadMntOption, "the 'bind' and 'rbind' options are not allowed with tmpfs mounts") 129 } 130 if foundBind { 131 return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rbind' and 'bind' can be used") 132 } 133 foundBind = true 134 case "z", "Z": 135 if isTmpfs { 136 return nil, errors.Wrapf(ErrBadMntOption, "the 'z' and 'Z' options are not allowed with tmpfs mounts") 137 } 138 if foundZ { 139 return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'z' and 'Z' can be used") 140 } 141 foundZ = true 142 case "U": 143 if foundU { 144 return nil, errors.Wrapf(ErrDupeMntOption, "the 'U' option can only be set once") 145 } 146 foundU = true 147 default: 148 return nil, errors.Wrapf(ErrBadMntOption, "unknown mount option %q", opt) 149 } 150 newOptions = append(newOptions, opt) 151 } 152 153 if !foundWrite { 154 newOptions = append(newOptions, "rw") 155 } 156 if !foundProp { 157 newOptions = append(newOptions, "rprivate") 158 } 159 defaults, err := getDefaultMountOptions(sourcePath) 160 if err != nil { 161 return nil, err 162 } 163 if !foundExec && defaults.noexec { 164 newOptions = append(newOptions, "noexec") 165 } 166 if !foundSuid && defaults.nosuid { 167 newOptions = append(newOptions, "nosuid") 168 } 169 if !foundDev && defaults.nodev { 170 newOptions = append(newOptions, "nodev") 171 } 172 if isTmpfs && !foundCopyUp { 173 newOptions = append(newOptions, "tmpcopyup") 174 } 175 if !isTmpfs && !foundBind { 176 newOptions = append(newOptions, "rbind") 177 } 178 179 return newOptions, nil 180 } 181 182 func ParseDriverOpts(option string) (string, string, error) { 183 token := strings.SplitN(option, "=", 2) 184 if len(token) != 2 { 185 return "", "", errors.Wrapf(ErrBadMntOption, "cannot parse driver opts") 186 } 187 opt := strings.SplitN(token[1], "=", 2) 188 if len(opt) != 2 { 189 return "", "", errors.Wrapf(ErrBadMntOption, "cannot parse driver opts") 190 } 191 return opt[0], opt[1], nil 192 }