mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
f198ee525a
All archive that are created from somewhere generally have to be closed, because at some point there is a file or a pipe or something that backs them. So, we make archive.Archive a ReadCloser. However, code consuming archives does not typically close them so we add an archive.ArchiveReader and use that when we're only reading. We then change all the Tar/Archive places to create ReadClosers, and to properly close them everywhere. As an added bonus we can use ReadCloserWrapper rather than EofReader in several places, which is good as EofReader doesn't always work right. For instance, many compression schemes like gzip knows it is EOF before having read the EOF from the stream, so the EofCloser never sees an EOF. Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
397 lines
8.7 KiB
Go
397 lines
8.7 KiB
Go
/*
|
|
|
|
aufs driver directory structure
|
|
|
|
.
|
|
├── layers // Metadata of layers
|
|
│ ├── 1
|
|
│ ├── 2
|
|
│ └── 3
|
|
├── diffs // Content of the layer
|
|
│ ├── 1 // Contains layers that need to be mounted for the id
|
|
│ ├── 2
|
|
│ └── 3
|
|
└── mnt // Mount points for the rw layers to be mounted
|
|
├── 1
|
|
├── 2
|
|
└── 3
|
|
|
|
*/
|
|
|
|
package aufs
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"github.com/dotcloud/docker/archive"
|
|
"github.com/dotcloud/docker/graphdriver"
|
|
mountpk "github.com/dotcloud/docker/pkg/mount"
|
|
"github.com/dotcloud/docker/utils"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
func init() {
|
|
graphdriver.Register("aufs", Init)
|
|
}
|
|
|
|
type Driver struct {
|
|
root string
|
|
sync.Mutex // Protects concurrent modification to active
|
|
active map[string]int
|
|
}
|
|
|
|
// New returns a new AUFS driver.
|
|
// An error is returned if AUFS is not supported.
|
|
func Init(root string) (graphdriver.Driver, error) {
|
|
// Try to load the aufs kernel module
|
|
if err := supportsAufs(); err != nil {
|
|
return nil, err
|
|
}
|
|
paths := []string{
|
|
"mnt",
|
|
"diff",
|
|
"layers",
|
|
}
|
|
|
|
a := &Driver{
|
|
root: root,
|
|
active: make(map[string]int),
|
|
}
|
|
|
|
// Create the root aufs driver dir and return
|
|
// if it already exists
|
|
// If not populate the dir structure
|
|
if err := os.MkdirAll(root, 0755); err != nil {
|
|
if os.IsExist(err) {
|
|
return a, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
for _, p := range paths {
|
|
if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// Return a nil error if the kernel supports aufs
|
|
// We cannot modprobe because inside dind modprobe fails
|
|
// to run
|
|
func supportsAufs() error {
|
|
// We can try to modprobe aufs first before looking at
|
|
// proc/filesystems for when aufs is supported
|
|
exec.Command("modprobe", "aufs").Run()
|
|
|
|
f, err := os.Open("/proc/filesystems")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
s := bufio.NewScanner(f)
|
|
for s.Scan() {
|
|
if strings.Contains(s.Text(), "aufs") {
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("AUFS was not found in /proc/filesystems")
|
|
}
|
|
|
|
func (a Driver) rootPath() string {
|
|
return a.root
|
|
}
|
|
|
|
func (Driver) String() string {
|
|
return "aufs"
|
|
}
|
|
|
|
func (a Driver) Status() [][2]string {
|
|
ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
|
|
return [][2]string{
|
|
{"Root Dir", a.rootPath()},
|
|
{"Dirs", fmt.Sprintf("%d", len(ids))},
|
|
}
|
|
}
|
|
|
|
// Exists returns true if the given id is registered with
|
|
// this driver
|
|
func (a Driver) Exists(id string) bool {
|
|
if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Three folders are created for each id
|
|
// mnt, layers, and diff
|
|
func (a *Driver) Create(id, parent string) error {
|
|
if err := a.createDirsFor(id); err != nil {
|
|
return err
|
|
}
|
|
// Write the layers metadata
|
|
f, err := os.Create(path.Join(a.rootPath(), "layers", id))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
if parent != "" {
|
|
ids, err := getParentIds(a.rootPath(), parent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := fmt.Fprintln(f, parent); err != nil {
|
|
return err
|
|
}
|
|
for _, i := range ids {
|
|
if _, err := fmt.Fprintln(f, i); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *Driver) createDirsFor(id string) error {
|
|
paths := []string{
|
|
"mnt",
|
|
"diff",
|
|
}
|
|
|
|
for _, p := range paths {
|
|
if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Unmount and remove the dir information
|
|
func (a *Driver) Remove(id string) error {
|
|
// Protect the a.active from concurrent access
|
|
a.Lock()
|
|
defer a.Unlock()
|
|
|
|
if a.active[id] != 0 {
|
|
utils.Errorf("Warning: removing active id %s\n", id)
|
|
}
|
|
|
|
// Make sure the dir is umounted first
|
|
if err := a.unmount(id); err != nil {
|
|
return err
|
|
}
|
|
tmpDirs := []string{
|
|
"mnt",
|
|
"diff",
|
|
}
|
|
|
|
// Atomically remove each directory in turn by first moving it out of the
|
|
// way (so that docker doesn't find it anymore) before doing removal of
|
|
// the whole tree.
|
|
for _, p := range tmpDirs {
|
|
|
|
realPath := path.Join(a.rootPath(), p, id)
|
|
tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id))
|
|
if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(tmpPath)
|
|
}
|
|
|
|
// Remove the layers file for the id
|
|
if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Return the rootfs path for the id
|
|
// This will mount the dir at it's given path
|
|
func (a *Driver) Get(id string) (string, error) {
|
|
ids, err := getParentIds(a.rootPath(), id)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return "", err
|
|
}
|
|
ids = []string{}
|
|
}
|
|
|
|
// Protect the a.active from concurrent access
|
|
a.Lock()
|
|
defer a.Unlock()
|
|
|
|
count := a.active[id]
|
|
|
|
// If a dir does not have a parent ( no layers )do not try to mount
|
|
// just return the diff path to the data
|
|
out := path.Join(a.rootPath(), "diff", id)
|
|
if len(ids) > 0 {
|
|
out = path.Join(a.rootPath(), "mnt", id)
|
|
|
|
if count == 0 {
|
|
if err := a.mount(id); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
}
|
|
|
|
a.active[id] = count + 1
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func (a *Driver) Put(id string) {
|
|
// Protect the a.active from concurrent access
|
|
a.Lock()
|
|
defer a.Unlock()
|
|
|
|
if count := a.active[id]; count > 1 {
|
|
a.active[id] = count - 1
|
|
} else {
|
|
ids, _ := getParentIds(a.rootPath(), id)
|
|
// We only mounted if there are any parents
|
|
if ids != nil && len(ids) > 0 {
|
|
a.unmount(id)
|
|
}
|
|
delete(a.active, id)
|
|
}
|
|
}
|
|
|
|
// Returns an archive of the contents for the id
|
|
func (a *Driver) Diff(id string) (archive.Archive, error) {
|
|
return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
|
Compression: archive.Uncompressed,
|
|
})
|
|
}
|
|
|
|
func (a *Driver) ApplyDiff(id string, diff archive.ArchiveReader) error {
|
|
return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
|
|
}
|
|
|
|
// Returns the size of the contents for the id
|
|
func (a *Driver) DiffSize(id string) (int64, error) {
|
|
return utils.TreeSize(path.Join(a.rootPath(), "diff", id))
|
|
}
|
|
|
|
func (a *Driver) Changes(id string) ([]archive.Change, error) {
|
|
layers, err := a.getParentLayerPaths(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
|
|
}
|
|
|
|
func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
|
|
parentIds, err := getParentIds(a.rootPath(), id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(parentIds) == 0 {
|
|
return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
|
|
}
|
|
layers := make([]string, len(parentIds))
|
|
|
|
// Get the diff paths for all the parent ids
|
|
for i, p := range parentIds {
|
|
layers[i] = path.Join(a.rootPath(), "diff", p)
|
|
}
|
|
return layers, nil
|
|
}
|
|
|
|
func (a *Driver) mount(id string) error {
|
|
// If the id is mounted or we get an error return
|
|
if mounted, err := a.mounted(id); err != nil || mounted {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
target = path.Join(a.rootPath(), "mnt", id)
|
|
rw = path.Join(a.rootPath(), "diff", id)
|
|
)
|
|
|
|
layers, err := a.getParentLayerPaths(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := a.aufsMount(layers, rw, target); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *Driver) unmount(id string) error {
|
|
if mounted, err := a.mounted(id); err != nil || !mounted {
|
|
return err
|
|
}
|
|
target := path.Join(a.rootPath(), "mnt", id)
|
|
return Unmount(target)
|
|
}
|
|
|
|
func (a *Driver) mounted(id string) (bool, error) {
|
|
target := path.Join(a.rootPath(), "mnt", id)
|
|
return mountpk.Mounted(target)
|
|
}
|
|
|
|
// During cleanup aufs needs to unmount all mountpoints
|
|
func (a *Driver) Cleanup() error {
|
|
ids, err := loadIds(path.Join(a.rootPath(), "layers"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, id := range ids {
|
|
if err := a.unmount(id); err != nil {
|
|
utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
|
|
defer func() {
|
|
if err != nil {
|
|
Unmount(target)
|
|
}
|
|
}()
|
|
|
|
if err = a.tryMount(ro, rw, target); err != nil {
|
|
if err = a.mountRw(rw, target); err != nil {
|
|
return
|
|
}
|
|
|
|
for _, layer := range ro {
|
|
branch := fmt.Sprintf("append:%s=ro+wh", layer)
|
|
if err = mount("none", target, "aufs", MsRemount, branch); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Try to mount using the aufs fast path, if this fails then
|
|
// append ro layers.
|
|
func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
|
|
var (
|
|
rwBranch = fmt.Sprintf("%s=rw", rw)
|
|
roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
|
|
)
|
|
return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
|
|
}
|
|
|
|
func (a *Driver) mountRw(rw, target string) error {
|
|
return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
|
|
}
|
|
|
|
func rollbackMount(target string, err error) {
|
|
if err != nil {
|
|
Unmount(target)
|
|
}
|
|
}
|