func (daemon *Daemon) createRootfs(container *Container) error { // Step 1: create the container directory. // This doubles as a barrier to avoid race conditions. rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps) if err != nil { return err } if err := idtools.MkdirAs(container.root, 0700, rootUID, rootGID); err != nil { return err } initID := fmt.Sprintf("%s-init", container.ID) if err := daemon.driver.Create(initID, container.ImageID); err != nil { return err } initPath, err := daemon.driver.Get(initID, "") if err != nil { return err } if err := setupInitLayer(initPath, rootUID, rootGID); err != nil { daemon.driver.Put(initID) return err } // We want to unmount init layer before we take snapshot of it // for the actual container. daemon.driver.Put(initID) if err := daemon.driver.Create(container.ID, initID); err != nil { return err } return nil }
// Get returns the mountpoint for the given id after creating the target directories if necessary. func (d *Driver) Get(id, mountLabel string) (string, error) { mountpoint := d.mountPath(id) filesystem := d.zfsPath(id) options := label.FormatMountLabel("", mountLabel) logrus.Debugf(`[zfs] mount("%s", "%s", "%s")`, filesystem, mountpoint, options) rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return "", err } // Create the target directories if they don't exist if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil { return "", err } if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil { return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err) } // this could be our first mount after creation of the filesystem, and the root dir may still have root // permissions instead of the remapped root uid:gid (if user namespaces are enabled): if err := os.Chown(mountpoint, rootUID, rootGID); err != nil { return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err) } return mountpoint, nil }
// Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent. func (d *Driver) Create(id, parent, mountLabel string) error { dir := d.dir(id) rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return err } if err := idtools.MkdirAllAs(filepath.Dir(dir), 0700, rootUID, rootGID); err != nil { return err } if err := idtools.MkdirAs(dir, 0755, rootUID, rootGID); err != nil { return err } opts := []string{"level:s0"} if _, mountLabel, err := label.InitLabels(opts); err == nil { label.SetFileLabel(dir, mountLabel) } if parent == "" { return nil } parentDir, err := d.Get(parent, "") if err != nil { return fmt.Errorf("%s: %s", parent, err) } if err := chrootarchive.CopyWithTar(parentDir, dir); err != nil { return err } return nil }
// Init returns a new BTRFS driver. // An error is returned if BTRFS is not supported. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { fsMagic, err := graphdriver.GetFSMagic(home) if err != nil { return nil, err } if fsMagic != graphdriver.FsMagicBtrfs { return nil, graphdriver.ErrPrerequisites } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { return nil, err } if err := mount.MakePrivate(home); err != nil { return nil, err } driver := &Driver{ home: home, uidMaps: uidMaps, gidMaps: gidMaps, } return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil }
// Create the filesystem with given id. func (d *Driver) Create(id, parent, mountLabel string) error { subvolumes := path.Join(d.home, "subvolumes") rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return err } if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil { return err } if parent == "" { if err := subvolCreate(subvolumes, id); err != nil { return err } } else { parentDir, err := d.Get(parent, "") if err != nil { return err } if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { return err } } // if we have a remapped root (user namespaces enabled), change the created snapshot // dir ownership to match if rootUID != 0 || rootGID != 0 { if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil { return err } } return label.Relabel(path.Join(subvolumes, id), mountLabel, false) }
// Init returns a new AUFS driver. // An error is returned if AUFS is not supported. func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { // Try to load the aufs kernel module if err := supportsAufs(); err != nil { return nil, graphdriver.ErrNotSupported } fsMagic, err := graphdriver.GetFSMagic(root) if err != nil { return nil, err } if fsName, ok := graphdriver.FsNames[fsMagic]; ok { backingFs = fsName } for _, magic := range incompatibleFsMagic { if fsMagic == magic { return nil, graphdriver.ErrIncompatibleFS } } paths := []string{ "mnt", "diff", "layers", } a := &Driver{ root: root, uidMaps: uidMaps, gidMaps: gidMaps, pathCache: make(map[string]string), } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } // Create the root aufs driver dir and return // if it already exists // If not populate the dir structure if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil { if os.IsExist(err) { return a, nil } return nil, err } if err := mountpk.MakePrivate(root); err != nil { return nil, err } // Populate the dir structure for _, p := range paths { if err := idtools.MkdirAllAs(path.Join(root, p), 0700, rootUID, rootGID); err != nil { return nil, err } } return a, nil }
// Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent. func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error { if len(storageOpt) != 0 { return fmt.Errorf("--storage-opt is not supported for vfs") } dir := d.dir(id) rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return err } if err := idtools.MkdirAllAs(filepath.Dir(dir), 0700, rootUID, rootGID); err != nil { return err } if err := idtools.MkdirAs(dir, 0755, rootUID, rootGID); err != nil { return err } opts := []string{"level:s0"} if _, mountLabel, err := label.InitLabels(opts); err == nil { label.SetFileLabel(dir, mountLabel) } if parent == "" { return nil } parentDir, err := d.Get(parent, "") if err != nil { return fmt.Errorf("%s: %s", parent, err) } if err := CopyWithTar(parentDir, dir); err != nil { return err } return nil }
// CreateDaemonRoot creates the root for the daemon func CreateDaemonRoot(config *Config) error { // get the canonical path to the Docker root directory var realRoot string if _, err := os.Stat(config.Root); err != nil && os.IsNotExist(err) { realRoot = config.Root } else { realRoot, err = fileutils.ReadSymlinkedDirectory(config.Root) if err != nil { return fmt.Errorf("Unable to get the full path to root (%s): %s", config.Root, err) } } uidMaps, gidMaps, err := setupRemappedRoot(config) if err != nil { return err } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return err } if err := setupDaemonRoot(config, realRoot, rootUID, rootGID); err != nil { return err } return nil }
// NewGraph instantiates a new graph at the given root path in the filesystem. // `root` will be created if it doesn't exist. func NewGraph(root string, driver graphdriver.Driver, uidMaps, gidMaps []idtools.IDMap) (*Graph, error) { abspath, err := filepath.Abs(root) if err != nil { return nil, err } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } // Create the root directory if it doesn't exists if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { return nil, err } graph := &Graph{ root: abspath, idIndex: truncindex.NewTruncIndex([]string{}), driver: driver, retained: &retainedLayers{layerHolders: make(map[string]map[string]struct{})}, uidMaps: uidMaps, gidMaps: gidMaps, parentRefs: make(map[string]int), } // Windows does not currently support tarsplit functionality. if runtime.GOOS == "windows" { graph.tarSplitDisabled = true } if err := graph.restore(); err != nil { return nil, err } return graph, nil }
// Init returns a new BTRFS driver. // An error is returned if BTRFS is not supported. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { rootdir := path.Dir(home) var buf syscall.Statfs_t if err := syscall.Statfs(rootdir, &buf); err != nil { return nil, err } if graphdriver.FsMagic(buf.Type) != graphdriver.FsMagicBtrfs { return nil, graphdriver.ErrPrerequisites } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { return nil, err } if err := mount.MakePrivate(home); err != nil { return nil, err } driver := &Driver{ home: home, uidMaps: uidMaps, gidMaps: gidMaps, } return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil }
// Create the filesystem with given id. func (d *Driver) Create(id, parent, mountLabel string) error { subvolumes := path.Join(d.home, "subvolumes") rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return err } if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil { return err } if parent == "" { if err := subvolCreate(subvolumes, id); err != nil { return err } } else { parentDir, err := d.Get(parent, "") if err != nil { return err } if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { return err } } return label.Relabel(path.Join(subvolumes, id), mountLabel, false) }
// Init returns the a native diff driver for overlay filesystem. // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. // If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { opts, err := parseOptions(options) if err != nil { return nil, err } if err := supportsOverlay(); err != nil { return nil, graphdriver.ErrNotSupported } // require kernel 4.0.0 to ensure multiple lower dirs are supported v, err := kernel.GetKernelVersion() if err != nil { return nil, err } if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 { if !opts.overrideKernelCheck { return nil, graphdriver.ErrNotSupported } logrus.Warnf("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update") } fsMagic, err := graphdriver.GetFSMagic(home) if err != nil { return nil, err } if fsName, ok := graphdriver.FsNames[fsMagic]; ok { backingFs = fsName } // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs switch fsMagic { case graphdriver.FsMagicBtrfs, graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: logrus.Errorf("'overlay2' is not supported over %s", backingFs) return nil, graphdriver.ErrIncompatibleFS } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } // Create the driver home dir if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { return nil, err } if err := mount.MakePrivate(home); err != nil { return nil, err } d := &Driver{ home: home, uidMaps: uidMaps, gidMaps: gidMaps, ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), } return d, nil }
// Create the filesystem with given id. func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { subvolumes := path.Join(d.home, "subvolumes") rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return err } if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil { return err } if parent == "" { if err := subvolCreate(subvolumes, id); err != nil { return err } } else { parentDir := d.subvolumesDirID(parent) st, err := os.Stat(parentDir) if err != nil { return err } if !st.IsDir() { return fmt.Errorf("%s: not a directory", parentDir) } if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { return err } } var storageOpt map[string]string if opts != nil { storageOpt = opts.StorageOpt } if _, ok := storageOpt["size"]; ok { driver := &Driver{} if err := d.parseStorageOpt(storageOpt, driver); err != nil { return err } if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil { return err } } // if we have a remapped root (user namespaces enabled), change the created snapshot // dir ownership to match if rootUID != 0 || rootGID != 0 { if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil { return err } } mountLabel := "" if opts != nil { mountLabel = opts.MountLabel } return label.Relabel(path.Join(subvolumes, id), mountLabel, false) }
// Get creates and mounts the required file system for the given id and returns the mount path. func (d *Driver) Get(id string, mountLabel string) (string, error) { // Protect the d.active from concurrent access d.Lock() defer d.Unlock() mount := d.active[id] if mount != nil { mount.count++ return mount.path, nil } mount = &ActiveMount{count: 1} dir := d.dir(id) if _, err := os.Stat(dir); err != nil { return "", err } // If id has a root, just return it rootDir := path.Join(dir, "root") if _, err := os.Stat(rootDir); err == nil { mount.path = rootDir d.active[id] = mount return mount.path, nil } lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) if err != nil { return "", err } lowerDir := path.Join(d.dir(string(lowerID)), "root") upperDir := path.Join(dir, "upper") workDir := path.Join(dir, "work") mergedDir := path.Join(dir, "merged") opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) } // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a // user namespace requires this to move a directory from lower to upper. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return "", err } if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { return "", err } mount.path = mergedDir mount.mounted = true d.active[id] = mount return mount.path, nil }
// Get creates and mounts the required file system for the given id and returns the mount path. func (d *Driver) Get(id string, mountLabel string) (string, error) { dir := d.dir(id) if _, err := os.Stat(dir); err != nil { return "", err } // If id has a root, just return it rootDir := path.Join(dir, "root") if _, err := os.Stat(rootDir); err == nil { d.pathCacheLock.Lock() d.pathCache[id] = rootDir d.pathCacheLock.Unlock() return rootDir, nil } lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) if err != nil { return "", err } lowerDir := path.Join(d.dir(string(lowerID)), "root") upperDir := path.Join(dir, "upper") workDir := path.Join(dir, "work") mergedDir := path.Join(dir, "merged") opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) // if it's mounted already, just return mounted, err := d.mounted(mergedDir) if err != nil { return "", err } if mounted { return mergedDir, nil } if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) } // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a // user namespace requires this to move a directory from lower to upper. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return "", err } if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { return "", err } d.pathCacheLock.Lock() d.pathCache[id] = mergedDir d.pathCacheLock.Unlock() return mergedDir, nil }
// mktemp creates a temporary sub-directory inside the graph's filesystem. func (graph *Graph) mktemp() (string, error) { dir := filepath.Join(graph.root, "_tmp", stringid.GenerateNonCryptoID()) rootUID, rootGID, err := idtools.GetRootUIDGID(graph.uidMaps, graph.gidMaps) if err != nil { return "", err } if err := idtools.MkdirAllAs(dir, 0700, rootUID, rootGID); err != nil { return "", err } return dir, nil }
// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. // If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { if err := supportsOverlay(); err != nil { return nil, graphdriver.ErrNotSupported } fsMagic, err := graphdriver.GetFSMagic(home) if err != nil { return nil, err } if fsName, ok := graphdriver.FsNames[fsMagic]; ok { backingFs = fsName } // check if they are running over btrfs, aufs, zfs or overlay switch fsMagic { case graphdriver.FsMagicBtrfs: logrus.Error("'overlay' is not supported over btrfs.") return nil, graphdriver.ErrIncompatibleFS case graphdriver.FsMagicAufs: logrus.Error("'overlay' is not supported over aufs.") return nil, graphdriver.ErrIncompatibleFS case graphdriver.FsMagicZfs: logrus.Error("'overlay' is not supported over zfs.") return nil, graphdriver.ErrIncompatibleFS case graphdriver.FsMagicOverlay: logrus.Error("'overlay' is not supported over overlay.") return nil, graphdriver.ErrIncompatibleFS } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } // Create the driver home dir if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { return nil, err } if err := mount.MakePrivate(home); err != nil { return nil, err } d := &Driver{ home: home, pathCache: make(map[string]string), uidMaps: uidMaps, gidMaps: gidMaps, ctr: graphdriver.NewRefCounter(), } return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil }
// Get creates and mounts the required file system for the given id and returns the mount path. func (d *Driver) Get(id string, mountLabel string) (s string, err error) { dir := d.dir(id) if _, err := os.Stat(dir); err != nil { return "", err } diffDir := path.Join(dir, "diff") lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile)) if err != nil { // If no lower, just return diff directory if os.IsNotExist(err) { return diffDir, nil } return "", err } mergedDir := path.Join(dir, "merged") if count := d.ctr.Increment(mergedDir); count > 1 { return mergedDir, nil } defer func() { if err != nil { if c := d.ctr.Decrement(mergedDir); c <= 0 { syscall.Unmount(mergedDir, 0) } } }() workDir := path.Join(dir, "work") opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work")) mountLabel = label.FormatMountLabel(opts, mountLabel) if len(mountLabel) > syscall.Getpagesize() { return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountLabel)) } if err := mountFrom(d.home, "overlay", path.Join(id, "merged"), "overlay", mountLabel); err != nil { return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) } // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a // user namespace requires this to move a directory from lower to upper. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return "", err } if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { return "", err } return mergedDir, nil }
// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem. // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error. // If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { if err := supportsOverlay(); err != nil { return nil, graphdriver.ErrNotSupported } fsMagic, err := graphdriver.GetFSMagic(home) if err != nil { return nil, err } if fsName, ok := graphdriver.FsNames[fsMagic]; ok { backingFs = fsName } switch fsMagic { case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs, graphdriver.FsMagicEcryptfs: logrus.Errorf("'overlay' is not supported over %s", backingFs) return nil, graphdriver.ErrIncompatibleFS } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } // Create the driver home dir if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { return nil, err } if err := mount.MakePrivate(home); err != nil { return nil, err } supportsDType, err := fsutils.SupportsDType(home) if err != nil { return nil, err } if !supportsDType { // not a fatal error until v1.16 (#27443) logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs)) } d := &Driver{ home: home, uidMaps: uidMaps, gidMaps: gidMaps, ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), supportsDType: supportsDType, } return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil }
// Init returns a new VFS driver. // This sets the home directory for the driver and returns NaiveDiffDriver. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { d := &Driver{ home: home, uidMaps: uidMaps, gidMaps: gidMaps, } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { return nil, err } return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil }
// Get mounts a device with given id into the root filesystem func (d *Driver) Get(id, mountLabel string) (string, error) { mp := path.Join(d.home, "mnt", id) rootFs := path.Join(mp, "rootfs") if count := d.ctr.Increment(mp); count > 1 { return rootFs, nil } uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { d.ctr.Decrement(mp) return "", err } // Create the target directories if they don't exist if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) { d.ctr.Decrement(mp) return "", err } if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) { d.ctr.Decrement(mp) return "", err } // Mount the device if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil { d.ctr.Decrement(mp) return "", err } if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) { d.ctr.Decrement(mp) d.DeviceSet.UnmountDevice(id, mp) return "", err } idFile := path.Join(mp, "id") if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) { // Create an "id" file with the container/image id in it to help reconstruct this in case // of later problems if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil { d.ctr.Decrement(mp) d.DeviceSet.UnmountDevice(id, mp) return "", err } } return rootFs, nil }
func (a *Driver) migrateContainers(pth string, setupInit func(p string, rootUID, rootGID int) error) error { fis, err := ioutil.ReadDir(pth) if err != nil { return err } rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) if err != nil { return err } for _, fi := range fis { if id := fi.Name(); fi.IsDir() && pathExists(path.Join(pth, id, "rw")) { if err := tryRelocate(path.Join(pth, id, "rw"), path.Join(a.rootPath(), "diff", id)); err != nil { return err } if !a.Exists(id) { metadata, err := loadMetadata(path.Join(pth, id, "config.json")) if err != nil { return err } initID := fmt.Sprintf("%s-init", id) if err := a.Create(initID, metadata.Image, ""); err != nil { return err } initPath, err := a.Get(initID, "") if err != nil { return err } // setup init layer if err := setupInit(initPath, rootUID, rootGID); err != nil { return err } if err := a.Create(id, initID, ""); err != nil { return err } } } } return nil }
func (a *Driver) createDirsFor(id string) error { paths := []string{ "mnt", "diff", } rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) if err != nil { return err } for _, p := range paths { if err := idtools.MkdirAllAs(path.Join(a.rootPath(), p, id), 0755, rootUID, rootGID); err != nil { return err } } return nil }
// Get creates and mounts the required file system for the given id and returns the mount path. func (d *Driver) Get(id string, mountLabel string) (s string, err error) { dir := d.dir(id) if _, err := os.Stat(dir); err != nil { return "", err } // If id has a root, just return it rootDir := path.Join(dir, "root") if _, err := os.Stat(rootDir); err == nil { return rootDir, nil } mergedDir := path.Join(dir, "merged") if count := d.ctr.Increment(mergedDir); count > 1 { return mergedDir, nil } defer func() { if err != nil { if c := d.ctr.Decrement(mergedDir); c <= 0 { syscall.Unmount(mergedDir, 0) } } }() lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) if err != nil { return "", err } var ( lowerDir = path.Join(d.dir(string(lowerID)), "root") upperDir = path.Join(dir, "upper") workDir = path.Join(dir, "work") opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir) ) if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil { return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) } // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a // user namespace requires this to move a directory from lower to upper. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return "", err } if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { return "", err } return mergedDir, nil }
// Init returns a new BTRFS driver. // An error is returned if BTRFS is not supported. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { fsMagic, err := graphdriver.GetFSMagic(home) if err != nil { return nil, err } if fsMagic != graphdriver.FsMagicBtrfs { return nil, graphdriver.ErrPrerequisites } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { return nil, err } if err := mount.MakePrivate(home); err != nil { return nil, err } opt, err := parseOptions(options) if err != nil { return nil, err } if userDiskQuota { if err := subvolEnableQuota(home); err != nil { return nil, err } quotaEnabled = true } driver := &Driver{ home: home, uidMaps: uidMaps, gidMaps: gidMaps, options: opt, } return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil }
// Create the filesystem with given id. func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error { if len(storageOpt) != 0 { return fmt.Errorf("--storage-opt is not supported for btrfs") } subvolumes := path.Join(d.home, "subvolumes") rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return err } if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil { return err } if parent == "" { if err := subvolCreate(subvolumes, id); err != nil { return err } } else { parentDir := d.subvolumesDirID(parent) st, err := os.Stat(parentDir) if err != nil { return err } if !st.IsDir() { return fmt.Errorf("%s: not a directory", parentDir) } if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { return err } } // if we have a remapped root (user namespaces enabled), change the created snapshot // dir ownership to match if rootUID != 0 || rootGID != 0 { if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil { return err } } return label.Relabel(path.Join(subvolumes, id), mountLabel, false) }
// createDirsFor creates two directories for the given id. // mnt and diff func (a *Driver) createDirsFor(id string) error { paths := []string{ "mnt", "diff", } rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) if err != nil { return err } // Directory permission is 0755. // The path of directories are <aufs_root_path>/mnt/<image_id> // and <aufs_root_path>/diff/<image_id> for _, p := range paths { if err := idtools.MkdirAllAs(path.Join(a.rootPath(), p, id), 0755, rootUID, rootGID); err != nil { return err } } return nil }
// Get returns the mountpoint for the given id after creating the target directories if necessary. func (d *Driver) Get(id, mountLabel string) (string, error) { mountpoint := d.mountPath(id) filesystem := d.zfsPath(id) options := label.FormatMountLabel("", mountLabel) logrus.Debugf(`[zfs] mount("%s", "%s", "%s")`, filesystem, mountpoint, options) rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return "", err } // Create the target directories if they don't exist if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil { return "", err } err = mount.Mount(filesystem, mountpoint, "zfs", options) if err != nil { return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err) } return mountpoint, nil }
// Handler for teasing out the automatic decompression func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool) error { if tarArchive == nil { return fmt.Errorf("Empty archive") } if options == nil { options = &archive.TarOptions{} } if options.ExcludePatterns == nil { options.ExcludePatterns = []string{} } rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) if err != nil { return err } dest = filepath.Clean(dest) if _, err := os.Stat(dest); os.IsNotExist(err) { if err := idtools.MkdirAllNewAs(dest, 0755, rootUID, rootGID); err != nil { return err } } r := ioutil.NopCloser(tarArchive) if decompress { decompressedArchive, err := archive.DecompressStream(tarArchive) if err != nil { return err } defer decompressedArchive.Close() r = decompressedArchive } return invokeUnpack(r, dest, options) }
// CopyWithTar creates a tar archive of filesystem path `src`, and // unpacks it at filesystem path `dst`. // The archive is streamed directly with fixed buffering and no // intermediary disk IO. func (archiver *Archiver) CopyWithTar(src, dst string) error { srcSt, err := os.Stat(src) if err != nil { return err } if !srcSt.IsDir() { return archiver.CopyFileWithTar(src, dst) } // if this archiver is set up with ID mapping we need to create // the new destination directory with the remapped root UID/GID pair // as owner rootUID, rootGID, err := idtools.GetRootUIDGID(archiver.UIDMaps, archiver.GIDMaps) if err != nil { return err } // Create dst, copy src's content into it logrus.Debugf("Creating dest directory: %s", dst) if err := idtools.MkdirAllNewAs(dst, 0755, rootUID, rootGID); err != nil { return err } logrus.Debugf("Calling TarUntar(%s, %s)", src, dst) return archiver.TarUntar(src, dst) }