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