github.com/amylindburg/docker@v1.7.0/daemon/graphdriver/aufs/aufs.go (about)

     1  /*
     2  
     3  aufs driver directory structure
     4  
     5  .
     6  ├── layers // Metadata of layers
     7  │   ├── 1
     8  │   ├── 2
     9  │   └── 3
    10  ├── diff  // Content of the layer
    11  │   ├── 1  // Contains layers that need to be mounted for the id
    12  │   ├── 2
    13  │   └── 3
    14  └── mnt    // Mount points for the rw layers to be mounted
    15      ├── 1
    16      ├── 2
    17      └── 3
    18  
    19  */
    20  
    21  package aufs
    22  
    23  import (
    24  	"bufio"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"os"
    28  	"os/exec"
    29  	"path"
    30  	"strings"
    31  	"sync"
    32  	"syscall"
    33  
    34  	"github.com/Sirupsen/logrus"
    35  	"github.com/docker/docker/daemon/graphdriver"
    36  	"github.com/docker/docker/pkg/archive"
    37  	"github.com/docker/docker/pkg/chrootarchive"
    38  	"github.com/docker/docker/pkg/directory"
    39  	mountpk "github.com/docker/docker/pkg/mount"
    40  	"github.com/docker/docker/pkg/stringid"
    41  	"github.com/docker/libcontainer/label"
    42  )
    43  
    44  var (
    45  	ErrAufsNotSupported = fmt.Errorf("AUFS was not found in /proc/filesystems")
    46  	incompatibleFsMagic = []graphdriver.FsMagic{
    47  		graphdriver.FsMagicBtrfs,
    48  		graphdriver.FsMagicAufs,
    49  	}
    50  	backingFs = "<unknown>"
    51  
    52  	enableDirpermLock sync.Once
    53  	enableDirperm     bool
    54  )
    55  
    56  func init() {
    57  	graphdriver.Register("aufs", Init)
    58  }
    59  
    60  type Driver struct {
    61  	root       string
    62  	sync.Mutex // Protects concurrent modification to active
    63  	active     map[string]int
    64  }
    65  
    66  // New returns a new AUFS driver.
    67  // An error is returned if AUFS is not supported.
    68  func Init(root string, options []string) (graphdriver.Driver, error) {
    69  
    70  	// Try to load the aufs kernel module
    71  	if err := supportsAufs(); err != nil {
    72  		return nil, graphdriver.ErrNotSupported
    73  	}
    74  
    75  	fsMagic, err := graphdriver.GetFSMagic(root)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
    80  		backingFs = fsName
    81  	}
    82  
    83  	for _, magic := range incompatibleFsMagic {
    84  		if fsMagic == magic {
    85  			return nil, graphdriver.ErrIncompatibleFS
    86  		}
    87  	}
    88  
    89  	paths := []string{
    90  		"mnt",
    91  		"diff",
    92  		"layers",
    93  	}
    94  
    95  	a := &Driver{
    96  		root:   root,
    97  		active: make(map[string]int),
    98  	}
    99  
   100  	// Create the root aufs driver dir and return
   101  	// if it already exists
   102  	// If not populate the dir structure
   103  	if err := os.MkdirAll(root, 0755); err != nil {
   104  		if os.IsExist(err) {
   105  			return a, nil
   106  		}
   107  		return nil, err
   108  	}
   109  
   110  	if err := mountpk.MakePrivate(root); err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	for _, p := range paths {
   115  		if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
   116  			return nil, err
   117  		}
   118  	}
   119  	return a, nil
   120  }
   121  
   122  // Return a nil error if the kernel supports aufs
   123  // We cannot modprobe because inside dind modprobe fails
   124  // to run
   125  func supportsAufs() error {
   126  	// We can try to modprobe aufs first before looking at
   127  	// proc/filesystems for when aufs is supported
   128  	exec.Command("modprobe", "aufs").Run()
   129  
   130  	f, err := os.Open("/proc/filesystems")
   131  	if err != nil {
   132  		return err
   133  	}
   134  	defer f.Close()
   135  
   136  	s := bufio.NewScanner(f)
   137  	for s.Scan() {
   138  		if strings.Contains(s.Text(), "aufs") {
   139  			return nil
   140  		}
   141  	}
   142  	return ErrAufsNotSupported
   143  }
   144  
   145  func (a *Driver) rootPath() string {
   146  	return a.root
   147  }
   148  
   149  func (*Driver) String() string {
   150  	return "aufs"
   151  }
   152  
   153  func (a *Driver) Status() [][2]string {
   154  	ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
   155  	return [][2]string{
   156  		{"Root Dir", a.rootPath()},
   157  		{"Backing Filesystem", backingFs},
   158  		{"Dirs", fmt.Sprintf("%d", len(ids))},
   159  		{"Dirperm1 Supported", fmt.Sprintf("%v", useDirperm())},
   160  	}
   161  }
   162  
   163  // Exists returns true if the given id is registered with
   164  // this driver
   165  func (a *Driver) Exists(id string) bool {
   166  	if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
   167  		return false
   168  	}
   169  	return true
   170  }
   171  
   172  // Three folders are created for each id
   173  // mnt, layers, and diff
   174  func (a *Driver) Create(id, parent string) error {
   175  	if err := a.createDirsFor(id); err != nil {
   176  		return err
   177  	}
   178  	// Write the layers metadata
   179  	f, err := os.Create(path.Join(a.rootPath(), "layers", id))
   180  	if err != nil {
   181  		return err
   182  	}
   183  	defer f.Close()
   184  
   185  	if parent != "" {
   186  		ids, err := getParentIds(a.rootPath(), parent)
   187  		if err != nil {
   188  			return err
   189  		}
   190  
   191  		if _, err := fmt.Fprintln(f, parent); err != nil {
   192  			return err
   193  		}
   194  		for _, i := range ids {
   195  			if _, err := fmt.Fprintln(f, i); err != nil {
   196  				return err
   197  			}
   198  		}
   199  	}
   200  	return nil
   201  }
   202  
   203  func (a *Driver) createDirsFor(id string) error {
   204  	paths := []string{
   205  		"mnt",
   206  		"diff",
   207  	}
   208  
   209  	for _, p := range paths {
   210  		if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
   211  			return err
   212  		}
   213  	}
   214  	return nil
   215  }
   216  
   217  // Unmount and remove the dir information
   218  func (a *Driver) Remove(id string) error {
   219  	// Protect the a.active from concurrent access
   220  	a.Lock()
   221  	defer a.Unlock()
   222  
   223  	if a.active[id] != 0 {
   224  		logrus.Errorf("Removing active id %s", id)
   225  	}
   226  
   227  	// Make sure the dir is umounted first
   228  	if err := a.unmount(id); err != nil {
   229  		return err
   230  	}
   231  	tmpDirs := []string{
   232  		"mnt",
   233  		"diff",
   234  	}
   235  
   236  	// Atomically remove each directory in turn by first moving it out of the
   237  	// way (so that docker doesn't find it anymore) before doing removal of
   238  	// the whole tree.
   239  	for _, p := range tmpDirs {
   240  
   241  		realPath := path.Join(a.rootPath(), p, id)
   242  		tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id))
   243  		if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) {
   244  			return err
   245  		}
   246  		defer os.RemoveAll(tmpPath)
   247  	}
   248  
   249  	// Remove the layers file for the id
   250  	if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
   251  		return err
   252  	}
   253  	return nil
   254  }
   255  
   256  // Return the rootfs path for the id
   257  // This will mount the dir at it's given path
   258  func (a *Driver) Get(id, mountLabel string) (string, error) {
   259  	ids, err := getParentIds(a.rootPath(), id)
   260  	if err != nil {
   261  		if !os.IsNotExist(err) {
   262  			return "", err
   263  		}
   264  		ids = []string{}
   265  	}
   266  
   267  	// Protect the a.active from concurrent access
   268  	a.Lock()
   269  	defer a.Unlock()
   270  
   271  	count := a.active[id]
   272  
   273  	// If a dir does not have a parent ( no layers )do not try to mount
   274  	// just return the diff path to the data
   275  	out := path.Join(a.rootPath(), "diff", id)
   276  	if len(ids) > 0 {
   277  		out = path.Join(a.rootPath(), "mnt", id)
   278  
   279  		if count == 0 {
   280  			if err := a.mount(id, mountLabel); err != nil {
   281  				return "", err
   282  			}
   283  		}
   284  	}
   285  
   286  	a.active[id] = count + 1
   287  
   288  	return out, nil
   289  }
   290  
   291  func (a *Driver) Put(id string) error {
   292  	// Protect the a.active from concurrent access
   293  	a.Lock()
   294  	defer a.Unlock()
   295  
   296  	if count := a.active[id]; count > 1 {
   297  		a.active[id] = count - 1
   298  	} else {
   299  		ids, _ := getParentIds(a.rootPath(), id)
   300  		// We only mounted if there are any parents
   301  		if ids != nil && len(ids) > 0 {
   302  			a.unmount(id)
   303  		}
   304  		delete(a.active, id)
   305  	}
   306  	return nil
   307  }
   308  
   309  // Diff produces an archive of the changes between the specified
   310  // layer and its parent layer which may be "".
   311  func (a *Driver) Diff(id, parent string) (archive.Archive, error) {
   312  	// AUFS doesn't need the parent layer to produce a diff.
   313  	return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
   314  		Compression:     archive.Uncompressed,
   315  		ExcludePatterns: []string{".wh..wh.*"},
   316  	})
   317  }
   318  
   319  func (a *Driver) applyDiff(id string, diff archive.ArchiveReader) error {
   320  	return chrootarchive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
   321  }
   322  
   323  // DiffSize calculates the changes between the specified id
   324  // and its parent and returns the size in bytes of the changes
   325  // relative to its base filesystem directory.
   326  func (a *Driver) DiffSize(id, parent string) (size int64, err error) {
   327  	// AUFS doesn't need the parent layer to calculate the diff size.
   328  	return directory.Size(path.Join(a.rootPath(), "diff", id))
   329  }
   330  
   331  // ApplyDiff extracts the changeset from the given diff into the
   332  // layer with the specified id and parent, returning the size of the
   333  // new layer in bytes.
   334  func (a *Driver) ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error) {
   335  	// AUFS doesn't need the parent id to apply the diff.
   336  	if err = a.applyDiff(id, diff); err != nil {
   337  		return
   338  	}
   339  
   340  	return a.DiffSize(id, parent)
   341  }
   342  
   343  // Changes produces a list of changes between the specified layer
   344  // and its parent layer. If parent is "", then all changes will be ADD changes.
   345  func (a *Driver) Changes(id, parent string) ([]archive.Change, error) {
   346  	// AUFS doesn't have snapshots, so we need to get changes from all parent
   347  	// layers.
   348  	layers, err := a.getParentLayerPaths(id)
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  	return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
   353  }
   354  
   355  func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
   356  	parentIds, err := getParentIds(a.rootPath(), id)
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  	layers := make([]string, len(parentIds))
   361  
   362  	// Get the diff paths for all the parent ids
   363  	for i, p := range parentIds {
   364  		layers[i] = path.Join(a.rootPath(), "diff", p)
   365  	}
   366  	return layers, nil
   367  }
   368  
   369  func (a *Driver) mount(id, mountLabel string) error {
   370  	// If the id is mounted or we get an error return
   371  	if mounted, err := a.mounted(id); err != nil || mounted {
   372  		return err
   373  	}
   374  
   375  	var (
   376  		target = path.Join(a.rootPath(), "mnt", id)
   377  		rw     = path.Join(a.rootPath(), "diff", id)
   378  	)
   379  
   380  	layers, err := a.getParentLayerPaths(id)
   381  	if err != nil {
   382  		return err
   383  	}
   384  
   385  	if err := a.aufsMount(layers, rw, target, mountLabel); err != nil {
   386  		return fmt.Errorf("error creating aufs mount to %s: %v", target, err)
   387  	}
   388  	return nil
   389  }
   390  
   391  func (a *Driver) unmount(id string) error {
   392  	if mounted, err := a.mounted(id); err != nil || !mounted {
   393  		return err
   394  	}
   395  	target := path.Join(a.rootPath(), "mnt", id)
   396  	return Unmount(target)
   397  }
   398  
   399  func (a *Driver) mounted(id string) (bool, error) {
   400  	target := path.Join(a.rootPath(), "mnt", id)
   401  	return mountpk.Mounted(target)
   402  }
   403  
   404  // During cleanup aufs needs to unmount all mountpoints
   405  func (a *Driver) Cleanup() error {
   406  	ids, err := loadIds(path.Join(a.rootPath(), "layers"))
   407  	if err != nil {
   408  		return err
   409  	}
   410  
   411  	for _, id := range ids {
   412  		if err := a.unmount(id); err != nil {
   413  			logrus.Errorf("Unmounting %s: %s", stringid.TruncateID(id), err)
   414  		}
   415  	}
   416  
   417  	return mountpk.Unmount(a.root)
   418  }
   419  
   420  func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) {
   421  	defer func() {
   422  		if err != nil {
   423  			Unmount(target)
   424  		}
   425  	}()
   426  
   427  	// Mount options are clipped to page size(4096 bytes). If there are more
   428  	// layers then these are remounted individually using append.
   429  
   430  	offset := 54
   431  	if useDirperm() {
   432  		offset += len("dirperm1")
   433  	}
   434  	b := make([]byte, syscall.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel
   435  	bp := copy(b, fmt.Sprintf("br:%s=rw", rw))
   436  
   437  	firstMount := true
   438  	i := 0
   439  
   440  	for {
   441  		for ; i < len(ro); i++ {
   442  			layer := fmt.Sprintf(":%s=ro+wh", ro[i])
   443  
   444  			if firstMount {
   445  				if bp+len(layer) > len(b) {
   446  					break
   447  				}
   448  				bp += copy(b[bp:], layer)
   449  			} else {
   450  				data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel)
   451  				if err = mount("none", target, "aufs", MsRemount, data); err != nil {
   452  					return
   453  				}
   454  			}
   455  		}
   456  
   457  		if firstMount {
   458  			opts := "dio,xino=/dev/shm/aufs.xino"
   459  			if useDirperm() {
   460  				opts += ",dirperm1"
   461  			}
   462  			data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel)
   463  			if err = mount("none", target, "aufs", 0, data); err != nil {
   464  				return
   465  			}
   466  			firstMount = false
   467  		}
   468  
   469  		if i == len(ro) {
   470  			break
   471  		}
   472  	}
   473  
   474  	return
   475  }
   476  
   477  // useDirperm checks dirperm1 mount option can be used with the current
   478  // version of aufs.
   479  func useDirperm() bool {
   480  	enableDirpermLock.Do(func() {
   481  		base, err := ioutil.TempDir("", "docker-aufs-base")
   482  		if err != nil {
   483  			logrus.Errorf("error checking dirperm1: %v", err)
   484  			return
   485  		}
   486  		defer os.RemoveAll(base)
   487  
   488  		union, err := ioutil.TempDir("", "docker-aufs-union")
   489  		if err != nil {
   490  			logrus.Errorf("error checking dirperm1: %v", err)
   491  			return
   492  		}
   493  		defer os.RemoveAll(union)
   494  
   495  		opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base)
   496  		if err := mount("none", union, "aufs", 0, opts); err != nil {
   497  			return
   498  		}
   499  		enableDirperm = true
   500  		if err := Unmount(union); err != nil {
   501  			logrus.Errorf("error checking dirperm1: failed to unmount %v", err)
   502  		}
   503  	})
   504  	return enableDirperm
   505  }