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