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