github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/volume/mounts/linux_parser.go (about) 1 package mounts // import "github.com/docker/docker/volume/mounts" 2 3 import ( 4 "errors" 5 "fmt" 6 "path" 7 "path/filepath" 8 "strings" 9 10 "github.com/docker/docker/api/types/mount" 11 "github.com/docker/docker/pkg/stringid" 12 "github.com/docker/docker/volume" 13 ) 14 15 // NewLinuxParser creates a parser with Linux semantics. 16 func NewLinuxParser() Parser { 17 return &linuxParser{ 18 fi: defaultFileInfoProvider{}, 19 } 20 } 21 22 type linuxParser struct { 23 fi fileInfoProvider 24 } 25 26 func linuxSplitRawSpec(raw string) ([]string, error) { 27 if strings.Count(raw, ":") > 2 { 28 return nil, errInvalidSpec(raw) 29 } 30 31 arr := strings.SplitN(raw, ":", 3) 32 if arr[0] == "" { 33 return nil, errInvalidSpec(raw) 34 } 35 return arr, nil 36 } 37 38 func linuxValidateNotRoot(p string) error { 39 p = path.Clean(strings.ReplaceAll(p, `\`, `/`)) 40 if p == "/" { 41 return ErrVolumeTargetIsRoot 42 } 43 return nil 44 } 45 func linuxValidateAbsolute(p string) error { 46 p = strings.ReplaceAll(p, `\`, `/`) 47 if path.IsAbs(p) { 48 return nil 49 } 50 return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p) 51 } 52 func (p *linuxParser) ValidateMountConfig(mnt *mount.Mount) error { 53 // there was something looking like a bug in existing codebase: 54 // - validateMountConfig on linux was called with options skipping bind source existence when calling ParseMountRaw 55 // - but not when calling ParseMountSpec directly... nor when the unit test called it directly 56 return p.validateMountConfigImpl(mnt, true) 57 } 58 func (p *linuxParser) validateMountConfigImpl(mnt *mount.Mount, validateBindSourceExists bool) error { 59 if len(mnt.Target) == 0 { 60 return &errMountConfig{mnt, errMissingField("Target")} 61 } 62 63 if err := linuxValidateNotRoot(mnt.Target); err != nil { 64 return &errMountConfig{mnt, err} 65 } 66 67 if err := linuxValidateAbsolute(mnt.Target); err != nil { 68 return &errMountConfig{mnt, err} 69 } 70 71 switch mnt.Type { 72 case mount.TypeBind: 73 if len(mnt.Source) == 0 { 74 return &errMountConfig{mnt, errMissingField("Source")} 75 } 76 // Don't error out just because the propagation mode is not supported on the platform 77 if opts := mnt.BindOptions; opts != nil { 78 if len(opts.Propagation) > 0 && len(linuxPropagationModes) > 0 { 79 if _, ok := linuxPropagationModes[opts.Propagation]; !ok { 80 return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)} 81 } 82 } 83 } 84 if mnt.VolumeOptions != nil { 85 return &errMountConfig{mnt, errExtraField("VolumeOptions")} 86 } 87 88 if err := linuxValidateAbsolute(mnt.Source); err != nil { 89 return &errMountConfig{mnt, err} 90 } 91 92 if validateBindSourceExists { 93 exists, _, err := p.fi.fileInfo(mnt.Source) 94 if err != nil { 95 return &errMountConfig{mnt, err} 96 } 97 if !exists { 98 return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)} 99 } 100 } 101 102 case mount.TypeVolume: 103 if mnt.BindOptions != nil { 104 return &errMountConfig{mnt, errExtraField("BindOptions")} 105 } 106 107 if len(mnt.Source) == 0 && mnt.ReadOnly { 108 return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")} 109 } 110 case mount.TypeTmpfs: 111 if mnt.BindOptions != nil { 112 return &errMountConfig{mnt, errExtraField("BindOptions")} 113 } 114 if len(mnt.Source) != 0 { 115 return &errMountConfig{mnt, errExtraField("Source")} 116 } 117 if _, err := p.ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil { 118 return &errMountConfig{mnt, err} 119 } 120 default: 121 return &errMountConfig{mnt, errors.New("mount type unknown")} 122 } 123 return nil 124 } 125 126 // label modes 127 var linuxLabelModes = map[string]bool{ 128 "Z": true, 129 "z": true, 130 } 131 132 // consistency modes 133 var linuxConsistencyModes = map[mount.Consistency]bool{ 134 mount.ConsistencyFull: true, 135 mount.ConsistencyCached: true, 136 mount.ConsistencyDelegated: true, 137 } 138 var linuxPropagationModes = map[mount.Propagation]bool{ 139 mount.PropagationPrivate: true, 140 mount.PropagationRPrivate: true, 141 mount.PropagationSlave: true, 142 mount.PropagationRSlave: true, 143 mount.PropagationShared: true, 144 mount.PropagationRShared: true, 145 } 146 147 const linuxDefaultPropagationMode = mount.PropagationRPrivate 148 149 func linuxGetPropagation(mode string) mount.Propagation { 150 for _, o := range strings.Split(mode, ",") { 151 prop := mount.Propagation(o) 152 if linuxPropagationModes[prop] { 153 return prop 154 } 155 } 156 return linuxDefaultPropagationMode 157 } 158 159 func linuxHasPropagation(mode string) bool { 160 for _, o := range strings.Split(mode, ",") { 161 if linuxPropagationModes[mount.Propagation(o)] { 162 return true 163 } 164 } 165 return false 166 } 167 168 func linuxValidMountMode(mode string) bool { 169 if mode == "" { 170 return true 171 } 172 173 rwModeCount := 0 174 labelModeCount := 0 175 propagationModeCount := 0 176 copyModeCount := 0 177 consistencyModeCount := 0 178 179 for _, o := range strings.Split(mode, ",") { 180 switch { 181 case rwModes[o]: 182 rwModeCount++ 183 case linuxLabelModes[o]: 184 labelModeCount++ 185 case linuxPropagationModes[mount.Propagation(o)]: 186 propagationModeCount++ 187 case copyModeExists(o): 188 copyModeCount++ 189 case linuxConsistencyModes[mount.Consistency(o)]: 190 consistencyModeCount++ 191 default: 192 return false 193 } 194 } 195 196 // Only one string for each mode is allowed. 197 if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 || consistencyModeCount > 1 { 198 return false 199 } 200 return true 201 } 202 203 func (p *linuxParser) ReadWrite(mode string) bool { 204 if !linuxValidMountMode(mode) { 205 return false 206 } 207 208 for _, o := range strings.Split(mode, ",") { 209 if o == "ro" { 210 return false 211 } 212 } 213 return true 214 } 215 216 func (p *linuxParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) { 217 arr, err := linuxSplitRawSpec(raw) 218 if err != nil { 219 return nil, err 220 } 221 222 var spec mount.Mount 223 var mode string 224 switch len(arr) { 225 case 1: 226 // Just a destination path in the container 227 spec.Target = arr[0] 228 case 2: 229 if linuxValidMountMode(arr[1]) { 230 // Destination + Mode is not a valid volume - volumes 231 // cannot include a mode. e.g. /foo:rw 232 return nil, errInvalidSpec(raw) 233 } 234 // Host Source Path or Name + Destination 235 spec.Source = arr[0] 236 spec.Target = arr[1] 237 case 3: 238 // HostSourcePath+DestinationPath+Mode 239 spec.Source = arr[0] 240 spec.Target = arr[1] 241 mode = arr[2] 242 default: 243 return nil, errInvalidSpec(raw) 244 } 245 246 if !linuxValidMountMode(mode) { 247 return nil, errInvalidMode(mode) 248 } 249 250 if path.IsAbs(spec.Source) { 251 spec.Type = mount.TypeBind 252 } else { 253 spec.Type = mount.TypeVolume 254 } 255 256 spec.ReadOnly = !p.ReadWrite(mode) 257 258 // cannot assume that if a volume driver is passed in that we should set it 259 if volumeDriver != "" && spec.Type == mount.TypeVolume { 260 spec.VolumeOptions = &mount.VolumeOptions{ 261 DriverConfig: &mount.Driver{Name: volumeDriver}, 262 } 263 } 264 265 if copyData, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet { 266 if spec.VolumeOptions == nil { 267 spec.VolumeOptions = &mount.VolumeOptions{} 268 } 269 spec.VolumeOptions.NoCopy = !copyData 270 } 271 if linuxHasPropagation(mode) { 272 spec.BindOptions = &mount.BindOptions{ 273 Propagation: linuxGetPropagation(mode), 274 } 275 } 276 277 mp, err := p.parseMountSpec(spec, false) 278 if mp != nil { 279 mp.Mode = mode 280 } 281 if err != nil { 282 err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err) 283 } 284 return mp, err 285 } 286 func (p *linuxParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) { 287 return p.parseMountSpec(cfg, true) 288 } 289 func (p *linuxParser) parseMountSpec(cfg mount.Mount, validateBindSourceExists bool) (*MountPoint, error) { 290 if err := p.validateMountConfigImpl(&cfg, validateBindSourceExists); err != nil { 291 return nil, err 292 } 293 mp := &MountPoint{ 294 RW: !cfg.ReadOnly, 295 Destination: path.Clean(filepath.ToSlash(cfg.Target)), 296 Type: cfg.Type, 297 Spec: cfg, 298 } 299 300 switch cfg.Type { 301 case mount.TypeVolume: 302 if cfg.Source == "" { 303 mp.Name = stringid.GenerateRandomID() 304 } else { 305 mp.Name = cfg.Source 306 } 307 mp.CopyData = p.DefaultCopyMode() 308 309 if cfg.VolumeOptions != nil { 310 if cfg.VolumeOptions.DriverConfig != nil { 311 mp.Driver = cfg.VolumeOptions.DriverConfig.Name 312 } 313 if cfg.VolumeOptions.NoCopy { 314 mp.CopyData = false 315 } 316 } 317 case mount.TypeBind: 318 mp.Source = path.Clean(filepath.ToSlash(cfg.Source)) 319 if cfg.BindOptions != nil && len(cfg.BindOptions.Propagation) > 0 { 320 mp.Propagation = cfg.BindOptions.Propagation 321 } else { 322 // If user did not specify a propagation mode, get 323 // default propagation mode. 324 mp.Propagation = linuxDefaultPropagationMode 325 } 326 case mount.TypeTmpfs: 327 // NOP 328 } 329 return mp, nil 330 } 331 332 func (p *linuxParser) ParseVolumesFrom(spec string) (string, string, error) { 333 if len(spec) == 0 { 334 return "", "", fmt.Errorf("volumes-from specification cannot be an empty string") 335 } 336 337 specParts := strings.SplitN(spec, ":", 2) 338 id := specParts[0] 339 mode := "rw" 340 341 if len(specParts) == 2 { 342 mode = specParts[1] 343 if !linuxValidMountMode(mode) { 344 return "", "", errInvalidMode(mode) 345 } 346 // For now don't allow propagation properties while importing 347 // volumes from data container. These volumes will inherit 348 // the same propagation property as of the original volume 349 // in data container. This probably can be relaxed in future. 350 if linuxHasPropagation(mode) { 351 return "", "", errInvalidMode(mode) 352 } 353 // Do not allow copy modes on volumes-from 354 if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet { 355 return "", "", errInvalidMode(mode) 356 } 357 } 358 return id, mode, nil 359 } 360 361 func (p *linuxParser) DefaultPropagationMode() mount.Propagation { 362 return linuxDefaultPropagationMode 363 } 364 365 func (p *linuxParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) { 366 var rawOpts []string 367 if readOnly { 368 rawOpts = append(rawOpts, "ro") 369 } 370 371 if opt != nil && opt.Mode != 0 { 372 rawOpts = append(rawOpts, fmt.Sprintf("mode=%o", opt.Mode)) 373 } 374 375 if opt != nil && opt.SizeBytes != 0 { 376 // calculate suffix here, making this linux specific, but that is 377 // okay, since API is that way anyways. 378 379 // we do this by finding the suffix that divides evenly into the 380 // value, returning the value itself, with no suffix, if it fails. 381 // 382 // For the most part, we don't enforce any semantic to this values. 383 // The operating system will usually align this and enforce minimum 384 // and maximums. 385 var ( 386 size = opt.SizeBytes 387 suffix string 388 ) 389 for _, r := range []struct { 390 suffix string 391 divisor int64 392 }{ 393 {"g", 1 << 30}, 394 {"m", 1 << 20}, 395 {"k", 1 << 10}, 396 } { 397 if size%r.divisor == 0 { 398 size = size / r.divisor 399 suffix = r.suffix 400 break 401 } 402 } 403 404 rawOpts = append(rawOpts, fmt.Sprintf("size=%d%s", size, suffix)) 405 } 406 return strings.Join(rawOpts, ","), nil 407 } 408 409 func (p *linuxParser) DefaultCopyMode() bool { 410 return true 411 } 412 func (p *linuxParser) ValidateVolumeName(name string) error { 413 return nil 414 } 415 416 func (p *linuxParser) IsBackwardCompatible(m *MountPoint) bool { 417 return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName 418 } 419 420 func (p *linuxParser) ValidateTmpfsMountDestination(dest string) error { 421 if err := linuxValidateNotRoot(dest); err != nil { 422 return err 423 } 424 return linuxValidateAbsolute(dest) 425 }