Tizen_4.0 base
[platform/upstream/docker-engine.git] / daemon / graphdriver / overlay / overlay.go
1 // +build linux
2
3 package overlay
4
5 import (
6         "bufio"
7         "fmt"
8         "io"
9         "io/ioutil"
10         "os"
11         "os/exec"
12         "path"
13         "strconv"
14         "syscall"
15
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"
26 )
27
28 // This is a small wrapper over the NaiveDiffWriter that lets us have a custom
29 // implementation of ApplyDiff()
30
31 var (
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>"
35 )
36
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)
43 }
44
45 type naiveDiffDriverWithApply struct {
46         graphdriver.Driver
47         applyDiff ApplyDiffProtoDriver
48 }
49
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),
54                 applyDiff: driver,
55         }
56 }
57
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)
63         }
64         return b, err
65 }
66
67 // This backend uses the overlay union filesystem for containers
68 // plus hard link file sharing for images.
69
70 // Each container/image can have a "root" subdirectory which is a plain
71 // filesystem hierarchy, or they can use overlay.
72
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.
79
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.
88
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.
94
95 // Driver contains information about the home directory and the list of active mounts that are created using this driver.
96 type Driver struct {
97         home          string
98         uidMaps       []idtools.IDMap
99         gidMaps       []idtools.IDMap
100         ctr           *graphdriver.RefCounter
101         supportsDType bool
102         locker        *locker.Locker
103 }
104
105 func init() {
106         graphdriver.Register("overlay", Init)
107 }
108
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) {
113
114         if err := supportsOverlay(); err != nil {
115                 return nil, graphdriver.ErrNotSupported
116         }
117
118         fsMagic, err := graphdriver.GetFSMagic(home)
119         if err != nil {
120                 return nil, err
121         }
122         if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
123                 backingFs = fsName
124         }
125
126         switch fsMagic {
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
130         }
131
132         rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
133         if err != nil {
134                 return nil, err
135         }
136         // Create the driver home dir
137         if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
138                 return nil, err
139         }
140
141         if err := mount.MakePrivate(home); err != nil {
142                 return nil, err
143         }
144
145         supportsDType, err := fsutils.SupportsDType(home)
146         if err != nil {
147                 return nil, err
148         }
149         if !supportsDType {
150                 // not a fatal error until v17.12 (#27443)
151                 logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
152         }
153
154         d := &Driver{
155                 home:          home,
156                 uidMaps:       uidMaps,
157                 gidMaps:       gidMaps,
158                 ctr:           graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
159                 supportsDType: supportsDType,
160                 locker:        locker.New(),
161         }
162
163         return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
164 }
165
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()
170
171         f, err := os.Open("/proc/filesystems")
172         if err != nil {
173                 return err
174         }
175         defer f.Close()
176
177         s := bufio.NewScanner(f)
178         for s.Scan() {
179                 if s.Text() == "nodev\toverlay" {
180                         return nil
181                 }
182         }
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
185 }
186
187 func (d *Driver) String() string {
188         return "overlay"
189 }
190
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 {
194         return [][2]string{
195                 {"Backing Filesystem", backingFs},
196                 {"Supports d_type", strconv.FormatBool(d.supportsDType)},
197         }
198 }
199
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) {
202         dir := d.dir(id)
203         if _, err := os.Stat(dir); err != nil {
204                 return nil, err
205         }
206
207         metadata := make(map[string]string)
208
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
213                 return metadata, nil
214         }
215
216         lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
217         if err != nil {
218                 return nil, err
219         }
220
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")
225
226         return metadata, nil
227 }
228
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
231 // we had created.
232 func (d *Driver) Cleanup() error {
233         return mount.Unmount(d.home)
234 }
235
236 // CreateReadWrite creates a layer that is writable for use as a container
237 // file system.
238 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
239         return d.Create(id, parent, opts)
240 }
241
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) {
245
246         if opts != nil && len(opts.StorageOpt) != 0 {
247                 return fmt.Errorf("--storage-opt is not supported for overlay")
248         }
249
250         dir := d.dir(id)
251
252         rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
253         if err != nil {
254                 return err
255         }
256         if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil {
257                 return err
258         }
259         if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil {
260                 return err
261         }
262
263         defer func() {
264                 // Clean up on failure
265                 if retErr != nil {
266                         os.RemoveAll(dir)
267                 }
268         }()
269
270         // Toplevel images are just a "root" dir
271         if parent == "" {
272                 if err := idtools.MkdirAs(path.Join(dir, "root"), 0755, rootUID, rootGID); err != nil {
273                         return err
274                 }
275                 return nil
276         }
277
278         parentDir := d.dir(parent)
279
280         // Ensure parent exists
281         if _, err := os.Lstat(parentDir); err != nil {
282                 return err
283         }
284
285         // If parent has a root, just do an overlay to it
286         parentRoot := path.Join(parentDir, "root")
287
288         if s, err := os.Lstat(parentRoot); err == nil {
289                 if err := idtools.MkdirAs(path.Join(dir, "upper"), s.Mode(), rootUID, rootGID); err != nil {
290                         return err
291                 }
292                 if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
293                         return err
294                 }
295                 if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
296                         return err
297                 }
298                 if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil {
299                         return err
300                 }
301                 return nil
302         }
303
304         // Otherwise, copy the upper and the lower-id from the parent
305
306         lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id"))
307         if err != nil {
308                 return err
309         }
310
311         if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil {
312                 return err
313         }
314
315         parentUpperDir := path.Join(parentDir, "upper")
316         s, err := os.Lstat(parentUpperDir)
317         if err != nil {
318                 return err
319         }
320
321         upperDir := path.Join(dir, "upper")
322         if err := idtools.MkdirAs(upperDir, s.Mode(), rootUID, rootGID); err != nil {
323                 return err
324         }
325         if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
326                 return err
327         }
328         if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
329                 return err
330         }
331
332         return copyDir(parentUpperDir, upperDir, 0)
333 }
334
335 func (d *Driver) dir(id string) string {
336         return path.Join(d.home, id)
337 }
338
339 // Remove cleans the directories that are created for this id.
340 func (d *Driver) Remove(id string) error {
341         d.locker.Lock(id)
342         defer d.locker.Unlock(id)
343         return system.EnsureRemoveAll(d.dir(id))
344 }
345
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) {
348         d.locker.Lock(id)
349         defer d.locker.Unlock(id)
350         dir := d.dir(id)
351         if _, err := os.Stat(dir); err != nil {
352                 return "", err
353         }
354         // If id has a root, just return it
355         rootDir := path.Join(dir, "root")
356         if _, err := os.Stat(rootDir); err == nil {
357                 return rootDir, nil
358         }
359         mergedDir := path.Join(dir, "merged")
360         if count := d.ctr.Increment(mergedDir); count > 1 {
361                 return mergedDir, nil
362         }
363         defer func() {
364                 if err != nil {
365                         if c := d.ctr.Decrement(mergedDir); c <= 0 {
366                                 syscall.Unmount(mergedDir, 0)
367                         }
368                 }
369         }()
370         lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
371         if err != nil {
372                 return "", err
373         }
374         var (
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)
379         )
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)
382         }
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)
386         if err != nil {
387                 return "", err
388         }
389         if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
390                 return "", err
391         }
392         return mergedDir, nil
393 }
394
395 // Put unmounts the mount path created for the give id.
396 func (d *Driver) Put(id string) error {
397         d.locker.Lock(id)
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 {
401                 return nil
402         }
403         mountpoint := path.Join(d.dir(id), "merged")
404         if count := d.ctr.Decrement(mountpoint); count > 0 {
405                 return nil
406         }
407         if err := syscall.Unmount(mountpoint, syscall.MNT_DETACH); err != nil {
408                 logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
409         }
410         return nil
411 }
412
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) {
415         dir := d.dir(id)
416
417         if parent == "" {
418                 return 0, ErrApplyDiffFallback
419         }
420
421         parentRootDir := path.Join(d.dir(parent), "root")
422         if _, err := os.Stat(parentRootDir); err != nil {
423                 return 0, ErrApplyDiffFallback
424         }
425
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
432
433         tmpRootDir, err := ioutil.TempDir(dir, "tmproot")
434         if err != nil {
435                 return 0, err
436         }
437         defer func() {
438                 if err != nil {
439                         os.RemoveAll(tmpRootDir)
440                 } else {
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"))
445                 }
446         }()
447
448         if err = copyDir(parentRootDir, tmpRootDir, copyHardlink); err != nil {
449                 return 0, err
450         }
451
452         options := &archive.TarOptions{UIDMaps: d.uidMaps, GIDMaps: d.gidMaps}
453         if size, err = graphdriver.ApplyUncompressedLayer(tmpRootDir, diff, options); err != nil {
454                 return 0, err
455         }
456
457         rootDir := path.Join(dir, "root")
458         if err := os.Rename(tmpRootDir, rootDir); err != nil {
459                 return 0, err
460         }
461
462         return
463 }
464
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))
468         return err == nil
469 }