mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #33362 from dnephin/refactor-uid-maps
Partial refactor of UID/GID usage to a new struct
This commit is contained in:
commit
34536c498d
47 changed files with 466 additions and 656 deletions
|
@ -216,7 +216,7 @@ func (container *Container) WriteHostConfig() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
|
// SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
|
||||||
func (container *Container) SetupWorkingDirectory(rootUID, rootGID int) error {
|
func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error {
|
||||||
if container.Config.WorkingDir == "" {
|
if container.Config.WorkingDir == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ func (container *Container) SetupWorkingDirectory(rootUID, rootGID int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := idtools.MkdirAllNewAs(pth, 0755, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIDs); err != nil {
|
||||||
pthInfo, err2 := os.Stat(pth)
|
pthInfo, err2 := os.Stat(pth)
|
||||||
if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
|
if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
|
||||||
return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
|
return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
|
||||||
|
|
|
@ -425,7 +425,7 @@ func copyExistingContents(source, destination string) error {
|
||||||
}
|
}
|
||||||
if len(srcList) == 0 {
|
if len(srcList) == 0 {
|
||||||
// If the source volume is empty, copies files from the root into the volume
|
// If the source volume is empty, copies files from the root into the volume
|
||||||
if err := chrootarchive.CopyWithTar(source, destination); err != nil {
|
if err := chrootarchive.NewArchiver(nil).CopyWithTar(source, destination); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -375,7 +375,7 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
|
||||||
|
|
||||||
destExists := true
|
destExists := true
|
||||||
destDir := false
|
destDir := false
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
|
|
||||||
// Work in daemon-local OS specific file paths
|
// Work in daemon-local OS specific file paths
|
||||||
destPath = filepath.FromSlash(destPath)
|
destPath = filepath.FromSlash(destPath)
|
||||||
|
@ -413,13 +413,7 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
|
||||||
destExists = false
|
destExists = false
|
||||||
}
|
}
|
||||||
|
|
||||||
uidMaps, gidMaps := daemon.GetUIDGIDMaps()
|
archiver := chrootarchive.NewArchiver(daemon.idMappings)
|
||||||
archiver := &archive.Archiver{
|
|
||||||
Untar: chrootarchive.Untar,
|
|
||||||
UIDMaps: uidMaps,
|
|
||||||
GIDMaps: gidMaps,
|
|
||||||
}
|
|
||||||
|
|
||||||
src, err := os.Stat(fullSrcPath)
|
src, err := os.Stat(fullSrcPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -430,7 +424,7 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
|
||||||
if err := archiver.CopyWithTar(fullSrcPath, destPath); err != nil {
|
if err := archiver.CopyWithTar(fullSrcPath, destPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fixPermissions(fullSrcPath, destPath, rootUID, rootGID, destExists)
|
return fixPermissions(fullSrcPath, destPath, rootIDs.UID, rootIDs.GID, destExists)
|
||||||
}
|
}
|
||||||
if decompress && archive.IsArchivePath(fullSrcPath) {
|
if decompress && archive.IsArchivePath(fullSrcPath) {
|
||||||
// Only try to untar if it is a file and that we've been told to decompress (when ADD-ing a remote file)
|
// Only try to untar if it is a file and that we've been told to decompress (when ADD-ing a remote file)
|
||||||
|
@ -459,12 +453,12 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
|
||||||
destPath = filepath.Join(destPath, filepath.Base(srcPath))
|
destPath = filepath.Join(destPath, filepath.Base(srcPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := idtools.MkdirAllNewAs(filepath.Dir(destPath), 0755, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChownNew(filepath.Dir(destPath), 0755, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := archiver.CopyFileWithTar(fullSrcPath, destPath); err != nil {
|
if err := archiver.CopyFileWithTar(fullSrcPath, destPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return fixPermissions(fullSrcPath, destPath, rootUID, rootGID, destExists)
|
return fixPermissions(fullSrcPath, destPath, rootIDs.UID, rootIDs.GID, destExists)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,9 @@ import (
|
||||||
// defaultTarCopyOptions is the setting that is used when unpacking an archive
|
// defaultTarCopyOptions is the setting that is used when unpacking an archive
|
||||||
// for a copy API event.
|
// for a copy API event.
|
||||||
func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
|
func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
|
||||||
uidMaps, gidMaps := daemon.GetUIDGIDMaps()
|
|
||||||
return &archive.TarOptions{
|
return &archive.TarOptions{
|
||||||
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
||||||
UIDMaps: uidMaps,
|
UIDMaps: daemon.idMappings.UIDs(),
|
||||||
GIDMaps: gidMaps,
|
GIDMaps: daemon.idMappings.GIDs(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,6 @@ func (daemon *Daemon) tarCopyOptions(container *container.Container, noOverwrite
|
||||||
|
|
||||||
return &archive.TarOptions{
|
return &archive.TarOptions{
|
||||||
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
||||||
ChownOpts: &archive.TarChownOptions{
|
ChownOpts: &idtools.IDPair{UID: user.Uid, GID: user.Gid},
|
||||||
UID: user.Uid,
|
|
||||||
GID: user.Gid,
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,14 +109,14 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
|
||||||
}
|
}
|
||||||
c.ShmPath = "/dev/shm"
|
c.ShmPath = "/dev/shm"
|
||||||
} else {
|
} else {
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
if !c.HasMountFor("/dev/shm") {
|
if !c.HasMountFor("/dev/shm") {
|
||||||
shmPath, err := c.ShmResourcePath()
|
shmPath, err := c.ShmResourcePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := idtools.MkdirAllAs(shmPath, 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(shmPath, 0700, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
|
||||||
if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil {
|
if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil {
|
||||||
return fmt.Errorf("mounting shm tmpfs: %s", err)
|
return fmt.Errorf("mounting shm tmpfs: %s", err)
|
||||||
}
|
}
|
||||||
if err := os.Chown(shmPath, rootUID, rootGID); err != nil {
|
if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,9 +147,9 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
|
||||||
logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
|
logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
|
||||||
|
|
||||||
// retrieve possible remapped range start for root UID, GID
|
// retrieve possible remapped range start for root UID, GID
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
// create tmpfs
|
// create tmpfs
|
||||||
if err := idtools.MkdirAllAs(localMountPath, 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(localMountPath, 0700, rootIDs); err != nil {
|
||||||
return errors.Wrap(err, "error creating secret local mount path")
|
return errors.Wrap(err, "error creating secret local mount path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootUID, rootGID)
|
tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
|
||||||
if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
|
if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
|
||||||
return errors.Wrap(err, "unable to setup secret mount")
|
return errors.Wrap(err, "unable to setup secret mount")
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
|
||||||
// secrets are created in the SecretMountPath on the host, at a
|
// secrets are created in the SecretMountPath on the host, at a
|
||||||
// single level
|
// single level
|
||||||
fPath := c.SecretFilePath(*s)
|
fPath := c.SecretFilePath(*s)
|
||||||
if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil {
|
||||||
return errors.Wrap(err, "error creating secret mount path")
|
return errors.Wrap(err, "error creating secret mount path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Chown(fPath, rootUID+uid, rootGID+gid); err != nil {
|
if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
|
||||||
return errors.Wrap(err, "error setting ownership for secret")
|
return errors.Wrap(err, "error setting ownership for secret")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,9 +232,9 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
|
||||||
logrus.Debugf("configs: setting up config dir: %s", localPath)
|
logrus.Debugf("configs: setting up config dir: %s", localPath)
|
||||||
|
|
||||||
// retrieve possible remapped range start for root UID, GID
|
// retrieve possible remapped range start for root UID, GID
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
// create tmpfs
|
// create tmpfs
|
||||||
if err := idtools.MkdirAllAs(localPath, 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(localPath, 0700, rootIDs); err != nil {
|
||||||
return errors.Wrap(err, "error creating config dir")
|
return errors.Wrap(err, "error creating config dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +261,7 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
|
||||||
|
|
||||||
log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath})
|
log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath})
|
||||||
|
|
||||||
if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil {
|
||||||
return errors.Wrap(err, "error creating config path")
|
return errors.Wrap(err, "error creating config path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Chown(fPath, rootUID+uid, rootGID+gid); err != nil {
|
if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
|
||||||
return errors.Wrap(err, "error setting ownership for config")
|
return errors.Wrap(err, "error setting ownership for config")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,14 +117,11 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
if err != nil {
|
if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAs(container.Root, 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := idtools.MkdirAs(container.CheckpointDir(), 0700, rootUID, rootGID); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
|
||||||
}
|
}
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
if err := container.SetupWorkingDirectory(rootUID, rootGID); err != nil {
|
if err := container.SetupWorkingDirectory(rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,8 +94,7 @@ type Daemon struct {
|
||||||
seccompEnabled bool
|
seccompEnabled bool
|
||||||
apparmorEnabled bool
|
apparmorEnabled bool
|
||||||
shutdown bool
|
shutdown bool
|
||||||
uidMaps []idtools.IDMap
|
idMappings *idtools.IDMappings
|
||||||
gidMaps []idtools.IDMap
|
|
||||||
layerStore layer.Store
|
layerStore layer.Store
|
||||||
imageStore image.Store
|
imageStore image.Store
|
||||||
PluginStore *plugin.Store // todo: remove
|
PluginStore *plugin.Store // todo: remove
|
||||||
|
@ -524,21 +523,17 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
uidMaps, gidMaps, err := setupRemappedRoot(config)
|
idMappings, err := setupRemappedRoot(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
rootIDs := idMappings.RootPair()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := setupDaemonProcess(config); err != nil {
|
if err := setupDaemonProcess(config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up the tmpDir to use a canonical path
|
// set up the tmpDir to use a canonical path
|
||||||
tmp, err := prepareTempDir(config.Root, rootUID, rootGID)
|
tmp, err := prepareTempDir(config.Root, rootIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err)
|
return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err)
|
||||||
}
|
}
|
||||||
|
@ -587,7 +582,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
|
||||||
}
|
}
|
||||||
|
|
||||||
daemonRepo := filepath.Join(config.Root, "containers")
|
daemonRepo := filepath.Join(config.Root, "containers")
|
||||||
if err := idtools.MkdirAllAs(daemonRepo, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
|
if err := idtools.MkdirAllAndChown(daemonRepo, 0700, rootIDs); err != nil && !os.IsExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,8 +627,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
|
||||||
MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
|
MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
|
||||||
GraphDriver: driverName,
|
GraphDriver: driverName,
|
||||||
GraphDriverOptions: config.GraphOptions,
|
GraphDriverOptions: config.GraphOptions,
|
||||||
UIDMaps: uidMaps,
|
IDMappings: idMappings,
|
||||||
GIDMaps: gidMaps,
|
|
||||||
PluginGetter: d.PluginStore,
|
PluginGetter: d.PluginStore,
|
||||||
ExperimentalEnabled: config.Experimental,
|
ExperimentalEnabled: config.Experimental,
|
||||||
})
|
})
|
||||||
|
@ -665,7 +659,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the volumes driver
|
// Configure the volumes driver
|
||||||
volStore, err := d.configureVolumes(rootUID, rootGID)
|
volStore, err := d.configureVolumes(rootIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -728,8 +722,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
|
||||||
d.EventsService = eventsService
|
d.EventsService = eventsService
|
||||||
d.volumes = volStore
|
d.volumes = volStore
|
||||||
d.root = config.Root
|
d.root = config.Root
|
||||||
d.uidMaps = uidMaps
|
d.idMappings = idMappings
|
||||||
d.gidMaps = gidMaps
|
|
||||||
d.seccompEnabled = sysInfo.Seccomp
|
d.seccompEnabled = sysInfo.Seccomp
|
||||||
d.apparmorEnabled = sysInfo.AppArmor
|
d.apparmorEnabled = sysInfo.AppArmor
|
||||||
|
|
||||||
|
@ -970,25 +963,10 @@ func (daemon *Daemon) GraphDriverName() string {
|
||||||
return daemon.layerStore.DriverName()
|
return daemon.layerStore.DriverName()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUIDGIDMaps returns the current daemon's user namespace settings
|
|
||||||
// for the full uid and gid maps which will be applied to containers
|
|
||||||
// started in this instance.
|
|
||||||
func (daemon *Daemon) GetUIDGIDMaps() ([]idtools.IDMap, []idtools.IDMap) {
|
|
||||||
return daemon.uidMaps, daemon.gidMaps
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRemappedUIDGID returns the current daemon's uid and gid values
|
|
||||||
// if user namespaces are in use for this daemon instance. If not
|
|
||||||
// this function will return "real" root values of 0, 0.
|
|
||||||
func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
|
|
||||||
uid, gid, _ := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
|
|
||||||
return uid, gid
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepareTempDir prepares and returns the default directory to use
|
// prepareTempDir prepares and returns the default directory to use
|
||||||
// for temporary files.
|
// for temporary files.
|
||||||
// If it doesn't exist, it is created. If it exists, its content is removed.
|
// If it doesn't exist, it is created. If it exists, its content is removed.
|
||||||
func prepareTempDir(rootDir string, rootUID, rootGID int) (string, error) {
|
func prepareTempDir(rootDir string, rootIDs idtools.IDPair) (string, error) {
|
||||||
var tmpDir string
|
var tmpDir string
|
||||||
if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" {
|
if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" {
|
||||||
tmpDir = filepath.Join(rootDir, "tmp")
|
tmpDir = filepath.Join(rootDir, "tmp")
|
||||||
|
@ -1008,12 +986,12 @@ func prepareTempDir(rootDir string, rootUID, rootGID int) (string, error) {
|
||||||
}
|
}
|
||||||
// We don't remove the content of tmpdir if it's not the default,
|
// We don't remove the content of tmpdir if it's not the default,
|
||||||
// it may hold things that do not belong to us.
|
// it may hold things that do not belong to us.
|
||||||
return tmpDir, idtools.MkdirAllAs(tmpDir, 0700, rootUID, rootGID)
|
return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) setupInitLayer(initPath string) error {
|
func (daemon *Daemon) setupInitLayer(initPath string) error {
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
return initlayer.Setup(initPath, rootUID, rootGID)
|
return initlayer.Setup(initPath, rootIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setDefaultMtu(conf *config.Config) {
|
func setDefaultMtu(conf *config.Config) {
|
||||||
|
@ -1024,8 +1002,8 @@ func setDefaultMtu(conf *config.Config) {
|
||||||
conf.Mtu = config.DefaultNetworkMtu
|
conf.Mtu = config.DefaultNetworkMtu
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) configureVolumes(rootUID, rootGID int) (*store.VolumeStore, error) {
|
func (daemon *Daemon) configureVolumes(rootIDs idtools.IDPair) (*store.VolumeStore, error) {
|
||||||
volumesDriver, err := local.New(daemon.configStore.Root, rootUID, rootGID)
|
volumesDriver, err := local.New(daemon.configStore.Root, rootIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1171,18 +1149,9 @@ func CreateDaemonRoot(config *config.Config) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uidMaps, gidMaps, err := setupRemappedRoot(config)
|
idMappings, err := setupRemappedRoot(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
return setupDaemonRoot(config, realRoot, idMappings.RootPair())
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := setupDaemonRoot(config, realRoot, rootUID, rootGID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
_ "github.com/docker/docker/pkg/discovery/memory"
|
_ "github.com/docker/docker/pkg/discovery/memory"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/registrar"
|
"github.com/docker/docker/pkg/registrar"
|
||||||
"github.com/docker/docker/pkg/truncindex"
|
"github.com/docker/docker/pkg/truncindex"
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
|
@ -127,7 +128,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
volumesDriver, err := local.New(tmp, 0, 0)
|
volumesDriver, err := local.New(tmp, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1026,40 +1026,38 @@ func parseRemappedRoot(usergrp string) (string, string, error) {
|
||||||
return username, groupname, nil
|
return username, groupname, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRemappedRoot(config *config.Config) ([]idtools.IDMap, []idtools.IDMap, error) {
|
func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
|
||||||
if runtime.GOOS != "linux" && config.RemappedRoot != "" {
|
if runtime.GOOS != "linux" && config.RemappedRoot != "" {
|
||||||
return nil, nil, fmt.Errorf("User namespaces are only supported on Linux")
|
return nil, fmt.Errorf("User namespaces are only supported on Linux")
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the daemon was started with remapped root option, parse
|
// if the daemon was started with remapped root option, parse
|
||||||
// the config option to the int uid,gid values
|
// the config option to the int uid,gid values
|
||||||
var (
|
|
||||||
uidMaps, gidMaps []idtools.IDMap
|
|
||||||
)
|
|
||||||
if config.RemappedRoot != "" {
|
if config.RemappedRoot != "" {
|
||||||
username, groupname, err := parseRemappedRoot(config.RemappedRoot)
|
username, groupname, err := parseRemappedRoot(config.RemappedRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if username == "root" {
|
if username == "root" {
|
||||||
// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
|
// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
|
||||||
// effectively
|
// effectively
|
||||||
logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
|
logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
|
||||||
return uidMaps, gidMaps, nil
|
return &idtools.IDMappings{}, nil
|
||||||
}
|
}
|
||||||
logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname)
|
logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname)
|
||||||
// update remapped root setting now that we have resolved them to actual names
|
// update remapped root setting now that we have resolved them to actual names
|
||||||
config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
|
config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
|
||||||
|
|
||||||
uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname)
|
mappings, err := idtools.NewIDMappings(username, groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Can't create ID mappings: %v", err)
|
return nil, errors.Wrapf(err, "Can't create ID mappings: %v")
|
||||||
}
|
}
|
||||||
|
return mappings, nil
|
||||||
}
|
}
|
||||||
return uidMaps, gidMaps, nil
|
return &idtools.IDMappings{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int) error {
|
func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
|
||||||
config.Root = rootDir
|
config.Root = rootDir
|
||||||
// the docker root metadata directory needs to have execute permissions for all users (g+x,o+x)
|
// the docker root metadata directory needs to have execute permissions for all users (g+x,o+x)
|
||||||
// so that syscalls executing as non-root, operating on subdirectories of the graph root
|
// so that syscalls executing as non-root, operating on subdirectories of the graph root
|
||||||
|
@ -1084,10 +1082,10 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int
|
||||||
// a new subdirectory with ownership set to the remapped uid/gid (so as to allow
|
// a new subdirectory with ownership set to the remapped uid/gid (so as to allow
|
||||||
// `chdir()` to work for containers namespaced to that uid/gid)
|
// `chdir()` to work for containers namespaced to that uid/gid)
|
||||||
if config.RemappedRoot != "" {
|
if config.RemappedRoot != "" {
|
||||||
config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootUID, rootGID))
|
config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIDs.UID, rootIDs.GID))
|
||||||
logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
|
logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
|
||||||
// Create the root directory if it doesn't exist
|
// Create the root directory if it doesn't exist
|
||||||
if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIDs); err != nil {
|
||||||
return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
|
return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
|
||||||
}
|
}
|
||||||
// we also need to verify that any pre-existing directories in the path to
|
// we also need to verify that any pre-existing directories in the path to
|
||||||
|
@ -1100,7 +1098,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int
|
||||||
if dirPath == "/" {
|
if dirPath == "/" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !idtools.CanAccess(dirPath, rootUID, rootGID) {
|
if !idtools.CanAccess(dirPath, rootIDs) {
|
||||||
return fmt.Errorf("A subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories.", config.Root)
|
return fmt.Errorf("A subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories.", config.Root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/docker/docker/daemon/config"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
"github.com/docker/docker/volume/drivers"
|
"github.com/docker/docker/volume/drivers"
|
||||||
"github.com/docker/docker/volume/local"
|
"github.com/docker/docker/volume/local"
|
||||||
|
@ -277,7 +278,7 @@ func TestMigratePre17Volumes(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
drv, err := local.New(volumeRoot, 0, 0)
|
drv, err := local.New(volumeRoot, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,11 +454,11 @@ func (daemon *Daemon) cleanupMounts() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRemappedRoot(config *config.Config) ([]idtools.IDMap, []idtools.IDMap, error) {
|
func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
|
||||||
return nil, nil, nil
|
return &idtools.IDMappings{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDaemonRoot(config *config.Config, rootDir string, rootUID, rootGID int) error {
|
func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
|
||||||
config.Root = rootDir
|
config.Root = rootDir
|
||||||
// Create the root directory if it doesn't exists
|
// Create the root directory if it doesn't exists
|
||||||
if err := system.MkdirAllWithACL(config.Root, 0); err != nil && !os.IsExist(err) {
|
if err := system.MkdirAllWithACL(config.Root, 0); err != nil && !os.IsExist(err) {
|
||||||
|
|
|
@ -40,11 +40,10 @@ func (daemon *Daemon) containerExport(container *container.Container) (io.ReadCl
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
uidMaps, gidMaps := daemon.GetUIDGIDMaps()
|
|
||||||
archive, err := archive.TarWithOptions(container.BaseFS, &archive.TarOptions{
|
archive, err := archive.TarWithOptions(container.BaseFS, &archive.TarOptions{
|
||||||
Compression: archive.Uncompressed,
|
Compression: archive.Uncompressed,
|
||||||
UIDMaps: uidMaps,
|
UIDMaps: daemon.idMappings.UIDs(),
|
||||||
GIDMaps: gidMaps,
|
GIDMaps: daemon.idMappings.GIDs(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
|
|
|
@ -4,6 +4,8 @@ package devmapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -17,11 +19,51 @@ func init() {
|
||||||
defaultMetaDataLoopbackSize = 200 * 1024 * 1024
|
defaultMetaDataLoopbackSize = 200 * 1024 * 1024
|
||||||
defaultBaseFsSize = 300 * 1024 * 1024
|
defaultBaseFsSize = 300 * 1024 * 1024
|
||||||
defaultUdevSyncOverride = true
|
defaultUdevSyncOverride = true
|
||||||
if err := graphtest.InitLoopbacks(); err != nil {
|
if err := initLoopbacks(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initLoopbacks ensures that the loopback devices are properly created within
|
||||||
|
// the system running the device mapper tests.
|
||||||
|
func initLoopbacks() error {
|
||||||
|
statT, err := getBaseLoopStats()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// create at least 8 loopback files, ya, that is a good number
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
loopPath := fmt.Sprintf("/dev/loop%d", i)
|
||||||
|
// only create new loopback files if they don't exist
|
||||||
|
if _, err := os.Stat(loopPath); err != nil {
|
||||||
|
if mkerr := syscall.Mknod(loopPath,
|
||||||
|
uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil {
|
||||||
|
return mkerr
|
||||||
|
}
|
||||||
|
os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
|
||||||
|
// loop0 device on the system. If it does not exist we assume 0,0,0660 for the
|
||||||
|
// stat data
|
||||||
|
func getBaseLoopStats() (*syscall.Stat_t, error) {
|
||||||
|
loop0, err := os.Stat("/dev/loop0")
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return &syscall.Stat_t{
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 0,
|
||||||
|
Mode: 0660,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return loop0.Sys().(*syscall.Stat_t), nil
|
||||||
|
}
|
||||||
|
|
||||||
// This avoids creating a new driver for each test if all tests are run
|
// This avoids creating a new driver for each test if all tests are run
|
||||||
// Make sure to put new tests between TestDevmapperSetup and TestDevmapperTeardown
|
// Make sure to put new tests between TestDevmapperSetup and TestDevmapperTeardown
|
||||||
func TestDevmapperSetup(t *testing.T) {
|
func TestDevmapperSetup(t *testing.T) {
|
||||||
|
|
|
@ -16,6 +16,8 @@ import (
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -33,14 +35,9 @@ type Driver struct {
|
||||||
|
|
||||||
func newDriver(t testing.TB, name string, options []string) *Driver {
|
func newDriver(t testing.TB, name string, options []string) *Driver {
|
||||||
root, err := ioutil.TempDir("", "docker-graphtest-")
|
root, err := ioutil.TempDir("", "docker-graphtest-")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(root, 0755); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
require.NoError(t, os.MkdirAll(root, 0755))
|
||||||
d, err := graphdriver.GetDriver(name, nil, graphdriver.Options{DriverOptions: options, Root: root})
|
d, err := graphdriver.GetDriver(name, nil, graphdriver.Options{DriverOptions: options, Root: root})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("graphdriver: %v\n", err)
|
t.Logf("graphdriver: %v\n", err)
|
||||||
|
@ -86,14 +83,11 @@ func DriverTestCreateEmpty(t testing.TB, drivername string, driverOptions ...str
|
||||||
driver := GetDriver(t, drivername, driverOptions...)
|
driver := GetDriver(t, drivername, driverOptions...)
|
||||||
defer PutDriver(t)
|
defer PutDriver(t)
|
||||||
|
|
||||||
if err := driver.Create("empty", "", nil); err != nil {
|
err := driver.Create("empty", "", nil)
|
||||||
t.Fatal(err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := driver.Remove("empty"); err != nil {
|
require.NoError(t, driver.Remove("empty"))
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !driver.Exists("empty") {
|
if !driver.Exists("empty") {
|
||||||
|
@ -101,21 +95,14 @@ func DriverTestCreateEmpty(t testing.TB, drivername string, driverOptions ...str
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := driver.Get("empty", "")
|
dir, err := driver.Get("empty", "")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyFile(t, dir, 0755|os.ModeDir, 0, 0)
|
verifyFile(t, dir, 0755|os.ModeDir, 0, 0)
|
||||||
|
|
||||||
// Verify that the directory is empty
|
// Verify that the directory is empty
|
||||||
fis, err := readDir(dir)
|
fis, err := readDir(dir)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
assert.Len(t, fis, 0)
|
||||||
}
|
|
||||||
|
|
||||||
if len(fis) != 0 {
|
|
||||||
t.Fatal("New directory not empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
driver.Put("empty")
|
driver.Put("empty")
|
||||||
}
|
}
|
||||||
|
@ -127,9 +114,7 @@ func DriverTestCreateBase(t testing.TB, drivername string, driverOptions ...stri
|
||||||
|
|
||||||
createBase(t, driver, "Base")
|
createBase(t, driver, "Base")
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := driver.Remove("Base"); err != nil {
|
require.NoError(t, driver.Remove("Base"))
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
verifyBase(t, driver, "Base")
|
verifyBase(t, driver, "Base")
|
||||||
}
|
}
|
||||||
|
@ -140,21 +125,14 @@ func DriverTestCreateSnap(t testing.TB, drivername string, driverOptions ...stri
|
||||||
defer PutDriver(t)
|
defer PutDriver(t)
|
||||||
|
|
||||||
createBase(t, driver, "Base")
|
createBase(t, driver, "Base")
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := driver.Remove("Base"); err != nil {
|
require.NoError(t, driver.Remove("Base"))
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := driver.Create("Snap", "Base", nil); err != nil {
|
err := driver.Create("Snap", "Base", nil)
|
||||||
t.Fatal(err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := driver.Remove("Snap"); err != nil {
|
require.NoError(t, driver.Remove("Snap"))
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
verifyBase(t, driver, "Snap")
|
verifyBase(t, driver, "Snap")
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
package graphtest
|
package graphtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -11,81 +10,24 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/daemon/graphdriver"
|
"github.com/docker/docker/daemon/graphdriver"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitLoopbacks ensures that the loopback devices are properly created within
|
|
||||||
// the system running the device mapper tests.
|
|
||||||
func InitLoopbacks() error {
|
|
||||||
statT, err := getBaseLoopStats()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// create at least 8 loopback files, ya, that is a good number
|
|
||||||
for i := 0; i < 8; i++ {
|
|
||||||
loopPath := fmt.Sprintf("/dev/loop%d", i)
|
|
||||||
// only create new loopback files if they don't exist
|
|
||||||
if _, err := os.Stat(loopPath); err != nil {
|
|
||||||
if mkerr := syscall.Mknod(loopPath,
|
|
||||||
uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil {
|
|
||||||
return mkerr
|
|
||||||
}
|
|
||||||
os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
|
|
||||||
// loop0 device on the system. If it does not exist we assume 0,0,0660 for the
|
|
||||||
// stat data
|
|
||||||
func getBaseLoopStats() (*syscall.Stat_t, error) {
|
|
||||||
loop0, err := os.Stat("/dev/loop0")
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return &syscall.Stat_t{
|
|
||||||
Uid: 0,
|
|
||||||
Gid: 0,
|
|
||||||
Mode: 0660,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return loop0.Sys().(*syscall.Stat_t), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyFile(t testing.TB, path string, mode os.FileMode, uid, gid uint32) {
|
func verifyFile(t testing.TB, path string, mode os.FileMode, uid, gid uint32) {
|
||||||
fi, err := os.Stat(path)
|
fi, err := os.Stat(path)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fi.Mode()&os.ModeType != mode&os.ModeType {
|
actual := fi.Mode()
|
||||||
t.Fatalf("Expected %s type 0x%x, got 0x%x", path, mode&os.ModeType, fi.Mode()&os.ModeType)
|
assert.Equal(t, mode&os.ModeType, actual&os.ModeType, path)
|
||||||
}
|
assert.Equal(t, mode&os.ModePerm, actual&os.ModePerm, path)
|
||||||
|
assert.Equal(t, mode&os.ModeSticky, actual&os.ModeSticky, path)
|
||||||
if fi.Mode()&os.ModePerm != mode&os.ModePerm {
|
assert.Equal(t, mode&os.ModeSetuid, actual&os.ModeSetuid, path)
|
||||||
t.Fatalf("Expected %s mode %o, got %o", path, mode&os.ModePerm, fi.Mode()&os.ModePerm)
|
assert.Equal(t, mode&os.ModeSetgid, actual&os.ModeSetgid, path)
|
||||||
}
|
|
||||||
|
|
||||||
if fi.Mode()&os.ModeSticky != mode&os.ModeSticky {
|
|
||||||
t.Fatalf("Expected %s sticky 0x%x, got 0x%x", path, mode&os.ModeSticky, fi.Mode()&os.ModeSticky)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fi.Mode()&os.ModeSetuid != mode&os.ModeSetuid {
|
|
||||||
t.Fatalf("Expected %s setuid 0x%x, got 0x%x", path, mode&os.ModeSetuid, fi.Mode()&os.ModeSetuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fi.Mode()&os.ModeSetgid != mode&os.ModeSetgid {
|
|
||||||
t.Fatalf("Expected %s setgid 0x%x, got 0x%x", path, mode&os.ModeSetgid, fi.Mode()&os.ModeSetgid)
|
|
||||||
}
|
|
||||||
|
|
||||||
if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
|
if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
|
||||||
if stat.Uid != uid {
|
assert.Equal(t, uid, stat.Uid, path)
|
||||||
t.Fatalf("%s no owned by uid %d", path, uid)
|
assert.Equal(t, gid, stat.Gid, path)
|
||||||
}
|
|
||||||
if stat.Gid != gid {
|
|
||||||
t.Fatalf("%s not owned by gid %d", path, gid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,35 +36,25 @@ func createBase(t testing.TB, driver graphdriver.Driver, name string) {
|
||||||
oldmask := syscall.Umask(0)
|
oldmask := syscall.Umask(0)
|
||||||
defer syscall.Umask(oldmask)
|
defer syscall.Umask(oldmask)
|
||||||
|
|
||||||
if err := driver.CreateReadWrite(name, "", nil); err != nil {
|
err := driver.CreateReadWrite(name, "", nil)
|
||||||
t.Fatal(err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := driver.Get(name, "")
|
dir, err := driver.Get(name, "")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer driver.Put(name)
|
defer driver.Put(name)
|
||||||
|
|
||||||
subdir := path.Join(dir, "a subdir")
|
subdir := path.Join(dir, "a subdir")
|
||||||
if err := os.Mkdir(subdir, 0705|os.ModeSticky); err != nil {
|
require.NoError(t, os.Mkdir(subdir, 0705|os.ModeSticky))
|
||||||
t.Fatal(err)
|
require.NoError(t, os.Chown(subdir, 1, 2))
|
||||||
}
|
|
||||||
if err := os.Chown(subdir, 1, 2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
file := path.Join(dir, "a file")
|
file := path.Join(dir, "a file")
|
||||||
if err := ioutil.WriteFile(file, []byte("Some data"), 0222|os.ModeSetuid); err != nil {
|
err = ioutil.WriteFile(file, []byte("Some data"), 0222|os.ModeSetuid)
|
||||||
t.Fatal(err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyBase(t testing.TB, driver graphdriver.Driver, name string) {
|
func verifyBase(t testing.TB, driver graphdriver.Driver, name string) {
|
||||||
dir, err := driver.Get(name, "")
|
dir, err := driver.Get(name, "")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer driver.Put(name)
|
defer driver.Put(name)
|
||||||
|
|
||||||
subdir := path.Join(dir, "a subdir")
|
subdir := path.Join(dir, "a subdir")
|
||||||
|
@ -131,13 +63,7 @@ func verifyBase(t testing.TB, driver graphdriver.Driver, name string) {
|
||||||
file := path.Join(dir, "a file")
|
file := path.Join(dir, "a file")
|
||||||
verifyFile(t, file, 0222|os.ModeSetuid, 0, 0)
|
verifyFile(t, file, 0222|os.ModeSetuid, 0, 0)
|
||||||
|
|
||||||
fis, err := readDir(dir)
|
files, err := readDir(dir)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
assert.Len(t, files, 2)
|
||||||
}
|
|
||||||
|
|
||||||
if len(fis) != 2 {
|
|
||||||
t.Fatal("Unexpected files in base image")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,12 @@ import (
|
||||||
"github.com/docker/docker/pkg/chrootarchive"
|
"github.com/docker/docker/pkg/chrootarchive"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
|
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// CopyWithTar defines the copy method to use.
|
// CopyWithTar defines the copy method to use.
|
||||||
CopyWithTar = chrootarchive.CopyWithTar
|
CopyWithTar = chrootarchive.NewArchiver(nil).CopyWithTar
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -26,15 +25,11 @@ func init() {
|
||||||
// This sets the home directory for the driver and returns NaiveDiffDriver.
|
// This sets the home directory for the driver and returns NaiveDiffDriver.
|
||||||
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||||
d := &Driver{
|
d := &Driver{
|
||||||
home: home,
|
home: home,
|
||||||
uidMaps: uidMaps,
|
idMappings: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps),
|
||||||
gidMaps: gidMaps,
|
|
||||||
}
|
}
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
rootIDs := d.idMappings.RootPair()
|
||||||
if err != nil {
|
if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||||
|
@ -45,9 +40,8 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
// In order to support layering, files are copied from the parent layer into the new layer. There is no copy-on-write support.
|
// In order to support layering, files are copied from the parent layer into the new layer. There is no copy-on-write support.
|
||||||
// Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver
|
// Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
home string
|
home string
|
||||||
uidMaps []idtools.IDMap
|
idMappings *idtools.IDMappings
|
||||||
gidMaps []idtools.IDMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) String() string {
|
func (d *Driver) String() string {
|
||||||
|
@ -82,14 +76,11 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
dir := d.dir(id)
|
dir := d.dir(id)
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
rootIDs := d.idMappings.RootPair()
|
||||||
if err != nil {
|
if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAllAs(filepath.Dir(dir), 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := idtools.MkdirAs(dir, 0755, rootUID, rootGID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
labelOpts := []string{"level:s0"}
|
labelOpts := []string{"level:s0"}
|
||||||
|
@ -103,10 +94,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %s", parent, err)
|
return fmt.Errorf("%s: %s", parent, err)
|
||||||
}
|
}
|
||||||
if err := CopyWithTar(parentDir, dir); err != nil {
|
return CopyWithTar(parentDir, dir)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) dir(id string) string {
|
func (d *Driver) dir(id string) string {
|
||||||
|
|
|
@ -72,8 +72,8 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
||||||
if selinuxEnabled() {
|
if selinuxEnabled() {
|
||||||
securityOptions = append(securityOptions, "name=selinux")
|
securityOptions = append(securityOptions, "name=selinux")
|
||||||
}
|
}
|
||||||
uid, gid := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
if uid != 0 || gid != 0 {
|
if rootIDs.UID != 0 || rootIDs.GID != 0 {
|
||||||
securityOptions = append(securityOptions, "name=userns")
|
securityOptions = append(securityOptions, "name=userns")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
//
|
//
|
||||||
// This extra layer is used by all containers as the top-most ro layer. It protects
|
// This extra layer is used by all containers as the top-most ro layer. It protects
|
||||||
// the container from unwanted side-effects on the rw layer.
|
// the container from unwanted side-effects on the rw layer.
|
||||||
func Setup(initLayer string, rootUID, rootGID int) error {
|
func Setup(initLayer string, rootIDs idtools.IDPair) error {
|
||||||
for pth, typ := range map[string]string{
|
for pth, typ := range map[string]string{
|
||||||
"/dev/pts": "dir",
|
"/dev/pts": "dir",
|
||||||
"/dev/shm": "dir",
|
"/dev/shm": "dir",
|
||||||
|
@ -38,12 +38,12 @@ func Setup(initLayer string, rootUID, rootGID int) error {
|
||||||
|
|
||||||
if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
|
if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch typ {
|
switch typ {
|
||||||
case "dir":
|
case "dir":
|
||||||
if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, pth), 0755, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, pth), 0755, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "file":
|
case "file":
|
||||||
|
@ -51,7 +51,7 @@ func Setup(initLayer string, rootUID, rootGID int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.Chown(rootUID, rootGID)
|
f.Chown(rootIDs.UID, rootIDs.GID)
|
||||||
f.Close()
|
f.Close()
|
||||||
default:
|
default:
|
||||||
if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
|
if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
|
||||||
|
|
|
@ -2,12 +2,16 @@
|
||||||
|
|
||||||
package initlayer
|
package initlayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
)
|
||||||
|
|
||||||
// Setup populates a directory with mountpoints suitable
|
// Setup populates a directory with mountpoints suitable
|
||||||
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
||||||
// empty file at /.dockerinit
|
// empty file at /.dockerinit
|
||||||
//
|
//
|
||||||
// This extra layer is used by all containers as the top-most ro layer. It protects
|
// This extra layer is used by all containers as the top-most ro layer. It protects
|
||||||
// the container from unwanted side-effects on the rw layer.
|
// the container from unwanted side-effects on the rw layer.
|
||||||
func Setup(initLayer string, rootUID, rootGID int) error {
|
func Setup(initLayer string, rootIDs idtools.IDPair) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,13 +271,13 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
|
||||||
userNS := false
|
userNS := false
|
||||||
// user
|
// user
|
||||||
if c.HostConfig.UsernsMode.IsPrivate() {
|
if c.HostConfig.UsernsMode.IsPrivate() {
|
||||||
uidMap, gidMap := daemon.GetUIDGIDMaps()
|
uidMap := daemon.idMappings.UIDs()
|
||||||
if uidMap != nil {
|
if uidMap != nil {
|
||||||
userNS = true
|
userNS = true
|
||||||
ns := specs.LinuxNamespace{Type: "user"}
|
ns := specs.LinuxNamespace{Type: "user"}
|
||||||
setNamespace(s, ns)
|
setNamespace(s, ns)
|
||||||
s.Linux.UIDMappings = specMapping(uidMap)
|
s.Linux.UIDMappings = specMapping(uidMap)
|
||||||
s.Linux.GIDMappings = specMapping(gidMap)
|
s.Linux.GIDMappings = specMapping(daemon.idMappings.GIDs())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// network
|
// network
|
||||||
|
@ -591,7 +591,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
|
||||||
|
|
||||||
// TODO: until a kernel/mount solution exists for handling remount in a user namespace,
|
// TODO: until a kernel/mount solution exists for handling remount in a user namespace,
|
||||||
// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
|
// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
|
||||||
if uidMap, _ := daemon.GetUIDGIDMaps(); uidMap != nil || c.HostConfig.Privileged {
|
if uidMap := daemon.idMappings.UIDs(); uidMap != nil || c.HostConfig.Privileged {
|
||||||
for i, m := range s.Mounts {
|
for i, m := range s.Mounts {
|
||||||
if m.Type == "cgroup" {
|
if m.Type == "cgroup" {
|
||||||
clearReadOnly(&s.Mounts[i])
|
clearReadOnly(&s.Mounts[i])
|
||||||
|
@ -611,8 +611,7 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
|
||||||
Path: c.BaseFS,
|
Path: c.BaseFS,
|
||||||
Readonly: c.HostConfig.ReadonlyRootfs,
|
Readonly: c.HostConfig.ReadonlyRootfs,
|
||||||
}
|
}
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
if err := c.SetupWorkingDirectory(daemon.idMappings.RootPair()); err != nil {
|
||||||
if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cwd := c.Config.WorkingDir
|
cwd := c.Config.WorkingDir
|
||||||
|
|
|
@ -130,8 +130,7 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
|
||||||
Path: filepath.Dir(c.BaseFS),
|
Path: filepath.Dir(c.BaseFS),
|
||||||
Readonly: c.HostConfig.ReadonlyRootfs,
|
Readonly: c.HostConfig.ReadonlyRootfs,
|
||||||
}
|
}
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
if err := c.SetupWorkingDirectory(daemon.idMappings.RootPair()); err != nil {
|
||||||
if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cwd := c.Config.WorkingDir
|
cwd := c.Config.WorkingDir
|
||||||
|
|
|
@ -54,8 +54,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
path, err := m.Setup(c.MountLabel, daemon.idMappings.RootPair(), checkfunc)
|
||||||
path, err := m.Setup(c.MountLabel, rootUID, rootGID, checkfunc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -85,9 +84,9 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
|
||||||
// if we are going to mount any of the network files from container
|
// if we are going to mount any of the network files from container
|
||||||
// metadata, the ownership must be set properly for potential container
|
// metadata, the ownership must be set properly for potential container
|
||||||
// remapped root (user namespaces)
|
// remapped root (user namespaces)
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
for _, mount := range netMounts {
|
for _, mount := range netMounts {
|
||||||
if err := os.Chown(mount.Source, rootUID, rootGID); err != nil {
|
if err := os.Chown(mount.Source, rootIDs.UID, rootIDs.GID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
|
||||||
if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
|
if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s, err := mount.Setup(c.MountLabel, 0, 0, nil)
|
s, err := mount.Setup(c.MountLabel, idtools.IDPair{0, 0}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,5 @@ func (daemon *Daemon) ContainerCreateWorkdir(cID string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
rootUID, rootGID := daemon.GetRemappedUIDGID()
|
return container.SetupWorkingDirectory(daemon.idMappings.RootPair())
|
||||||
return container.SetupWorkingDirectory(rootUID, rootGID)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ func testDirectory(templateDir string) (dir string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if templateDir != "" {
|
if templateDir != "" {
|
||||||
if err = archive.CopyWithTar(templateDir, dir); err != nil {
|
if err = archive.NewDefaultArchiver().CopyWithTar(templateDir, dir); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,7 @@ type StoreOptions struct {
|
||||||
MetadataStorePathTemplate string
|
MetadataStorePathTemplate string
|
||||||
GraphDriver string
|
GraphDriver string
|
||||||
GraphDriverOptions []string
|
GraphDriverOptions []string
|
||||||
UIDMaps []idtools.IDMap
|
IDMappings *idtools.IDMappings
|
||||||
GIDMaps []idtools.IDMap
|
|
||||||
PluginGetter plugingetter.PluginGetter
|
PluginGetter plugingetter.PluginGetter
|
||||||
ExperimentalEnabled bool
|
ExperimentalEnabled bool
|
||||||
}
|
}
|
||||||
|
@ -55,8 +54,8 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
|
||||||
driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{
|
driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{
|
||||||
Root: options.StorePath,
|
Root: options.StorePath,
|
||||||
DriverOptions: options.GraphDriverOptions,
|
DriverOptions: options.GraphDriverOptions,
|
||||||
UIDMaps: options.UIDMaps,
|
UIDMaps: options.IDMappings.UIDs(),
|
||||||
GIDMaps: options.GIDMaps,
|
GIDMaps: options.IDMappings.GIDs(),
|
||||||
ExperimentalEnabled: options.ExperimentalEnabled,
|
ExperimentalEnabled: options.ExperimentalEnabled,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -20,7 +20,8 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
graphdriver.ApplyUncompressedLayer = archive.UnpackLayer
|
graphdriver.ApplyUncompressedLayer = archive.UnpackLayer
|
||||||
vfs.CopyWithTar = archive.CopyWithTar
|
defaultArchiver := archive.NewDefaultArchiver()
|
||||||
|
vfs.CopyWithTar = defaultArchiver.CopyWithTar
|
||||||
}
|
}
|
||||||
|
|
||||||
func newVFSGraphDriver(td string) (graphdriver.Driver, error) {
|
func newVFSGraphDriver(td string) (graphdriver.Driver, error) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (clnt *client) prepareBundleDir(uid, gid int) (string, error) {
|
||||||
}
|
}
|
||||||
if os.IsNotExist(err) || fi.Mode()&1 == 0 {
|
if os.IsNotExist(err) || fi.Mode()&1 == 0 {
|
||||||
p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
|
p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
|
||||||
if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) {
|
if err := idtools.MkdirAndChown(p, 0700, idtools.IDPair{uid, gid}); err != nil && !os.IsExist(err) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ func (clnt *client) Create(containerID string, checkpoint string, checkpointDir
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) {
|
if err := idtools.MkdirAllAndChown(container.dir, 0700, idtools.IDPair{uid, gid}); err != nil && !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/bzip2"
|
"compress/bzip2"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -31,10 +30,6 @@ type (
|
||||||
Compression int
|
Compression int
|
||||||
// WhiteoutFormat is the format of whiteouts unpacked
|
// WhiteoutFormat is the format of whiteouts unpacked
|
||||||
WhiteoutFormat int
|
WhiteoutFormat int
|
||||||
// TarChownOptions wraps the chown options UID and GID.
|
|
||||||
TarChownOptions struct {
|
|
||||||
UID, GID int
|
|
||||||
}
|
|
||||||
|
|
||||||
// TarOptions wraps the tar options.
|
// TarOptions wraps the tar options.
|
||||||
TarOptions struct {
|
TarOptions struct {
|
||||||
|
@ -44,7 +39,7 @@ type (
|
||||||
NoLchown bool
|
NoLchown bool
|
||||||
UIDMaps []idtools.IDMap
|
UIDMaps []idtools.IDMap
|
||||||
GIDMaps []idtools.IDMap
|
GIDMaps []idtools.IDMap
|
||||||
ChownOpts *TarChownOptions
|
ChownOpts *idtools.IDPair
|
||||||
IncludeSourceDir bool
|
IncludeSourceDir bool
|
||||||
// WhiteoutFormat is the expected on disk format for whiteout files.
|
// WhiteoutFormat is the expected on disk format for whiteout files.
|
||||||
// This format will be converted to the standard format on pack
|
// This format will be converted to the standard format on pack
|
||||||
|
@ -58,33 +53,26 @@ type (
|
||||||
RebaseNames map[string]string
|
RebaseNames map[string]string
|
||||||
InUserNS bool
|
InUserNS bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Archiver allows the reuse of most utility functions of this package
|
|
||||||
// with a pluggable Untar function. Also, to facilitate the passing of
|
|
||||||
// specific id mappings for untar, an archiver can be created with maps
|
|
||||||
// which will then be passed to Untar operations
|
|
||||||
Archiver struct {
|
|
||||||
Untar func(io.Reader, string, *TarOptions) error
|
|
||||||
UIDMaps []idtools.IDMap
|
|
||||||
GIDMaps []idtools.IDMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// breakoutError is used to differentiate errors related to breaking out
|
|
||||||
// When testing archive breakout in the unit tests, this error is expected
|
|
||||||
// in order for the test to pass.
|
|
||||||
breakoutError error
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// Archiver allows the reuse of most utility functions of this package
|
||||||
// ErrNotImplemented is the error message of function not implemented.
|
// with a pluggable Untar function. Also, to facilitate the passing of
|
||||||
ErrNotImplemented = errors.New("Function not implemented")
|
// specific id mappings for untar, an archiver can be created with maps
|
||||||
defaultArchiver = &Archiver{Untar: Untar, UIDMaps: nil, GIDMaps: nil}
|
// which will then be passed to Untar operations
|
||||||
)
|
type Archiver struct {
|
||||||
|
Untar func(io.Reader, string, *TarOptions) error
|
||||||
|
IDMappings *idtools.IDMappings
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
// NewDefaultArchiver returns a new Archiver without any IDMappings
|
||||||
// HeaderSize is the size in bytes of a tar header
|
func NewDefaultArchiver() *Archiver {
|
||||||
HeaderSize = 512
|
return &Archiver{Untar: Untar, IDMappings: &idtools.IDMappings{}}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
// breakoutError is used to differentiate errors related to breaking out
|
||||||
|
// When testing archive breakout in the unit tests, this error is expected
|
||||||
|
// in order for the test to pass.
|
||||||
|
type breakoutError error
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Uncompressed represents the uncompressed.
|
// Uncompressed represents the uncompressed.
|
||||||
|
@ -105,18 +93,6 @@ const (
|
||||||
OverlayWhiteoutFormat
|
OverlayWhiteoutFormat
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsArchive checks for the magic bytes of a tar or any supported compression
|
|
||||||
// algorithm.
|
|
||||||
func IsArchive(header []byte) bool {
|
|
||||||
compression := DetectCompression(header)
|
|
||||||
if compression != Uncompressed {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
r := tar.NewReader(bytes.NewBuffer(header))
|
|
||||||
_, err := r.Next()
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsArchivePath checks if the (possibly compressed) file at the given path
|
// IsArchivePath checks if the (possibly compressed) file at the given path
|
||||||
// starts with a tar file header.
|
// starts with a tar file header.
|
||||||
func IsArchivePath(path string) bool {
|
func IsArchivePath(path string) bool {
|
||||||
|
@ -369,9 +345,8 @@ type tarAppender struct {
|
||||||
Buffer *bufio.Writer
|
Buffer *bufio.Writer
|
||||||
|
|
||||||
// for hardlink mapping
|
// for hardlink mapping
|
||||||
SeenFiles map[uint64]string
|
SeenFiles map[uint64]string
|
||||||
UIDMaps []idtools.IDMap
|
IDMappings *idtools.IDMappings
|
||||||
GIDMaps []idtools.IDMap
|
|
||||||
|
|
||||||
// For packing and unpacking whiteout files in the
|
// For packing and unpacking whiteout files in the
|
||||||
// non standard format. The whiteout files defined
|
// non standard format. The whiteout files defined
|
||||||
|
@ -380,6 +355,15 @@ type tarAppender struct {
|
||||||
WhiteoutConverter tarWhiteoutConverter
|
WhiteoutConverter tarWhiteoutConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTarAppender(idMapping *idtools.IDMappings, writer io.Writer) *tarAppender {
|
||||||
|
return &tarAppender{
|
||||||
|
SeenFiles: make(map[uint64]string),
|
||||||
|
TarWriter: tar.NewWriter(writer),
|
||||||
|
Buffer: pools.BufioWriter32KPool.Get(nil),
|
||||||
|
IDMappings: idMapping,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// canonicalTarName provides a platform-independent and consistent posix-style
|
// canonicalTarName provides a platform-independent and consistent posix-style
|
||||||
//path for files and directories to be archived regardless of the platform.
|
//path for files and directories to be archived regardless of the platform.
|
||||||
func canonicalTarName(name string, isDir bool) (string, error) {
|
func canonicalTarName(name string, isDir bool) (string, error) {
|
||||||
|
@ -428,21 +412,15 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||||
//handle re-mapping container ID mappings back to host ID mappings before
|
//handle re-mapping container ID mappings back to host ID mappings before
|
||||||
//writing tar headers/files. We skip whiteout files because they were written
|
//writing tar headers/files. We skip whiteout files because they were written
|
||||||
//by the kernel and already have proper ownership relative to the host
|
//by the kernel and already have proper ownership relative to the host
|
||||||
if !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && (ta.UIDMaps != nil || ta.GIDMaps != nil) {
|
if !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IDMappings.Empty() {
|
||||||
uid, gid, err := getFileUIDGID(fi.Sys())
|
fileIDPair, err := getFileUIDGID(fi.Sys())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
xUID, err := idtools.ToContainer(uid, ta.UIDMaps)
|
hdr.Uid, hdr.Gid, err = ta.IDMappings.ToContainer(fileIDPair)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
xGID, err := idtools.ToContainer(gid, ta.GIDMaps)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hdr.Uid = xUID
|
|
||||||
hdr.Gid = xGID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ta.WhiteoutConverter != nil {
|
if ta.WhiteoutConverter != nil {
|
||||||
|
@ -496,7 +474,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions, inUserns bool) error {
|
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.IDPair, inUserns bool) error {
|
||||||
// hdr.Mode is in linux format, which we can use for sycalls,
|
// hdr.Mode is in linux format, which we can use for sycalls,
|
||||||
// but for os.Foo() calls we need the mode converted to os.FileMode,
|
// but for os.Foo() calls we need the mode converted to os.FileMode,
|
||||||
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
|
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
|
||||||
|
@ -576,7 +554,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
||||||
// Lchown is not supported on Windows.
|
// Lchown is not supported on Windows.
|
||||||
if Lchown && runtime.GOOS != "windows" {
|
if Lchown && runtime.GOOS != "windows" {
|
||||||
if chownOpts == nil {
|
if chownOpts == nil {
|
||||||
chownOpts = &TarChownOptions{UID: hdr.Uid, GID: hdr.Gid}
|
chownOpts = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
|
||||||
}
|
}
|
||||||
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
|
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -664,14 +642,11 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ta := &tarAppender{
|
ta := newTarAppender(
|
||||||
TarWriter: tar.NewWriter(compressWriter),
|
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
|
||||||
Buffer: pools.BufioWriter32KPool.Get(nil),
|
compressWriter,
|
||||||
SeenFiles: make(map[uint64]string),
|
)
|
||||||
UIDMaps: options.UIDMaps,
|
ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat)
|
||||||
GIDMaps: options.GIDMaps,
|
|
||||||
WhiteoutConverter: getWhiteoutConverter(options.WhiteoutFormat),
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// Make sure to check the error on Close.
|
// Make sure to check the error on Close.
|
||||||
|
@ -827,10 +802,8 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
|
||||||
defer pools.BufioReader32KPool.Put(trBuf)
|
defer pools.BufioReader32KPool.Put(trBuf)
|
||||||
|
|
||||||
var dirs []*tar.Header
|
var dirs []*tar.Header
|
||||||
remappedRootUID, remappedRootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps)
|
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||||
if err != nil {
|
rootIDs := idMappings.RootPair()
|
||||||
return err
|
|
||||||
}
|
|
||||||
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
|
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
|
||||||
|
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
|
@ -864,7 +837,7 @@ loop:
|
||||||
parent := filepath.Dir(hdr.Name)
|
parent := filepath.Dir(hdr.Name)
|
||||||
parentPath := filepath.Join(dest, parent)
|
parentPath := filepath.Join(dest, parent)
|
||||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||||
err = idtools.MkdirAllNewAs(parentPath, 0777, remappedRootUID, remappedRootGID)
|
err = idtools.MkdirAllAndChownNew(parentPath, 0777, rootIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -909,26 +882,8 @@ loop:
|
||||||
}
|
}
|
||||||
trBuf.Reset(tr)
|
trBuf.Reset(tr)
|
||||||
|
|
||||||
// if the options contain a uid & gid maps, convert header uid/gid
|
if err := remapIDs(idMappings, hdr); err != nil {
|
||||||
// entries using the maps such that lchown sets the proper mapped
|
return err
|
||||||
// uid/gid after writing the file. We only perform this mapping if
|
|
||||||
// the file isn't already owned by the remapped root UID or GID, as
|
|
||||||
// that specific uid/gid has no mapping from container -> host, and
|
|
||||||
// those files already have the proper ownership for inside the
|
|
||||||
// container.
|
|
||||||
if hdr.Uid != remappedRootUID {
|
|
||||||
xUID, err := idtools.ToHost(hdr.Uid, options.UIDMaps)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hdr.Uid = xUID
|
|
||||||
}
|
|
||||||
if hdr.Gid != remappedRootGID {
|
|
||||||
xGID, err := idtools.ToHost(hdr.Gid, options.GIDMaps)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hdr.Gid = xGID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if whiteoutConverter != nil {
|
if whiteoutConverter != nil {
|
||||||
|
@ -1013,23 +968,13 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer archive.Close()
|
defer archive.Close()
|
||||||
|
options := &TarOptions{
|
||||||
var options *TarOptions
|
UIDMaps: archiver.IDMappings.UIDs(),
|
||||||
if archiver.UIDMaps != nil || archiver.GIDMaps != nil {
|
GIDMaps: archiver.IDMappings.GIDs(),
|
||||||
options = &TarOptions{
|
|
||||||
UIDMaps: archiver.UIDMaps,
|
|
||||||
GIDMaps: archiver.GIDMaps,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return archiver.Untar(archive, dst, options)
|
return archiver.Untar(archive, dst, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
|
|
||||||
// If either Tar or Untar fails, TarUntar aborts and returns the error.
|
|
||||||
func TarUntar(src, dst string) error {
|
|
||||||
return defaultArchiver.TarUntar(src, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UntarPath untar a file from path to a destination, src is the source tar file path.
|
// UntarPath untar a file from path to a destination, src is the source tar file path.
|
||||||
func (archiver *Archiver) UntarPath(src, dst string) error {
|
func (archiver *Archiver) UntarPath(src, dst string) error {
|
||||||
archive, err := os.Open(src)
|
archive, err := os.Open(src)
|
||||||
|
@ -1037,22 +982,13 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer archive.Close()
|
defer archive.Close()
|
||||||
var options *TarOptions
|
options := &TarOptions{
|
||||||
if archiver.UIDMaps != nil || archiver.GIDMaps != nil {
|
UIDMaps: archiver.IDMappings.UIDs(),
|
||||||
options = &TarOptions{
|
GIDMaps: archiver.IDMappings.GIDs(),
|
||||||
UIDMaps: archiver.UIDMaps,
|
|
||||||
GIDMaps: archiver.GIDMaps,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return archiver.Untar(archive, dst, options)
|
return archiver.Untar(archive, dst, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UntarPath is a convenience function which looks for an archive
|
|
||||||
// at filesystem path `src`, and unpacks it at `dst`.
|
|
||||||
func UntarPath(src, dst string) error {
|
|
||||||
return defaultArchiver.UntarPath(src, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyWithTar creates a tar archive of filesystem path `src`, and
|
// CopyWithTar creates a tar archive of filesystem path `src`, and
|
||||||
// unpacks it at filesystem path `dst`.
|
// unpacks it at filesystem path `dst`.
|
||||||
// The archive is streamed directly with fixed buffering and no
|
// The archive is streamed directly with fixed buffering and no
|
||||||
|
@ -1069,27 +1005,16 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error {
|
||||||
// if this archiver is set up with ID mapping we need to create
|
// if this archiver is set up with ID mapping we need to create
|
||||||
// the new destination directory with the remapped root UID/GID pair
|
// the new destination directory with the remapped root UID/GID pair
|
||||||
// as owner
|
// as owner
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(archiver.UIDMaps, archiver.GIDMaps)
|
rootIDs := archiver.IDMappings.RootPair()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Create dst, copy src's content into it
|
// Create dst, copy src's content into it
|
||||||
logrus.Debugf("Creating dest directory: %s", dst)
|
logrus.Debugf("Creating dest directory: %s", dst)
|
||||||
if err := idtools.MkdirAllNewAs(dst, 0755, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
||||||
return archiver.TarUntar(src, dst)
|
return archiver.TarUntar(src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 CopyWithTar(src, dst string) error {
|
|
||||||
return defaultArchiver.CopyWithTar(src, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
||||||
// for a single file. It copies a regular file from path `src` to
|
// for a single file. It copies a regular file from path `src` to
|
||||||
// path `dst`, and preserves all its metadata.
|
// path `dst`, and preserves all its metadata.
|
||||||
|
@ -1131,28 +1056,10 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||||
hdr.Name = filepath.Base(dst)
|
hdr.Name = filepath.Base(dst)
|
||||||
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
||||||
|
|
||||||
remappedRootUID, remappedRootGID, err := idtools.GetRootUIDGID(archiver.UIDMaps, archiver.GIDMaps)
|
if err := remapIDs(archiver.IDMappings, hdr); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// only perform mapping if the file being copied isn't already owned by the
|
|
||||||
// uid or gid of the remapped root in the container
|
|
||||||
if remappedRootUID != hdr.Uid {
|
|
||||||
xUID, err := idtools.ToHost(hdr.Uid, archiver.UIDMaps)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hdr.Uid = xUID
|
|
||||||
}
|
|
||||||
if remappedRootGID != hdr.Gid {
|
|
||||||
xGID, err := idtools.ToHost(hdr.Gid, archiver.GIDMaps)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hdr.Gid = xGID
|
|
||||||
}
|
|
||||||
|
|
||||||
tw := tar.NewWriter(w)
|
tw := tar.NewWriter(w)
|
||||||
defer tw.Close()
|
defer tw.Close()
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
|
@ -1176,16 +1083,10 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error {
|
||||||
// for a single file. It copies a regular file from path `src` to
|
ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
|
||||||
// path `dst`, and preserves all its metadata.
|
hdr.Uid, hdr.Gid = ids.UID, ids.GID
|
||||||
//
|
return err
|
||||||
// Destination handling is in an operating specific manner depending
|
|
||||||
// where the daemon is running. If `dst` ends with a trailing slash
|
|
||||||
// the final destination path will be `dst/base(src)` (Linux) or
|
|
||||||
// `dst\base(src)` (Windows).
|
|
||||||
func CopyFileWithTar(src, dst string) (err error) {
|
|
||||||
return defaultArchiver.CopyFileWithTar(src, dst)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cmdStream executes a command, and returns its stdout as a stream.
|
// cmdStream executes a command, and returns its stdout as a stream.
|
||||||
|
|
|
@ -27,35 +27,22 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsArchiveNilHeader(t *testing.T) {
|
var defaultArchiver = NewDefaultArchiver()
|
||||||
out := IsArchive(nil)
|
|
||||||
if out {
|
func defaultTarUntar(src, dst string) error {
|
||||||
t.Fatalf("isArchive should return false as nil is not a valid archive header")
|
return defaultArchiver.TarUntar(src, dst)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsArchiveInvalidHeader(t *testing.T) {
|
func defaultUntarPath(src, dst string) error {
|
||||||
header := []byte{0x00, 0x01, 0x02}
|
return defaultArchiver.UntarPath(src, dst)
|
||||||
out := IsArchive(header)
|
|
||||||
if out {
|
|
||||||
t.Fatalf("isArchive should return false as %s is not a valid archive header", header)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsArchiveBzip2(t *testing.T) {
|
func defaultCopyFileWithTar(src, dst string) (err error) {
|
||||||
header := []byte{0x42, 0x5A, 0x68}
|
return defaultArchiver.CopyFileWithTar(src, dst)
|
||||||
out := IsArchive(header)
|
|
||||||
if !out {
|
|
||||||
t.Fatalf("isArchive should return true as %s is a bz2 header", header)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsArchive7zip(t *testing.T) {
|
func defaultCopyWithTar(src, dst string) error {
|
||||||
header := []byte{0x50, 0x4b, 0x03, 0x04}
|
return defaultArchiver.CopyWithTar(src, dst)
|
||||||
out := IsArchive(header)
|
|
||||||
if out {
|
|
||||||
t.Fatalf("isArchive should return false as %s is a 7z header and it is not supported", header)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsArchivePathDir(t *testing.T) {
|
func TestIsArchivePathDir(t *testing.T) {
|
||||||
|
@ -301,7 +288,7 @@ func TestUntarPathWithInvalidDest(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = UntarPath(tarFile, invalidDestFolder)
|
err = defaultUntarPath(tarFile, invalidDestFolder)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("UntarPath with invalid destination path should throw an error.")
|
t.Fatalf("UntarPath with invalid destination path should throw an error.")
|
||||||
}
|
}
|
||||||
|
@ -313,7 +300,7 @@ func TestUntarPathWithInvalidSrc(t *testing.T) {
|
||||||
t.Fatalf("Fail to create the destination file")
|
t.Fatalf("Fail to create the destination file")
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dest)
|
defer os.RemoveAll(dest)
|
||||||
err = UntarPath("/invalid/path", dest)
|
err = defaultUntarPath("/invalid/path", dest)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("UntarPath with invalid src path should throw an error.")
|
t.Fatalf("UntarPath with invalid src path should throw an error.")
|
||||||
}
|
}
|
||||||
|
@ -348,7 +335,7 @@ func TestUntarPath(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = UntarPath(tarFile, destFolder)
|
err = defaultUntarPath(tarFile, destFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("UntarPath shouldn't throw an error, %s.", err)
|
t.Fatalf("UntarPath shouldn't throw an error, %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -387,7 +374,7 @@ func TestUntarPathWithDestinationFile(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Fail to create the destination file")
|
t.Fatalf("Fail to create the destination file")
|
||||||
}
|
}
|
||||||
err = UntarPath(tarFile, destFile)
|
err = defaultUntarPath(tarFile, destFile)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("UntarPath should throw an error if the destination if a file")
|
t.Fatalf("UntarPath should throw an error if the destination if a file")
|
||||||
}
|
}
|
||||||
|
@ -430,7 +417,7 @@ func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = UntarPath(tarFile, destFolder)
|
err = defaultUntarPath(tarFile, destFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("UntarPath should throw not throw an error if the extracted file already exists and is a folder")
|
t.Fatalf("UntarPath should throw not throw an error if the extracted file already exists and is a folder")
|
||||||
}
|
}
|
||||||
|
@ -447,7 +434,7 @@ func TestCopyWithTarInvalidSrc(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = CopyWithTar(invalidSrc, destFolder)
|
err = defaultCopyWithTar(invalidSrc, destFolder)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.")
|
t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.")
|
||||||
}
|
}
|
||||||
|
@ -464,7 +451,7 @@ func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = CopyWithTar(srcFolder, inexistentDestFolder)
|
err = defaultCopyWithTar(srcFolder, inexistentDestFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.")
|
t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.")
|
||||||
}
|
}
|
||||||
|
@ -493,7 +480,7 @@ func TestCopyWithTarSrcFile(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ioutil.WriteFile(src, []byte("content"), 0777)
|
ioutil.WriteFile(src, []byte("content"), 0777)
|
||||||
err = CopyWithTar(src, dest)
|
err = defaultCopyWithTar(src, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err)
|
t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -522,7 +509,7 @@ func TestCopyWithTarSrcFolder(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ioutil.WriteFile(filepath.Join(src, "file"), []byte("content"), 0777)
|
ioutil.WriteFile(filepath.Join(src, "file"), []byte("content"), 0777)
|
||||||
err = CopyWithTar(src, dest)
|
err = defaultCopyWithTar(src, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err)
|
t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -545,7 +532,7 @@ func TestCopyFileWithTarInvalidSrc(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
invalidFile := filepath.Join(tempFolder, "doesnotexists")
|
invalidFile := filepath.Join(tempFolder, "doesnotexists")
|
||||||
err = CopyFileWithTar(invalidFile, destFolder)
|
err = defaultCopyFileWithTar(invalidFile, destFolder)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.")
|
t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.")
|
||||||
}
|
}
|
||||||
|
@ -563,7 +550,7 @@ func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = CopyFileWithTar(srcFile, inexistentDestFolder)
|
err = defaultCopyFileWithTar(srcFile, inexistentDestFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.")
|
t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.")
|
||||||
}
|
}
|
||||||
|
@ -590,7 +577,7 @@ func TestCopyFileWithTarSrcFolder(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = CopyFileWithTar(src, dest)
|
err = defaultCopyFileWithTar(src, dest)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("CopyFileWithTar should throw an error with a folder.")
|
t.Fatalf("CopyFileWithTar should throw an error with a folder.")
|
||||||
}
|
}
|
||||||
|
@ -614,7 +601,7 @@ func TestCopyFileWithTarSrcFile(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ioutil.WriteFile(src, []byte("content"), 0777)
|
ioutil.WriteFile(src, []byte("content"), 0777)
|
||||||
err = CopyWithTar(src, dest+"/")
|
err = defaultCopyWithTar(src, dest+"/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err)
|
t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -657,7 +644,7 @@ func checkNoChanges(fileNum int, hardlinks bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = TarUntar(srcDir, destDir)
|
err = defaultTarUntar(srcDir, destDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -871,7 +858,7 @@ func BenchmarkTarUntar(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(n))
|
b.SetBytes(int64(n))
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
err := TarUntar(origin, target)
|
err := defaultTarUntar(origin, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -899,7 +886,7 @@ func BenchmarkTarUntarWithLinks(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(n))
|
b.SetBytes(int64(n))
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
err := TarUntar(origin, target)
|
err := defaultTarUntar(origin, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
rsystem "github.com/opencontainers/runc/libcontainer/system"
|
rsystem "github.com/opencontainers/runc/libcontainer/system"
|
||||||
)
|
)
|
||||||
|
@ -72,13 +73,13 @@ func getInodeFromStat(stat interface{}) (inode uint64, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileUIDGID(stat interface{}) (int, int, error) {
|
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
|
||||||
s, ok := stat.(*syscall.Stat_t)
|
s, ok := stat.(*syscall.Stat_t)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return -1, -1, errors.New("cannot convert stat value to syscall.Stat_t")
|
return idtools.IDPair{}, errors.New("cannot convert stat value to syscall.Stat_t")
|
||||||
}
|
}
|
||||||
return int(s.Uid), int(s.Gid), nil
|
return idtools.IDPair{UID: int(s.Uid), GID: int(s.Gid)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func major(device uint64) uint64 {
|
func major(device uint64) uint64 {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/longpath"
|
"github.com/docker/docker/pkg/longpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileUIDGID(stat interface{}) (int, int, error) {
|
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
|
||||||
// no notion of file ownership mapping yet on Windows
|
// no notion of file ownership mapping yet on Windows
|
||||||
return 0, 0, nil
|
return idtools.IDPair{0, 0}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ func TestCopyFileWithInvalidDest(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ioutil.WriteFile(src, []byte("content"), 0777)
|
ioutil.WriteFile(src, []byte("content"), 0777)
|
||||||
err = CopyWithTar(src, dest)
|
err = defaultCopyWithTar(src, dest)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("archiver.CopyWithTar should throw an error on invalid dest.")
|
t.Fatalf("archiver.CopyWithTar should throw an error on invalid dest.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,13 +394,8 @@ func ChangesSize(newDir string, changes []Change) int64 {
|
||||||
func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMap) (io.ReadCloser, error) {
|
func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMap) (io.ReadCloser, error) {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
go func() {
|
go func() {
|
||||||
ta := &tarAppender{
|
ta := newTarAppender(idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), writer)
|
||||||
TarWriter: tar.NewWriter(writer),
|
|
||||||
Buffer: pools.BufioWriter32KPool.Get(nil),
|
|
||||||
SeenFiles: make(map[uint64]string),
|
|
||||||
UIDMaps: uidMaps,
|
|
||||||
GIDMaps: gidMaps,
|
|
||||||
}
|
|
||||||
// this buffer is needed for the duration of this piped stream
|
// this buffer is needed for the duration of this piped stream
|
||||||
defer pools.BufioWriter32KPool.Put(ta.Buffer)
|
defer pools.BufioWriter32KPool.Put(ta.Buffer)
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||||
if options.ExcludePatterns == nil {
|
if options.ExcludePatterns == nil {
|
||||||
options.ExcludePatterns = []string{}
|
options.ExcludePatterns = []string{}
|
||||||
}
|
}
|
||||||
remappedRootUID, remappedRootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps)
|
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
aufsTempdir := ""
|
aufsTempdir := ""
|
||||||
aufsHardlinks := make(map[string]*tar.Header)
|
aufsHardlinks := make(map[string]*tar.Header)
|
||||||
|
@ -195,27 +192,10 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||||
srcData = tmpFile
|
srcData = tmpFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the options contain a uid & gid maps, convert header uid/gid
|
if err := remapIDs(idMappings, srcHdr); err != nil {
|
||||||
// entries using the maps such that lchown sets the proper mapped
|
return 0, err
|
||||||
// uid/gid after writing the file. We only perform this mapping if
|
|
||||||
// the file isn't already owned by the remapped root UID or GID, as
|
|
||||||
// that specific uid/gid has no mapping from container -> host, and
|
|
||||||
// those files already have the proper ownership for inside the
|
|
||||||
// container.
|
|
||||||
if srcHdr.Uid != remappedRootUID {
|
|
||||||
xUID, err := idtools.ToHost(srcHdr.Uid, options.UIDMaps)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
srcHdr.Uid = xUID
|
|
||||||
}
|
|
||||||
if srcHdr.Gid != remappedRootGID {
|
|
||||||
xGID, err := idtools.ToHost(srcHdr.Gid, options.GIDMaps)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
srcHdr.Gid = xGID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := createTarFile(path, dest, srcHdr, srcData, true, nil, options.InUserNS); err != nil {
|
if err := createTarFile(path, dest, srcHdr, srcData, true, nil, options.InUserNS); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,13 @@ import (
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
)
|
)
|
||||||
|
|
||||||
var chrootArchiver = &archive.Archiver{Untar: Untar}
|
// NewArchiver returns a new Archiver which uses chrootarchive.Untar
|
||||||
|
func NewArchiver(idMappings *idtools.IDMappings) *archive.Archiver {
|
||||||
|
if idMappings == nil {
|
||||||
|
idMappings = &idtools.IDMappings{}
|
||||||
|
}
|
||||||
|
return &archive.Archiver{Untar: Untar, IDMappings: idMappings}
|
||||||
|
}
|
||||||
|
|
||||||
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
||||||
// and unpacks it into the directory at `dest`.
|
// and unpacks it into the directory at `dest`.
|
||||||
|
@ -30,7 +36,6 @@ func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOp
|
||||||
|
|
||||||
// Handler for teasing out the automatic decompression
|
// Handler for teasing out the automatic decompression
|
||||||
func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool) error {
|
func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool) error {
|
||||||
|
|
||||||
if tarArchive == nil {
|
if tarArchive == nil {
|
||||||
return fmt.Errorf("Empty archive")
|
return fmt.Errorf("Empty archive")
|
||||||
}
|
}
|
||||||
|
@ -41,14 +46,12 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions
|
||||||
options.ExcludePatterns = []string{}
|
options.ExcludePatterns = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps)
|
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||||
if err != nil {
|
rootIDs := idMappings.RootPair()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dest = filepath.Clean(dest)
|
dest = filepath.Clean(dest)
|
||||||
if _, err := os.Stat(dest); os.IsNotExist(err) {
|
if _, err := os.Stat(dest); os.IsNotExist(err) {
|
||||||
if err := idtools.MkdirAllNewAs(dest, 0755, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,33 +68,3 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions
|
||||||
|
|
||||||
return invokeUnpack(r, dest, options)
|
return invokeUnpack(r, dest, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
|
|
||||||
// If either Tar or Untar fails, TarUntar aborts and returns the error.
|
|
||||||
func TarUntar(src, dst string) error {
|
|
||||||
return chrootArchiver.TarUntar(src, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 CopyWithTar(src, dst string) error {
|
|
||||||
return chrootArchiver.CopyWithTar(src, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
|
||||||
// for a single file. It copies a regular file from path `src` to
|
|
||||||
// path `dst`, and preserves all its metadata.
|
|
||||||
//
|
|
||||||
// If `dst` ends with a trailing slash '/' ('\' on Windows), the final
|
|
||||||
// destination path will be `dst/base(src)` or `dst\base(src)`
|
|
||||||
func CopyFileWithTar(src, dst string) (err error) {
|
|
||||||
return chrootArchiver.CopyFileWithTar(src, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UntarPath is a convenience function which looks for an archive
|
|
||||||
// at filesystem path `src`, and unpacks it at `dst`.
|
|
||||||
func UntarPath(src, dst string) error {
|
|
||||||
return chrootArchiver.UntarPath(src, dst)
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,6 +22,24 @@ func init() {
|
||||||
reexec.Init()
|
reexec.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var chrootArchiver = NewArchiver(nil)
|
||||||
|
|
||||||
|
func TarUntar(src, dst string) error {
|
||||||
|
return chrootArchiver.TarUntar(src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CopyFileWithTar(src, dst string) (err error) {
|
||||||
|
return chrootArchiver.CopyFileWithTar(src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UntarPath(src, dst string) error {
|
||||||
|
return chrootArchiver.UntarPath(src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CopyWithTar(src, dst string) error {
|
||||||
|
return chrootArchiver.CopyWithTar(src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
func TestChrootTarUntar(t *testing.T) {
|
func TestChrootTarUntar(t *testing.T) {
|
||||||
tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntar")
|
tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntar")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -37,49 +37,56 @@ const (
|
||||||
// MkdirAllAs creates a directory (include any along the path) and then modifies
|
// MkdirAllAs creates a directory (include any along the path) and then modifies
|
||||||
// ownership to the requested uid/gid. If the directory already exists, this
|
// ownership to the requested uid/gid. If the directory already exists, this
|
||||||
// function will still change ownership to the requested uid/gid pair.
|
// function will still change ownership to the requested uid/gid pair.
|
||||||
|
// Deprecated: Use MkdirAllAndChown
|
||||||
func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
|
func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
|
||||||
return mkdirAs(path, mode, ownerUID, ownerGID, true, true)
|
return mkdirAs(path, mode, ownerUID, ownerGID, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkdirAllNewAs creates a directory (include any along the path) and then modifies
|
|
||||||
// ownership ONLY of newly created directories to the requested uid/gid. If the
|
|
||||||
// directories along the path exist, no change of ownership will be performed
|
|
||||||
func MkdirAllNewAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
|
|
||||||
return mkdirAs(path, mode, ownerUID, ownerGID, true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MkdirAs creates a directory and then modifies ownership to the requested uid/gid.
|
// MkdirAs creates a directory and then modifies ownership to the requested uid/gid.
|
||||||
// If the directory already exists, this function still changes ownership
|
// If the directory already exists, this function still changes ownership
|
||||||
|
// Deprecated: Use MkdirAndChown with a IDPair
|
||||||
func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
|
func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
|
||||||
return mkdirAs(path, mode, ownerUID, ownerGID, false, true)
|
return mkdirAs(path, mode, ownerUID, ownerGID, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
||||||
|
// ownership to the requested uid/gid. If the directory already exists, this
|
||||||
|
// function will still change ownership to the requested uid/gid pair.
|
||||||
|
func MkdirAllAndChown(path string, mode os.FileMode, ids IDPair) error {
|
||||||
|
return mkdirAs(path, mode, ids.UID, ids.GID, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
|
||||||
|
// If the directory already exists, this function still changes ownership
|
||||||
|
func MkdirAndChown(path string, mode os.FileMode, ids IDPair) error {
|
||||||
|
return mkdirAs(path, mode, ids.UID, ids.GID, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
|
||||||
|
// ownership ONLY of newly created directories to the requested uid/gid. If the
|
||||||
|
// directories along the path exist, no change of ownership will be performed
|
||||||
|
func MkdirAllAndChownNew(path string, mode os.FileMode, ids IDPair) error {
|
||||||
|
return mkdirAs(path, mode, ids.UID, ids.GID, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
// GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
|
// GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
|
||||||
// If the maps are empty, then the root uid/gid will default to "real" 0/0
|
// If the maps are empty, then the root uid/gid will default to "real" 0/0
|
||||||
func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
|
func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
|
||||||
var uid, gid int
|
uid, err := toHost(0, uidMap)
|
||||||
|
if err != nil {
|
||||||
if uidMap != nil {
|
return -1, -1, err
|
||||||
xUID, err := ToHost(0, uidMap)
|
|
||||||
if err != nil {
|
|
||||||
return -1, -1, err
|
|
||||||
}
|
|
||||||
uid = xUID
|
|
||||||
}
|
}
|
||||||
if gidMap != nil {
|
gid, err := toHost(0, gidMap)
|
||||||
xGID, err := ToHost(0, gidMap)
|
if err != nil {
|
||||||
if err != nil {
|
return -1, -1, err
|
||||||
return -1, -1, err
|
|
||||||
}
|
|
||||||
gid = xGID
|
|
||||||
}
|
}
|
||||||
return uid, gid, nil
|
return uid, gid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToContainer takes an id mapping, and uses it to translate a
|
// toContainer takes an id mapping, and uses it to translate a
|
||||||
// host ID to the remapped ID. If no map is provided, then the translation
|
// host ID to the remapped ID. If no map is provided, then the translation
|
||||||
// assumes a 1-to-1 mapping and returns the passed in id
|
// assumes a 1-to-1 mapping and returns the passed in id
|
||||||
func ToContainer(hostID int, idMap []IDMap) (int, error) {
|
func toContainer(hostID int, idMap []IDMap) (int, error) {
|
||||||
if idMap == nil {
|
if idMap == nil {
|
||||||
return hostID, nil
|
return hostID, nil
|
||||||
}
|
}
|
||||||
|
@ -92,10 +99,10 @@ func ToContainer(hostID int, idMap []IDMap) (int, error) {
|
||||||
return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID)
|
return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToHost takes an id mapping and a remapped ID, and translates the
|
// toHost takes an id mapping and a remapped ID, and translates the
|
||||||
// ID to the mapped host ID. If no map is provided, then the translation
|
// ID to the mapped host ID. If no map is provided, then the translation
|
||||||
// assumes a 1-to-1 mapping and returns the passed in id #
|
// assumes a 1-to-1 mapping and returns the passed in id #
|
||||||
func ToHost(contID int, idMap []IDMap) (int, error) {
|
func toHost(contID int, idMap []IDMap) (int, error) {
|
||||||
if idMap == nil {
|
if idMap == nil {
|
||||||
return contID, nil
|
return contID, nil
|
||||||
}
|
}
|
||||||
|
@ -108,26 +115,101 @@ func ToHost(contID int, idMap []IDMap) (int, error) {
|
||||||
return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID)
|
return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateIDMappings takes a requested user and group name and
|
// IDPair is a UID and GID pair
|
||||||
|
type IDPair struct {
|
||||||
|
UID int
|
||||||
|
GID int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDMappings contains a mappings of UIDs and GIDs
|
||||||
|
type IDMappings struct {
|
||||||
|
uids []IDMap
|
||||||
|
gids []IDMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIDMappings takes a requested user and group name and
|
||||||
// using the data from /etc/sub{uid,gid} ranges, creates the
|
// using the data from /etc/sub{uid,gid} ranges, creates the
|
||||||
// proper uid and gid remapping ranges for that user/group pair
|
// proper uid and gid remapping ranges for that user/group pair
|
||||||
func CreateIDMappings(username, groupname string) ([]IDMap, []IDMap, error) {
|
func NewIDMappings(username, groupname string) (*IDMappings, error) {
|
||||||
subuidRanges, err := parseSubuid(username)
|
subuidRanges, err := parseSubuid(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
subgidRanges, err := parseSubgid(groupname)
|
subgidRanges, err := parseSubgid(groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(subuidRanges) == 0 {
|
if len(subuidRanges) == 0 {
|
||||||
return nil, nil, fmt.Errorf("No subuid ranges found for user %q", username)
|
return nil, fmt.Errorf("No subuid ranges found for user %q", username)
|
||||||
}
|
}
|
||||||
if len(subgidRanges) == 0 {
|
if len(subgidRanges) == 0 {
|
||||||
return nil, nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
|
return nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
|
||||||
}
|
}
|
||||||
|
|
||||||
return createIDMap(subuidRanges), createIDMap(subgidRanges), nil
|
return &IDMappings{
|
||||||
|
uids: createIDMap(subuidRanges),
|
||||||
|
gids: createIDMap(subgidRanges),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIDMappingsFromMaps creates a new mapping from two slices
|
||||||
|
// Deprecated: this is a temporary shim while transitioning to IDMapping
|
||||||
|
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IDMappings {
|
||||||
|
return &IDMappings{uids: uids, gids: gids}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RootPair returns a uid and gid pair for the root user. The error is ignored
|
||||||
|
// because a root user always exists, and the defaults are correct when the uid
|
||||||
|
// and gid maps are empty.
|
||||||
|
func (i *IDMappings) RootPair() IDPair {
|
||||||
|
uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
|
||||||
|
return IDPair{UID: uid, GID: gid}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToHost returns the host UID and GID for the container uid, gid.
|
||||||
|
// Remapping is only performed if the ids aren't already the remapped root ids
|
||||||
|
func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) {
|
||||||
|
var err error
|
||||||
|
target := i.RootPair()
|
||||||
|
|
||||||
|
if pair.UID != target.UID {
|
||||||
|
target.UID, err = toHost(pair.UID, i.uids)
|
||||||
|
if err != nil {
|
||||||
|
return target, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pair.GID != target.GID {
|
||||||
|
target.GID, err = toHost(pair.GID, i.gids)
|
||||||
|
}
|
||||||
|
return target, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToContainer returns the container UID and GID for the host uid and gid
|
||||||
|
func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) {
|
||||||
|
uid, err := toContainer(pair.UID, i.uids)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
gid, err := toContainer(pair.GID, i.gids)
|
||||||
|
return uid, gid, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns true if there are no id mappings
|
||||||
|
func (i *IDMappings) Empty() bool {
|
||||||
|
return len(i.uids) == 0 && len(i.gids) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UIDs return the UID mapping
|
||||||
|
// TODO: remove this once everything has been refactored to use pairs
|
||||||
|
func (i *IDMappings) UIDs() []IDMap {
|
||||||
|
return i.uids
|
||||||
|
}
|
||||||
|
|
||||||
|
// GIDs return the UID mapping
|
||||||
|
// TODO: remove this once everything has been refactored to use pairs
|
||||||
|
func (i *IDMappings) GIDs() []IDMap {
|
||||||
|
return i.gids
|
||||||
}
|
}
|
||||||
|
|
||||||
func createIDMap(subidRanges ranges) []IDMap {
|
func createIDMap(subidRanges ranges) []IDMap {
|
||||||
|
|
|
@ -69,15 +69,15 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
|
||||||
|
|
||||||
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
||||||
// if that uid, gid pair has access (execute bit) to the directory
|
// if that uid, gid pair has access (execute bit) to the directory
|
||||||
func CanAccess(path string, uid, gid int) bool {
|
func CanAccess(path string, pair IDPair) bool {
|
||||||
statInfo, err := system.Stat(path)
|
statInfo, err := system.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
fileMode := os.FileMode(statInfo.Mode())
|
fileMode := os.FileMode(statInfo.Mode())
|
||||||
permBits := fileMode.Perm()
|
permBits := fileMode.Perm()
|
||||||
return accessible(statInfo.UID() == uint32(uid),
|
return accessible(statInfo.UID() == uint32(pair.UID),
|
||||||
statInfo.GID() == uint32(gid), permBits)
|
statInfo.GID() == uint32(pair.GID), permBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
func accessible(isOwner, isGroup bool, perms os.FileMode) bool {
|
func accessible(isOwner, isGroup bool, perms os.FileMode) bool {
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
|
@ -76,12 +78,9 @@ func TestMkdirAllAs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMkdirAllNewAs(t *testing.T) {
|
func TestMkdirAllAndChownNew(t *testing.T) {
|
||||||
|
|
||||||
dirName, err := ioutil.TempDir("", "mkdirnew")
|
dirName, err := ioutil.TempDir("", "mkdirnew")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("Couldn't create temp dir: %v", err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
defer os.RemoveAll(dirName)
|
||||||
|
|
||||||
testTree := map[string]node{
|
testTree := map[string]node{
|
||||||
|
@ -91,49 +90,32 @@ func TestMkdirAllNewAs(t *testing.T) {
|
||||||
"lib/x86_64": {45, 45},
|
"lib/x86_64": {45, 45},
|
||||||
"lib/x86_64/share": {1, 1},
|
"lib/x86_64/share": {1, 1},
|
||||||
}
|
}
|
||||||
|
require.NoError(t, buildTree(dirName, testTree))
|
||||||
if err := buildTree(dirName, testTree); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
|
// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
|
||||||
if err := MkdirAllNewAs(filepath.Join(dirName, "usr", "share"), 0755, 99, 99); err != nil {
|
err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{99, 99})
|
||||||
t.Fatal(err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
testTree["usr/share"] = node{99, 99}
|
testTree["usr/share"] = node{99, 99}
|
||||||
verifyTree, err := readTree(dirName, "")
|
verifyTree, err := readTree(dirName, "")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
require.NoError(t, compareTrees(testTree, verifyTree))
|
||||||
}
|
|
||||||
if err := compareTrees(testTree, verifyTree); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 2-deep new directories--both should be owned by the uid/gid pair
|
// test 2-deep new directories--both should be owned by the uid/gid pair
|
||||||
if err := MkdirAllNewAs(filepath.Join(dirName, "lib", "some", "other"), 0755, 101, 101); err != nil {
|
err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{101, 101})
|
||||||
t.Fatal(err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
testTree["lib/some"] = node{101, 101}
|
testTree["lib/some"] = node{101, 101}
|
||||||
testTree["lib/some/other"] = node{101, 101}
|
testTree["lib/some/other"] = node{101, 101}
|
||||||
verifyTree, err = readTree(dirName, "")
|
verifyTree, err = readTree(dirName, "")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
require.NoError(t, compareTrees(testTree, verifyTree))
|
||||||
}
|
|
||||||
if err := compareTrees(testTree, verifyTree); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// test a directory that already exists; should NOT be chowned
|
// test a directory that already exists; should NOT be chowned
|
||||||
if err := MkdirAllNewAs(filepath.Join(dirName, "usr"), 0755, 102, 102); err != nil {
|
err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{102, 102})
|
||||||
t.Fatal(err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
verifyTree, err = readTree(dirName, "")
|
verifyTree, err = readTree(dirName, "")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
require.NoError(t, compareTrees(testTree, verifyTree))
|
||||||
}
|
|
||||||
if err := compareTrees(testTree, verifyTree); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMkdirAs(t *testing.T) {
|
func TestMkdirAs(t *testing.T) {
|
||||||
|
|
|
@ -20,6 +20,6 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
|
||||||
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
||||||
// if that uid, gid pair has access (execute bit) to the directory
|
// if that uid, gid pair has access (execute bit) to the directory
|
||||||
// Windows does not require/support this function, so always return true
|
// Windows does not require/support this function, so always return true
|
||||||
func CanAccess(path string, uid, gid int) bool {
|
func CanAccess(path string, pair IDPair) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/daemon/initlayer"
|
"github.com/docker/docker/daemon/initlayer"
|
||||||
"github.com/docker/docker/libcontainerd"
|
"github.com/docker/docker/libcontainerd"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/docker/docker/pkg/plugins"
|
"github.com/docker/docker/pkg/plugins"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
@ -58,7 +59,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initlayer.Setup(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName), 0, 0); err != nil {
|
if err := initlayer.Setup(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName), idtools.IDPair{0, 0}); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,10 +55,10 @@ type activeMount struct {
|
||||||
// New instantiates a new Root instance with the provided scope. Scope
|
// New instantiates a new Root instance with the provided scope. Scope
|
||||||
// is the base path that the Root instance uses to store its
|
// is the base path that the Root instance uses to store its
|
||||||
// volumes. The base path is created here if it does not exist.
|
// volumes. The base path is created here if it does not exist.
|
||||||
func New(scope string, rootUID, rootGID int) (*Root, error) {
|
func New(scope string, rootIDs idtools.IDPair) (*Root, error) {
|
||||||
rootDirectory := filepath.Join(scope, volumesPathName)
|
rootDirectory := filepath.Join(scope, volumesPathName)
|
||||||
|
|
||||||
if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIDs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,8 +66,7 @@ func New(scope string, rootUID, rootGID int) (*Root, error) {
|
||||||
scope: scope,
|
scope: scope,
|
||||||
path: rootDirectory,
|
path: rootDirectory,
|
||||||
volumes: make(map[string]*localVolume),
|
volumes: make(map[string]*localVolume),
|
||||||
rootUID: rootUID,
|
rootIDs: rootIDs,
|
||||||
rootGID: rootGID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dirs, err := ioutil.ReadDir(rootDirectory)
|
dirs, err := ioutil.ReadDir(rootDirectory)
|
||||||
|
@ -125,8 +124,7 @@ type Root struct {
|
||||||
scope string
|
scope string
|
||||||
path string
|
path string
|
||||||
volumes map[string]*localVolume
|
volumes map[string]*localVolume
|
||||||
rootUID int
|
rootIDs idtools.IDPair
|
||||||
rootGID int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List lists all the volumes
|
// List lists all the volumes
|
||||||
|
@ -167,7 +165,7 @@ func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error
|
||||||
}
|
}
|
||||||
|
|
||||||
path := r.DataPath(name)
|
path := r.DataPath(name)
|
||||||
if err := idtools.MkdirAllAs(path, 0755, r.rootUID, r.rootGID); err != nil {
|
if err := idtools.MkdirAllAndChown(path, 0755, r.rootIDs); err != nil {
|
||||||
if os.IsExist(err) {
|
if os.IsExist(err) {
|
||||||
return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
|
return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ func TestRemove(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, 0, 0)
|
r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +83,7 @@ func TestInitializeWithVolumes(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, 0, 0)
|
r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +93,7 @@ func TestInitializeWithVolumes(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = New(rootDir, 0, 0)
|
r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -114,7 +115,7 @@ func TestCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, 0, 0)
|
r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -151,7 +152,7 @@ func TestCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = New(rootDir, 0, 0)
|
r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -189,7 +190,7 @@ func TestCreateWithOpts(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, 0, 0)
|
r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -270,7 +271,7 @@ func TestCreateWithOpts(t *testing.T) {
|
||||||
t.Fatal("expected mount to still be active")
|
t.Fatal("expected mount to still be active")
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = New(rootDir, 0, 0)
|
r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -292,7 +293,7 @@ func TestRealodNoOpts(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, 0, 0)
|
r, err := New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -320,7 +321,7 @@ func TestRealodNoOpts(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = New(rootDir, 0, 0)
|
r, err = New(rootDir, idtools.IDPair{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ func (m *MountPoint) Cleanup() error {
|
||||||
// configured, or creating the source directory if supplied.
|
// configured, or creating the source directory if supplied.
|
||||||
// The, optional, checkFun parameter allows doing additional checking
|
// The, optional, checkFun parameter allows doing additional checking
|
||||||
// before creating the source directory on the host.
|
// before creating the source directory on the host.
|
||||||
func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int, checkFun func(m *MountPoint) error) (path string, err error) {
|
func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if label.RelabelNeeded(m.Mode) {
|
if label.RelabelNeeded(m.Mode) {
|
||||||
|
@ -196,7 +196,7 @@ func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int, checkFun fun
|
||||||
}
|
}
|
||||||
// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
|
// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
|
||||||
// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
|
// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
|
||||||
if err := idtools.MkdirAllNewAs(m.Source, 0755, rootUID, rootGID); err != nil {
|
if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil {
|
||||||
if perr, ok := err.(*os.PathError); ok {
|
if perr, ok := err.(*os.PathError); ok {
|
||||||
if perr.Err != syscall.ENOTDIR {
|
if perr.Err != syscall.ENOTDIR {
|
||||||
return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)
|
return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue