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