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