1// Package local provides the default implementation for volumes. It
2// is used to mount data volume containers and directories local to
3// the host server.
4package local // import "github.com/docker/docker/volume/local"
5
6import (
7	"encoding/json"
8	"fmt"
9	"io/ioutil"
10	"os"
11	"path/filepath"
12	"reflect"
13	"strings"
14	"sync"
15
16	"github.com/docker/docker/daemon/names"
17	"github.com/docker/docker/errdefs"
18	"github.com/docker/docker/pkg/idtools"
19	"github.com/docker/docker/pkg/mount"
20	"github.com/docker/docker/volume"
21	"github.com/pkg/errors"
22)
23
24// VolumeDataPathName is the name of the directory where the volume data is stored.
25// It uses a very distinctive name to avoid collisions migrating data between
26// Docker versions.
27const (
28	VolumeDataPathName = "_data"
29	volumesPathName    = "volumes"
30)
31
32var (
33	// ErrNotFound is the typed error returned when the requested volume name can't be found
34	ErrNotFound = fmt.Errorf("volume not found")
35	// volumeNameRegex ensures the name assigned for the volume is valid.
36	// This name is used to create the bind directory, so we need to avoid characters that
37	// would make the path to escape the root directory.
38	volumeNameRegex = names.RestrictedNamePattern
39)
40
41type activeMount struct {
42	count   uint64
43	mounted bool
44}
45
46// New instantiates a new Root instance with the provided scope. Scope
47// is the base path that the Root instance uses to store its
48// volumes. The base path is created here if it does not exist.
49func New(scope string, rootIdentity idtools.Identity) (*Root, error) {
50	rootDirectory := filepath.Join(scope, volumesPathName)
51
52	if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIdentity); err != nil {
53		return nil, err
54	}
55
56	r := &Root{
57		scope:        scope,
58		path:         rootDirectory,
59		volumes:      make(map[string]*localVolume),
60		rootIdentity: rootIdentity,
61	}
62
63	dirs, err := ioutil.ReadDir(rootDirectory)
64	if err != nil {
65		return nil, err
66	}
67
68	for _, d := range dirs {
69		if !d.IsDir() {
70			continue
71		}
72
73		name := filepath.Base(d.Name())
74		v := &localVolume{
75			driverName: r.Name(),
76			name:       name,
77			path:       r.DataPath(name),
78		}
79		r.volumes[name] = v
80		optsFilePath := filepath.Join(rootDirectory, name, "opts.json")
81		if b, err := ioutil.ReadFile(optsFilePath); err == nil {
82			opts := optsConfig{}
83			if err := json.Unmarshal(b, &opts); err != nil {
84				return nil, errors.Wrapf(err, "error while unmarshaling volume options for volume: %s", name)
85			}
86			// Make sure this isn't an empty optsConfig.
87			// This could be empty due to buggy behavior in older versions of Docker.
88			if !reflect.DeepEqual(opts, optsConfig{}) {
89				v.opts = &opts
90			}
91
92			// unmount anything that may still be mounted (for example, from an unclean shutdown)
93			mount.Unmount(v.path)
94		}
95	}
96
97	return r, nil
98}
99
100// Root implements the Driver interface for the volume package and
101// manages the creation/removal of volumes. It uses only standard vfs
102// commands to create/remove dirs within its provided scope.
103type Root struct {
104	m            sync.Mutex
105	scope        string
106	path         string
107	volumes      map[string]*localVolume
108	rootIdentity idtools.Identity
109}
110
111// List lists all the volumes
112func (r *Root) List() ([]volume.Volume, error) {
113	var ls []volume.Volume
114	r.m.Lock()
115	for _, v := range r.volumes {
116		ls = append(ls, v)
117	}
118	r.m.Unlock()
119	return ls, nil
120}
121
122// DataPath returns the constructed path of this volume.
123func (r *Root) DataPath(volumeName string) string {
124	return filepath.Join(r.path, volumeName, VolumeDataPathName)
125}
126
127// Name returns the name of Root, defined in the volume package in the DefaultDriverName constant.
128func (r *Root) Name() string {
129	return volume.DefaultDriverName
130}
131
132// Create creates a new volume.Volume with the provided name, creating
133// the underlying directory tree required for this volume in the
134// process.
135func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) {
136	if err := r.validateName(name); err != nil {
137		return nil, err
138	}
139
140	r.m.Lock()
141	defer r.m.Unlock()
142
143	v, exists := r.volumes[name]
144	if exists {
145		return v, nil
146	}
147
148	path := r.DataPath(name)
149	if err := idtools.MkdirAllAndChown(path, 0755, r.rootIdentity); err != nil {
150		return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path)
151	}
152
153	var err error
154	defer func() {
155		if err != nil {
156			os.RemoveAll(filepath.Dir(path))
157		}
158	}()
159
160	v = &localVolume{
161		driverName: r.Name(),
162		name:       name,
163		path:       path,
164	}
165
166	if len(opts) != 0 {
167		if err = setOpts(v, opts); err != nil {
168			return nil, err
169		}
170		var b []byte
171		b, err = json.Marshal(v.opts)
172		if err != nil {
173			return nil, err
174		}
175		if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil {
176			return nil, errdefs.System(errors.Wrap(err, "error while persisting volume options"))
177		}
178	}
179
180	r.volumes[name] = v
181	return v, nil
182}
183
184// Remove removes the specified volume and all underlying data. If the
185// given volume does not belong to this driver and an error is
186// returned. The volume is reference counted, if all references are
187// not released then the volume is not removed.
188func (r *Root) Remove(v volume.Volume) error {
189	r.m.Lock()
190	defer r.m.Unlock()
191
192	lv, ok := v.(*localVolume)
193	if !ok {
194		return errdefs.System(errors.Errorf("unknown volume type %T", v))
195	}
196
197	if lv.active.count > 0 {
198		return errdefs.System(errors.Errorf("volume has active mounts"))
199	}
200
201	if err := lv.unmount(); err != nil {
202		return err
203	}
204
205	realPath, err := filepath.EvalSymlinks(lv.path)
206	if err != nil {
207		if !os.IsNotExist(err) {
208			return err
209		}
210		realPath = filepath.Dir(lv.path)
211	}
212
213	if !r.scopedPath(realPath) {
214		return errdefs.System(errors.Errorf("Unable to remove a directory outside of the local volume root %s: %s", r.scope, realPath))
215	}
216
217	if err := removePath(realPath); err != nil {
218		return err
219	}
220
221	delete(r.volumes, lv.name)
222	return removePath(filepath.Dir(lv.path))
223}
224
225func removePath(path string) error {
226	if err := os.RemoveAll(path); err != nil {
227		if os.IsNotExist(err) {
228			return nil
229		}
230		return errdefs.System(errors.Wrapf(err, "error removing volume path '%s'", path))
231	}
232	return nil
233}
234
235// Get looks up the volume for the given name and returns it if found
236func (r *Root) Get(name string) (volume.Volume, error) {
237	r.m.Lock()
238	v, exists := r.volumes[name]
239	r.m.Unlock()
240	if !exists {
241		return nil, ErrNotFound
242	}
243	return v, nil
244}
245
246// Scope returns the local volume scope
247func (r *Root) Scope() string {
248	return volume.LocalScope
249}
250
251type validationError string
252
253func (e validationError) Error() string {
254	return string(e)
255}
256
257func (e validationError) InvalidParameter() {}
258
259func (r *Root) validateName(name string) error {
260	if len(name) == 1 {
261		return validationError("volume name is too short, names should be at least two alphanumeric characters")
262	}
263	if !volumeNameRegex.MatchString(name) {
264		return validationError(fmt.Sprintf("%q includes invalid characters for a local volume name, only %q are allowed. If you intended to pass a host directory, use absolute path", name, names.RestrictedNameChars))
265	}
266	return nil
267}
268
269// localVolume implements the Volume interface from the volume package and
270// represents the volumes created by Root.
271type localVolume struct {
272	m sync.Mutex
273	// unique name of the volume
274	name string
275	// path is the path on the host where the data lives
276	path string
277	// driverName is the name of the driver that created the volume.
278	driverName string
279	// opts is the parsed list of options used to create the volume
280	opts *optsConfig
281	// active refcounts the active mounts
282	active activeMount
283}
284
285// Name returns the name of the given Volume.
286func (v *localVolume) Name() string {
287	return v.name
288}
289
290// DriverName returns the driver that created the given Volume.
291func (v *localVolume) DriverName() string {
292	return v.driverName
293}
294
295// Path returns the data location.
296func (v *localVolume) Path() string {
297	return v.path
298}
299
300// CachedPath returns the data location
301func (v *localVolume) CachedPath() string {
302	return v.path
303}
304
305// Mount implements the localVolume interface, returning the data location.
306// If there are any provided mount options, the resources will be mounted at this point
307func (v *localVolume) Mount(id string) (string, error) {
308	v.m.Lock()
309	defer v.m.Unlock()
310	if v.opts != nil {
311		if !v.active.mounted {
312			if err := v.mount(); err != nil {
313				return "", errdefs.System(err)
314			}
315			v.active.mounted = true
316		}
317		v.active.count++
318	}
319	return v.path, nil
320}
321
322// Unmount dereferences the id, and if it is the last reference will unmount any resources
323// that were previously mounted.
324func (v *localVolume) Unmount(id string) error {
325	v.m.Lock()
326	defer v.m.Unlock()
327
328	// Always decrement the count, even if the unmount fails
329	// Essentially docker doesn't care if this fails, it will send an error, but
330	// ultimately there's nothing that can be done. If we don't decrement the count
331	// this volume can never be removed until a daemon restart occurs.
332	if v.opts != nil {
333		v.active.count--
334	}
335
336	if v.active.count > 0 {
337		return nil
338	}
339
340	return v.unmount()
341}
342
343func (v *localVolume) unmount() error {
344	if v.opts != nil {
345		if err := mount.Unmount(v.path); err != nil {
346			if mounted, mErr := mount.Mounted(v.path); mounted || mErr != nil {
347				return errdefs.System(errors.Wrapf(err, "error while unmounting volume path '%s'", v.path))
348			}
349		}
350		v.active.mounted = false
351	}
352	return nil
353}
354
355func validateOpts(opts map[string]string) error {
356	for opt := range opts {
357		if !validOpts[opt] {
358			return validationError(fmt.Sprintf("invalid option key: %q", opt))
359		}
360	}
361	return nil
362}
363
364func (v *localVolume) Status() map[string]interface{} {
365	return nil
366}
367
368// getAddress finds out address/hostname from options
369func getAddress(opts string) string {
370	optsList := strings.Split(opts, ",")
371	for i := 0; i < len(optsList); i++ {
372		if strings.HasPrefix(optsList[i], "addr=") {
373			addr := strings.SplitN(optsList[i], "=", 2)[1]
374			return addr
375		}
376	}
377	return ""
378}
379