1// +build linux
2
3package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay"
4
5import (
6	"bufio"
7	"fmt"
8	"io"
9	"io/ioutil"
10	"os"
11	"os/exec"
12	"path"
13	"path/filepath"
14	"strconv"
15	"strings"
16
17	"github.com/docker/docker/daemon/graphdriver"
18	"github.com/docker/docker/daemon/graphdriver/copy"
19	"github.com/docker/docker/daemon/graphdriver/overlayutils"
20	"github.com/docker/docker/pkg/archive"
21	"github.com/docker/docker/pkg/containerfs"
22	"github.com/docker/docker/pkg/fsutils"
23	"github.com/docker/docker/pkg/idtools"
24	"github.com/docker/docker/pkg/locker"
25	"github.com/docker/docker/pkg/mount"
26	"github.com/docker/docker/pkg/parsers"
27	"github.com/docker/docker/pkg/system"
28	"github.com/opencontainers/selinux/go-selinux/label"
29	"github.com/sirupsen/logrus"
30	"golang.org/x/sys/unix"
31)
32
33// This is a small wrapper over the NaiveDiffWriter that lets us have a custom
34// implementation of ApplyDiff()
35
36var (
37	// ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer.
38	ErrApplyDiffFallback = fmt.Errorf("Fall back to normal ApplyDiff")
39	backingFs            = "<unknown>"
40)
41
42// ApplyDiffProtoDriver wraps the ProtoDriver by extending the interface with ApplyDiff method.
43type ApplyDiffProtoDriver interface {
44	graphdriver.ProtoDriver
45	// ApplyDiff writes the diff to the archive for the given id and parent id.
46	// It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise.
47	ApplyDiff(id, parent string, diff io.Reader) (size int64, err error)
48}
49
50type naiveDiffDriverWithApply struct {
51	graphdriver.Driver
52	applyDiff ApplyDiffProtoDriver
53}
54
55// NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff.
56func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, uidMaps, gidMaps []idtools.IDMap) graphdriver.Driver {
57	return &naiveDiffDriverWithApply{
58		Driver:    graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps),
59		applyDiff: driver,
60	}
61}
62
63// ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback.
64func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
65	b, err := d.applyDiff.ApplyDiff(id, parent, diff)
66	if err == ErrApplyDiffFallback {
67		return d.Driver.ApplyDiff(id, parent, diff)
68	}
69	return b, err
70}
71
72// This backend uses the overlay union filesystem for containers
73// plus hard link file sharing for images.
74
75// Each container/image can have a "root" subdirectory which is a plain
76// filesystem hierarchy, or they can use overlay.
77
78// If they use overlay there is a "upper" directory and a "lower-id"
79// file, as well as "merged" and "work" directories. The "upper"
80// directory has the upper layer of the overlay, and "lower-id" contains
81// the id of the parent whose "root" directory shall be used as the lower
82// layer in the overlay. The overlay itself is mounted in the "merged"
83// directory, and the "work" dir is needed for overlay to work.
84
85// When an overlay layer is created there are two cases, either the
86// parent has a "root" dir, then we start out with an empty "upper"
87// directory overlaid on the parents root. This is typically the
88// case with the init layer of a container which is based on an image.
89// If there is no "root" in the parent, we inherit the lower-id from
90// the parent and start by making a copy in the parent's "upper" dir.
91// This is typically the case for a container layer which copies
92// its parent -init upper layer.
93
94// Additionally we also have a custom implementation of ApplyLayer
95// which makes a recursive copy of the parent "root" layer using
96// hardlinks to share file data, and then applies the layer on top
97// of that. This means all child images share file (but not directory)
98// data with the parent.
99
100type overlayOptions struct{}
101
102// Driver contains information about the home directory and the list of active mounts that are created using this driver.
103type Driver struct {
104	home          string
105	uidMaps       []idtools.IDMap
106	gidMaps       []idtools.IDMap
107	ctr           *graphdriver.RefCounter
108	supportsDType bool
109	locker        *locker.Locker
110}
111
112func init() {
113	graphdriver.Register("overlay", Init)
114}
115
116// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem.
117// If overlay filesystem is not supported on the host, the error
118// graphdriver.ErrNotSupported is returned.
119// If an overlay filesystem is not supported over an existing filesystem then
120// error graphdriver.ErrIncompatibleFS is returned.
121func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
122	_, err := parseOptions(options)
123	if err != nil {
124		return nil, err
125	}
126
127	if err := supportsOverlay(); err != nil {
128		return nil, graphdriver.ErrNotSupported
129	}
130
131	// Perform feature detection on /var/lib/docker/overlay if it's an existing directory.
132	// This covers situations where /var/lib/docker/overlay is a mount, and on a different
133	// filesystem than /var/lib/docker.
134	// If the path does not exist, fall back to using /var/lib/docker for feature detection.
135	testdir := home
136	if _, err := os.Stat(testdir); os.IsNotExist(err) {
137		testdir = filepath.Dir(testdir)
138	}
139
140	fsMagic, err := graphdriver.GetFSMagic(testdir)
141	if err != nil {
142		return nil, err
143	}
144	if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
145		backingFs = fsName
146	}
147
148	switch fsMagic {
149	case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs:
150		logrus.WithField("storage-driver", "overlay").Errorf("'overlay' is not supported over %s", backingFs)
151		return nil, graphdriver.ErrIncompatibleFS
152	}
153
154	supportsDType, err := fsutils.SupportsDType(testdir)
155	if err != nil {
156		return nil, err
157	}
158	if !supportsDType {
159		if !graphdriver.IsInitialized(home) {
160			return nil, overlayutils.ErrDTypeNotSupported("overlay", backingFs)
161		}
162		// allow running without d_type only for existing setups (#27443)
163		logrus.WithField("storage-driver", "overlay").Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
164	}
165
166	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
167	if err != nil {
168		return nil, err
169	}
170	// Create the driver home dir
171	if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
172		return nil, err
173	}
174
175	d := &Driver{
176		home:          home,
177		uidMaps:       uidMaps,
178		gidMaps:       gidMaps,
179		ctr:           graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
180		supportsDType: supportsDType,
181		locker:        locker.New(),
182	}
183
184	return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
185}
186
187func parseOptions(options []string) (*overlayOptions, error) {
188	o := &overlayOptions{}
189	for _, option := range options {
190		key, _, err := parsers.ParseKeyValueOpt(option)
191		if err != nil {
192			return nil, err
193		}
194		key = strings.ToLower(key)
195		switch key {
196		default:
197			return nil, fmt.Errorf("overlay: unknown option %s", key)
198		}
199	}
200	return o, nil
201}
202
203func supportsOverlay() error {
204	// We can try to modprobe overlay first before looking at
205	// proc/filesystems for when overlay is supported
206	exec.Command("modprobe", "overlay").Run()
207
208	f, err := os.Open("/proc/filesystems")
209	if err != nil {
210		return err
211	}
212	defer f.Close()
213
214	s := bufio.NewScanner(f)
215	for s.Scan() {
216		if s.Text() == "nodev\toverlay" {
217			return nil
218		}
219	}
220	logrus.WithField("storage-driver", "overlay").Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
221	return graphdriver.ErrNotSupported
222}
223
224func (d *Driver) String() string {
225	return "overlay"
226}
227
228// Status returns current driver information in a two dimensional string array.
229// Output contains "Backing Filesystem" used in this implementation.
230func (d *Driver) Status() [][2]string {
231	return [][2]string{
232		{"Backing Filesystem", backingFs},
233		{"Supports d_type", strconv.FormatBool(d.supportsDType)},
234	}
235}
236
237// GetMetadata returns metadata about the overlay driver such as root,
238// LowerDir, UpperDir, WorkDir and MergeDir used to store data.
239func (d *Driver) GetMetadata(id string) (map[string]string, error) {
240	dir := d.dir(id)
241	if _, err := os.Stat(dir); err != nil {
242		return nil, err
243	}
244
245	metadata := make(map[string]string)
246
247	// If id has a root, it is an image
248	rootDir := path.Join(dir, "root")
249	if _, err := os.Stat(rootDir); err == nil {
250		metadata["RootDir"] = rootDir
251		return metadata, nil
252	}
253
254	lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
255	if err != nil {
256		return nil, err
257	}
258
259	metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root")
260	metadata["UpperDir"] = path.Join(dir, "upper")
261	metadata["WorkDir"] = path.Join(dir, "work")
262	metadata["MergedDir"] = path.Join(dir, "merged")
263
264	return metadata, nil
265}
266
267// Cleanup any state created by overlay which should be cleaned when daemon
268// is being shutdown. For now, we just have to unmount the bind mounted
269// we had created.
270func (d *Driver) Cleanup() error {
271	return mount.RecursiveUnmount(d.home)
272}
273
274// CreateReadWrite creates a layer that is writable for use as a container
275// file system.
276func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
277	return d.Create(id, parent, opts)
278}
279
280// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
281// The parent filesystem is used to configure these directories for the overlay.
282func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) {
283
284	if opts != nil && len(opts.StorageOpt) != 0 {
285		return fmt.Errorf("--storage-opt is not supported for overlay")
286	}
287
288	dir := d.dir(id)
289
290	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
291	if err != nil {
292		return err
293	}
294	root := idtools.Identity{UID: rootUID, GID: rootGID}
295
296	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
297		return err
298	}
299	if err := idtools.MkdirAndChown(dir, 0700, root); err != nil {
300		return err
301	}
302
303	defer func() {
304		// Clean up on failure
305		if retErr != nil {
306			os.RemoveAll(dir)
307		}
308	}()
309
310	// Toplevel images are just a "root" dir
311	if parent == "" {
312		return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, root)
313	}
314
315	parentDir := d.dir(parent)
316
317	// Ensure parent exists
318	if _, err := os.Lstat(parentDir); err != nil {
319		return err
320	}
321
322	// If parent has a root, just do an overlay to it
323	parentRoot := path.Join(parentDir, "root")
324
325	if s, err := os.Lstat(parentRoot); err == nil {
326		if err := idtools.MkdirAndChown(path.Join(dir, "upper"), s.Mode(), root); err != nil {
327			return err
328		}
329		if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil {
330			return err
331		}
332		return ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666)
333	}
334
335	// Otherwise, copy the upper and the lower-id from the parent
336
337	lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id"))
338	if err != nil {
339		return err
340	}
341
342	if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil {
343		return err
344	}
345
346	parentUpperDir := path.Join(parentDir, "upper")
347	s, err := os.Lstat(parentUpperDir)
348	if err != nil {
349		return err
350	}
351
352	upperDir := path.Join(dir, "upper")
353	if err := idtools.MkdirAndChown(upperDir, s.Mode(), root); err != nil {
354		return err
355	}
356	if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil {
357		return err
358	}
359
360	return copy.DirCopy(parentUpperDir, upperDir, copy.Content, true)
361}
362
363func (d *Driver) dir(id string) string {
364	return path.Join(d.home, id)
365}
366
367// Remove cleans the directories that are created for this id.
368func (d *Driver) Remove(id string) error {
369	if id == "" {
370		return fmt.Errorf("refusing to remove the directories: id is empty")
371	}
372	d.locker.Lock(id)
373	defer d.locker.Unlock(id)
374	return system.EnsureRemoveAll(d.dir(id))
375}
376
377// Get creates and mounts the required file system for the given id and returns the mount path.
378func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err error) {
379	d.locker.Lock(id)
380	defer d.locker.Unlock(id)
381	dir := d.dir(id)
382	if _, err := os.Stat(dir); err != nil {
383		return nil, err
384	}
385	// If id has a root, just return it
386	rootDir := path.Join(dir, "root")
387	if _, err := os.Stat(rootDir); err == nil {
388		return containerfs.NewLocalContainerFS(rootDir), nil
389	}
390
391	mergedDir := path.Join(dir, "merged")
392	if count := d.ctr.Increment(mergedDir); count > 1 {
393		return containerfs.NewLocalContainerFS(mergedDir), nil
394	}
395	defer func() {
396		if err != nil {
397			if c := d.ctr.Decrement(mergedDir); c <= 0 {
398				if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil {
399					logrus.WithField("storage-driver", "overlay").Debugf("Failed to unmount %s: %v: %v", id, mntErr, err)
400				}
401				// Cleanup the created merged directory; see the comment in Put's rmdir
402				if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) {
403					logrus.WithField("storage-driver", "overlay").Warnf("Failed to remove %s: %v: %v", id, rmErr, err)
404				}
405			}
406		}
407	}()
408	lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
409	if err != nil {
410		return nil, err
411	}
412	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
413	if err != nil {
414		return nil, err
415	}
416	if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
417		return nil, err
418	}
419	var (
420		lowerDir = path.Join(d.dir(string(lowerID)), "root")
421		upperDir = path.Join(dir, "upper")
422		workDir  = path.Join(dir, "work")
423		opts     = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
424	)
425	if err := unix.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
426		return nil, fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
427	}
428	// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
429	// user namespace requires this to move a directory from lower to upper.
430	if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
431		return nil, err
432	}
433	return containerfs.NewLocalContainerFS(mergedDir), nil
434}
435
436// Put unmounts the mount path created for the give id.
437// It also removes the 'merged' directory to force the kernel to unmount the
438// overlay mount in other namespaces.
439func (d *Driver) Put(id string) error {
440	d.locker.Lock(id)
441	defer d.locker.Unlock(id)
442	// If id has a root, just return
443	if _, err := os.Stat(path.Join(d.dir(id), "root")); err == nil {
444		return nil
445	}
446	mountpoint := path.Join(d.dir(id), "merged")
447	logger := logrus.WithField("storage-driver", "overlay")
448	if count := d.ctr.Decrement(mountpoint); count > 0 {
449		return nil
450	}
451	if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil {
452		logger.Debugf("Failed to unmount %s overlay: %v", id, err)
453	}
454
455	// Remove the mountpoint here. Removing the mountpoint (in newer kernels)
456	// will cause all other instances of this mount in other mount namespaces
457	// to be unmounted. This is necessary to avoid cases where an overlay mount
458	// that is present in another namespace will cause subsequent mounts
459	// operations to fail with ebusy.  We ignore any errors here because this may
460	// fail on older kernels which don't have
461	// torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied.
462	if err := unix.Rmdir(mountpoint); err != nil {
463		logger.Debugf("Failed to remove %s overlay: %v", id, err)
464	}
465	return nil
466}
467
468// ApplyDiff applies the new layer on top of the root, if parent does not exist with will return an ErrApplyDiffFallback error.
469func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) {
470	dir := d.dir(id)
471
472	if parent == "" {
473		return 0, ErrApplyDiffFallback
474	}
475
476	parentRootDir := path.Join(d.dir(parent), "root")
477	if _, err := os.Stat(parentRootDir); err != nil {
478		return 0, ErrApplyDiffFallback
479	}
480
481	// We now know there is a parent, and it has a "root" directory containing
482	// the full root filesystem. We can just hardlink it and apply the
483	// layer. This relies on two things:
484	// 1) ApplyDiff is only run once on a clean (no writes to upper layer) container
485	// 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks)
486	// These are all currently true and are not expected to break
487
488	tmpRootDir, err := ioutil.TempDir(dir, "tmproot")
489	if err != nil {
490		return 0, err
491	}
492	defer func() {
493		if err != nil {
494			os.RemoveAll(tmpRootDir)
495		} else {
496			os.RemoveAll(path.Join(dir, "upper"))
497			os.RemoveAll(path.Join(dir, "work"))
498			os.RemoveAll(path.Join(dir, "merged"))
499			os.RemoveAll(path.Join(dir, "lower-id"))
500		}
501	}()
502
503	if err = copy.DirCopy(parentRootDir, tmpRootDir, copy.Hardlink, true); err != nil {
504		return 0, err
505	}
506
507	options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps}
508	if size, err = graphdriver.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil {
509		return 0, err
510	}
511
512	rootDir := path.Join(dir, "root")
513	if err := os.Rename(tmpRootDir, rootDir); err != nil {
514		return 0, err
515	}
516
517	return
518}
519
520// Exists checks to see if the id is already mounted.
521func (d *Driver) Exists(id string) bool {
522	_, err := os.Stat(d.dir(id))
523	return err == nil
524}
525