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