github.com/afein/docker@v1.8.2/daemon/graphdriver/aufs/aufs.go (about)

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