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