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