16 "github.com/Sirupsen/logrus"
17 "github.com/docker/docker/daemon/graphdriver"
18 "github.com/docker/docker/daemon/graphdriver/overlayutils"
19 "github.com/docker/docker/pkg/archive"
20 "github.com/docker/docker/pkg/fsutils"
21 "github.com/docker/docker/pkg/idtools"
22 "github.com/docker/docker/pkg/locker"
23 "github.com/docker/docker/pkg/mount"
24 "github.com/docker/docker/pkg/system"
25 "github.com/opencontainers/selinux/go-selinux/label"
28 // This is a small wrapper over the NaiveDiffWriter that lets us have a custom
29 // implementation of ApplyDiff()
32 // ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer.
33 ErrApplyDiffFallback = fmt.Errorf("Fall back to normal ApplyDiff")
34 backingFs = "<unknown>"
37 // ApplyDiffProtoDriver wraps the ProtoDriver by extending the interface with ApplyDiff method.
38 type ApplyDiffProtoDriver interface {
39 graphdriver.ProtoDriver
40 // ApplyDiff writes the diff to the archive for the given id and parent id.
41 // It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise.
42 ApplyDiff(id, parent string, diff io.Reader) (size int64, err error)
45 type naiveDiffDriverWithApply struct {
47 applyDiff ApplyDiffProtoDriver
50 // NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff.
51 func NaiveDiffDriverWithApply(driver ApplyDiffProtoDriver, uidMaps, gidMaps []idtools.IDMap) graphdriver.Driver {
52 return &naiveDiffDriverWithApply{
53 Driver: graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps),
58 // ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback.
59 func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
60 b, err := d.applyDiff.ApplyDiff(id, parent, diff)
61 if err == ErrApplyDiffFallback {
62 return d.Driver.ApplyDiff(id, parent, diff)
67 // This backend uses the overlay union filesystem for containers
68 // plus hard link file sharing for images.
70 // Each container/image can have a "root" subdirectory which is a plain
71 // filesystem hierarchy, or they can use overlay.
73 // If they use overlay there is a "upper" directory and a "lower-id"
74 // file, as well as "merged" and "work" directories. The "upper"
75 // directory has the upper layer of the overlay, and "lower-id" contains
76 // the id of the parent whose "root" directory shall be used as the lower
77 // layer in the overlay. The overlay itself is mounted in the "merged"
78 // directory, and the "work" dir is needed for overlay to work.
80 // When an overlay layer is created there are two cases, either the
81 // parent has a "root" dir, then we start out with an empty "upper"
82 // directory overlaid on the parents root. This is typically the
83 // case with the init layer of a container which is based on an image.
84 // If there is no "root" in the parent, we inherit the lower-id from
85 // the parent and start by making a copy in the parent's "upper" dir.
86 // This is typically the case for a container layer which copies
87 // its parent -init upper layer.
89 // Additionally we also have a custom implementation of ApplyLayer
90 // which makes a recursive copy of the parent "root" layer using
91 // hardlinks to share file data, and then applies the layer on top
92 // of that. This means all child images share file (but not directory)
93 // data with the parent.
95 // Driver contains information about the home directory and the list of active mounts that are created using this driver.
98 uidMaps []idtools.IDMap
99 gidMaps []idtools.IDMap
100 ctr *graphdriver.RefCounter
102 locker *locker.Locker
106 graphdriver.Register("overlay", Init)
109 // Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem.
110 // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error.
111 // If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned.
112 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
114 if err := supportsOverlay(); err != nil {
115 return nil, graphdriver.ErrNotSupported
118 fsMagic, err := graphdriver.GetFSMagic(home)
122 if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
127 case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs, graphdriver.FsMagicEcryptfs:
128 logrus.Errorf("'overlay' is not supported over %s", backingFs)
129 return nil, graphdriver.ErrIncompatibleFS
132 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
136 // Create the driver home dir
137 if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
141 if err := mount.MakePrivate(home); err != nil {
145 supportsDType, err := fsutils.SupportsDType(home)
150 // not a fatal error until v17.12 (#27443)
151 logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
158 ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
159 supportsDType: supportsDType,
160 locker: locker.New(),
163 return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
166 func supportsOverlay() error {
167 // We can try to modprobe overlay first before looking at
168 // proc/filesystems for when overlay is supported
169 exec.Command("modprobe", "overlay").Run()
171 f, err := os.Open("/proc/filesystems")
177 s := bufio.NewScanner(f)
179 if s.Text() == "nodev\toverlay" {
183 logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
184 return graphdriver.ErrNotSupported
187 func (d *Driver) String() string {
191 // Status returns current driver information in a two dimensional string array.
192 // Output contains "Backing Filesystem" used in this implementation.
193 func (d *Driver) Status() [][2]string {
195 {"Backing Filesystem", backingFs},
196 {"Supports d_type", strconv.FormatBool(d.supportsDType)},
200 // GetMetadata returns meta data about the overlay driver such as root, LowerDir, UpperDir, WorkDir and MergeDir used to store data.
201 func (d *Driver) GetMetadata(id string) (map[string]string, error) {
203 if _, err := os.Stat(dir); err != nil {
207 metadata := make(map[string]string)
209 // If id has a root, it is an image
210 rootDir := path.Join(dir, "root")
211 if _, err := os.Stat(rootDir); err == nil {
212 metadata["RootDir"] = rootDir
216 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
221 metadata["LowerDir"] = path.Join(d.dir(string(lowerID)), "root")
222 metadata["UpperDir"] = path.Join(dir, "upper")
223 metadata["WorkDir"] = path.Join(dir, "work")
224 metadata["MergedDir"] = path.Join(dir, "merged")
229 // Cleanup any state created by overlay which should be cleaned when daemon
230 // is being shutdown. For now, we just have to unmount the bind mounted
232 func (d *Driver) Cleanup() error {
233 return mount.Unmount(d.home)
236 // CreateReadWrite creates a layer that is writable for use as a container
238 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
239 return d.Create(id, parent, opts)
242 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
243 // The parent filesystem is used to configure these directories for the overlay.
244 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) {
246 if opts != nil && len(opts.StorageOpt) != 0 {
247 return fmt.Errorf("--storage-opt is not supported for overlay")
252 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
256 if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil {
259 if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil {
264 // Clean up on failure
270 // Toplevel images are just a "root" dir
272 if err := idtools.MkdirAs(path.Join(dir, "root"), 0755, rootUID, rootGID); err != nil {
278 parentDir := d.dir(parent)
280 // Ensure parent exists
281 if _, err := os.Lstat(parentDir); err != nil {
285 // If parent has a root, just do an overlay to it
286 parentRoot := path.Join(parentDir, "root")
288 if s, err := os.Lstat(parentRoot); err == nil {
289 if err := idtools.MkdirAs(path.Join(dir, "upper"), s.Mode(), rootUID, rootGID); err != nil {
292 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
295 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
298 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil {
304 // Otherwise, copy the upper and the lower-id from the parent
306 lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id"))
311 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil {
315 parentUpperDir := path.Join(parentDir, "upper")
316 s, err := os.Lstat(parentUpperDir)
321 upperDir := path.Join(dir, "upper")
322 if err := idtools.MkdirAs(upperDir, s.Mode(), rootUID, rootGID); err != nil {
325 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
328 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
332 return copyDir(parentUpperDir, upperDir, 0)
335 func (d *Driver) dir(id string) string {
336 return path.Join(d.home, id)
339 // Remove cleans the directories that are created for this id.
340 func (d *Driver) Remove(id string) error {
342 defer d.locker.Unlock(id)
343 return system.EnsureRemoveAll(d.dir(id))
346 // Get creates and mounts the required file system for the given id and returns the mount path.
347 func (d *Driver) Get(id string, mountLabel string) (s string, err error) {
349 defer d.locker.Unlock(id)
351 if _, err := os.Stat(dir); err != nil {
354 // If id has a root, just return it
355 rootDir := path.Join(dir, "root")
356 if _, err := os.Stat(rootDir); err == nil {
359 mergedDir := path.Join(dir, "merged")
360 if count := d.ctr.Increment(mergedDir); count > 1 {
361 return mergedDir, nil
365 if c := d.ctr.Decrement(mergedDir); c <= 0 {
366 syscall.Unmount(mergedDir, 0)
370 lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
375 lowerDir = path.Join(d.dir(string(lowerID)), "root")
376 upperDir = path.Join(dir, "upper")
377 workDir = path.Join(dir, "work")
378 opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
380 if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
381 return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
383 // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
384 // user namespace requires this to move a directory from lower to upper.
385 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
389 if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
392 return mergedDir, nil
395 // Put unmounts the mount path created for the give id.
396 func (d *Driver) Put(id string) error {
398 defer d.locker.Unlock(id)
399 // If id has a root, just return
400 if _, err := os.Stat(path.Join(d.dir(id), "root")); err == nil {
403 mountpoint := path.Join(d.dir(id), "merged")
404 if count := d.ctr.Decrement(mountpoint); count > 0 {
407 if err := syscall.Unmount(mountpoint, syscall.MNT_DETACH); err != nil {
408 logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
413 // ApplyDiff applies the new layer on top of the root, if parent does not exist with will return an ErrApplyDiffFallback error.
414 func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) {
418 return 0, ErrApplyDiffFallback
421 parentRootDir := path.Join(d.dir(parent), "root")
422 if _, err := os.Stat(parentRootDir); err != nil {
423 return 0, ErrApplyDiffFallback
426 // We now know there is a parent, and it has a "root" directory containing
427 // the full root filesystem. We can just hardlink it and apply the
428 // layer. This relies on two things:
429 // 1) ApplyDiff is only run once on a clean (no writes to upper layer) container
430 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks)
431 // These are all currently true and are not expected to break
433 tmpRootDir, err := ioutil.TempDir(dir, "tmproot")
439 os.RemoveAll(tmpRootDir)
441 os.RemoveAll(path.Join(dir, "upper"))
442 os.RemoveAll(path.Join(dir, "work"))
443 os.RemoveAll(path.Join(dir, "merged"))
444 os.RemoveAll(path.Join(dir, "lower-id"))
448 if err = copyDir(parentRootDir, tmpRootDir, copyHardlink); err != nil {
452 options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps}
453 if size, err = graphdriver.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil {
457 rootDir := path.Join(dir, "root")
458 if err := os.Rename(tmpRootDir, rootDir); err != nil {
465 // Exists checks to see if the id is already mounted.
466 func (d *Driver) Exists(id string) bool {
467 _, err := os.Stat(d.dir(id))