Add ADD/COPY --chown flag support to Windows
This implements chown support on Windows. Built-in accounts as well as accounts included in the SAM database of the container are supported. NOTE: IDPair is now named Identity and IDMappings is now named IdentityMapping. The following are valid examples: ADD --chown=Guest . <some directory> COPY --chown=Administrator . <some directory> COPY --chown=Guests . <some directory> COPY --chown=ContainerUser . <some directory> On Windows an owner is only granted the permission to read the security descriptor and read/write the discretionary access control list. This fix also grants read/write and execute permissions to the owner. Signed-off-by: Salahuddin Khan <salah@docker.com>
This commit is contained in:
parent
1fd7e4c28d
commit
763d839261
64 changed files with 610 additions and 301 deletions
|
@ -56,21 +56,21 @@ type SessionGetter interface {
|
||||||
|
|
||||||
// BuildManager is shared across all Builder objects
|
// BuildManager is shared across all Builder objects
|
||||||
type BuildManager struct {
|
type BuildManager struct {
|
||||||
idMappings *idtools.IDMappings
|
idMapping *idtools.IdentityMapping
|
||||||
backend builder.Backend
|
backend builder.Backend
|
||||||
pathCache pathCache // TODO: make this persistent
|
pathCache pathCache // TODO: make this persistent
|
||||||
sg SessionGetter
|
sg SessionGetter
|
||||||
fsCache *fscache.FSCache
|
fsCache *fscache.FSCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuildManager creates a BuildManager
|
// NewBuildManager creates a BuildManager
|
||||||
func NewBuildManager(b builder.Backend, sg SessionGetter, fsCache *fscache.FSCache, idMappings *idtools.IDMappings) (*BuildManager, error) {
|
func NewBuildManager(b builder.Backend, sg SessionGetter, fsCache *fscache.FSCache, identityMapping *idtools.IdentityMapping) (*BuildManager, error) {
|
||||||
bm := &BuildManager{
|
bm := &BuildManager{
|
||||||
backend: b,
|
backend: b,
|
||||||
pathCache: &syncmap.Map{},
|
pathCache: &syncmap.Map{},
|
||||||
sg: sg,
|
sg: sg,
|
||||||
idMappings: idMappings,
|
idMapping: identityMapping,
|
||||||
fsCache: fsCache,
|
fsCache: fsCache,
|
||||||
}
|
}
|
||||||
if err := fsCache.RegisterTransport(remotecontext.ClientSessionRemote, NewClientSessionTransport()); err != nil {
|
if err := fsCache.RegisterTransport(remotecontext.ClientSessionRemote, NewClientSessionTransport()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -111,7 +111,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
|
||||||
ProgressWriter: config.ProgressWriter,
|
ProgressWriter: config.ProgressWriter,
|
||||||
Backend: bm.backend,
|
Backend: bm.backend,
|
||||||
PathCache: bm.pathCache,
|
PathCache: bm.pathCache,
|
||||||
IDMappings: bm.idMappings,
|
IDMapping: bm.idMapping,
|
||||||
}
|
}
|
||||||
b, err := newBuilder(ctx, builderOptions)
|
b, err := newBuilder(ctx, builderOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -159,7 +159,7 @@ type builderOptions struct {
|
||||||
Backend builder.Backend
|
Backend builder.Backend
|
||||||
ProgressWriter backend.ProgressWriter
|
ProgressWriter backend.ProgressWriter
|
||||||
PathCache pathCache
|
PathCache pathCache
|
||||||
IDMappings *idtools.IDMappings
|
IDMapping *idtools.IdentityMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builder is a Dockerfile builder
|
// Builder is a Dockerfile builder
|
||||||
|
@ -175,7 +175,7 @@ type Builder struct {
|
||||||
docker builder.Backend
|
docker builder.Backend
|
||||||
clientCtx context.Context
|
clientCtx context.Context
|
||||||
|
|
||||||
idMappings *idtools.IDMappings
|
idMapping *idtools.IdentityMapping
|
||||||
disableCommit bool
|
disableCommit bool
|
||||||
imageSources *imageSources
|
imageSources *imageSources
|
||||||
pathCache pathCache
|
pathCache pathCache
|
||||||
|
@ -199,7 +199,7 @@ func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, er
|
||||||
Aux: options.ProgressWriter.AuxFormatter,
|
Aux: options.ProgressWriter.AuxFormatter,
|
||||||
Output: options.ProgressWriter.Output,
|
Output: options.ProgressWriter.Output,
|
||||||
docker: options.Backend,
|
docker: options.Backend,
|
||||||
idMappings: options.IDMappings,
|
idMapping: options.IDMapping,
|
||||||
imageSources: newImageSources(clientCtx, options),
|
imageSources: newImageSources(clientCtx, options),
|
||||||
pathCache: options.PathCache,
|
pathCache: options.PathCache,
|
||||||
imageProber: newImageProber(options.Backend, config.CacheFrom, config.NoCache),
|
imageProber: newImageProber(options.Backend, config.CacheFrom, config.NoCache),
|
||||||
|
|
|
@ -451,7 +451,7 @@ func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote b
|
||||||
|
|
||||||
type copyFileOptions struct {
|
type copyFileOptions struct {
|
||||||
decompress bool
|
decompress bool
|
||||||
chownPair idtools.IDPair
|
identity idtools.Identity
|
||||||
archiver Archiver
|
archiver Archiver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,7 +481,7 @@ func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions)
|
||||||
return errors.Wrapf(err, "source path not found")
|
return errors.Wrapf(err, "source path not found")
|
||||||
}
|
}
|
||||||
if src.IsDir() {
|
if src.IsDir() {
|
||||||
return copyDirectory(archiver, srcEndpoint, destEndpoint, options.chownPair)
|
return copyDirectory(archiver, srcEndpoint, destEndpoint, options.identity)
|
||||||
}
|
}
|
||||||
if options.decompress && isArchivePath(source.root, srcPath) && !source.noDecompress {
|
if options.decompress && isArchivePath(source.root, srcPath) && !source.noDecompress {
|
||||||
return archiver.UntarPath(srcPath, destPath)
|
return archiver.UntarPath(srcPath, destPath)
|
||||||
|
@ -499,7 +499,7 @@ func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions)
|
||||||
destPath = dest.root.Join(destPath, source.root.Base(source.path))
|
destPath = dest.root.Join(destPath, source.root.Base(source.path))
|
||||||
destEndpoint = ©Endpoint{driver: dest.root, path: destPath}
|
destEndpoint = ©Endpoint{driver: dest.root, path: destPath}
|
||||||
}
|
}
|
||||||
return copyFile(archiver, srcEndpoint, destEndpoint, options.chownPair)
|
return copyFile(archiver, srcEndpoint, destEndpoint, options.identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isArchivePath(driver containerfs.ContainerFS, path string) bool {
|
func isArchivePath(driver containerfs.ContainerFS, path string) bool {
|
||||||
|
@ -517,7 +517,7 @@ func isArchivePath(driver containerfs.ContainerFS, path string) bool {
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyDirectory(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.IDPair) error {
|
func copyDirectory(archiver Archiver, source, dest *copyEndpoint, identity idtools.Identity) error {
|
||||||
destExists, err := isExistingDirectory(dest)
|
destExists, err := isExistingDirectory(dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to query destination path")
|
return errors.Wrapf(err, "failed to query destination path")
|
||||||
|
@ -527,17 +527,17 @@ func copyDirectory(archiver Archiver, source, dest *copyEndpoint, chownPair idto
|
||||||
return errors.Wrapf(err, "failed to copy directory")
|
return errors.Wrapf(err, "failed to copy directory")
|
||||||
}
|
}
|
||||||
// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
|
// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
|
||||||
return fixPermissions(source.path, dest.path, chownPair, !destExists)
|
return fixPermissions(source.path, dest.path, identity, !destExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyFile(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.IDPair) error {
|
func copyFile(archiver Archiver, source, dest *copyEndpoint, identity idtools.Identity) error {
|
||||||
if runtime.GOOS == "windows" && dest.driver.OS() == "linux" {
|
if runtime.GOOS == "windows" && dest.driver.OS() == "linux" {
|
||||||
// LCOW
|
// LCOW
|
||||||
if err := dest.driver.MkdirAll(dest.driver.Dir(dest.path), 0755); err != nil {
|
if err := dest.driver.MkdirAll(dest.driver.Dir(dest.path), 0755); err != nil {
|
||||||
return errors.Wrapf(err, "failed to create new directory")
|
return errors.Wrapf(err, "failed to create new directory")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, chownPair); err != nil {
|
if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, identity); err != nil {
|
||||||
// Normal containers
|
// Normal containers
|
||||||
return errors.Wrapf(err, "failed to create new directory")
|
return errors.Wrapf(err, "failed to create new directory")
|
||||||
}
|
}
|
||||||
|
@ -547,7 +547,7 @@ func copyFile(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.I
|
||||||
return errors.Wrapf(err, "failed to copy file")
|
return errors.Wrapf(err, "failed to copy file")
|
||||||
}
|
}
|
||||||
// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
|
// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
|
||||||
return fixPermissions(source.path, dest.path, chownPair, false)
|
return fixPermissions(source.path, dest.path, identity, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func endsInSlash(driver containerfs.Driver, path string) bool {
|
func endsInSlash(driver containerfs.Driver, path string) bool {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fixPermissions(source, destination string, rootIDs idtools.IDPair, overrideSkip bool) error {
|
func fixPermissions(source, destination string, identity idtools.Identity, overrideSkip bool) error {
|
||||||
var (
|
var (
|
||||||
skipChownRoot bool
|
skipChownRoot bool
|
||||||
err error
|
err error
|
||||||
|
@ -39,7 +39,7 @@ func fixPermissions(source, destination string, rootIDs idtools.IDPair, override
|
||||||
}
|
}
|
||||||
|
|
||||||
fullpath = filepath.Join(destination, cleaned)
|
fullpath = filepath.Join(destination, cleaned)
|
||||||
return os.Lchown(fullpath, rootIDs.UID, rootIDs.GID)
|
return os.Lchown(fullpath, identity.UID, identity.GID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
"github.com/docker/docker/pkg/reexec"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pathBlacklist = map[string]bool{
|
var pathBlacklist = map[string]bool{
|
||||||
|
@ -13,9 +19,69 @@ var pathBlacklist = map[string]bool{
|
||||||
"c:\\windows": true,
|
"c:\\windows": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixPermissions(source, destination string, rootIDs idtools.IDPair, overrideSkip bool) error {
|
func init() {
|
||||||
// chown is not supported on Windows
|
reexec.Register("windows-fix-permissions", fixPermissionsReexec)
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func fixPermissions(source, destination string, identity idtools.Identity, _ bool) error {
|
||||||
|
if identity.SID == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := reexec.Command("windows-fix-permissions", source, destination, identity.SID)
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
return errors.Wrapf(err, "failed to exec windows-fix-permissions: %s", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixPermissionsReexec() {
|
||||||
|
err := fixPermissionsWindows(os.Args[1], os.Args[2], os.Args[3])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprint(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixPermissionsWindows(source, destination, SID string) error {
|
||||||
|
|
||||||
|
privileges := []string{winio.SeRestorePrivilege, system.SeTakeOwnershipPrivilege}
|
||||||
|
|
||||||
|
err := winio.EnableProcessPrivileges(privileges)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer winio.DisableProcessPrivileges(privileges)
|
||||||
|
|
||||||
|
sid, err := windows.StringToSid(SID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owners on *nix have read/write/delete/read control and write DAC.
|
||||||
|
// Add an ACE that grants this to the user/group specified with the
|
||||||
|
// chown option. Currently Windows is not honoring the owner change,
|
||||||
|
// however, they are aware of this and it should be fixed at some
|
||||||
|
// point.
|
||||||
|
|
||||||
|
sddlString := system.SddlAdministratorsLocalSystem
|
||||||
|
sddlString += "(A;OICI;GRGWGXRCWDSD;;;" + SID + ")"
|
||||||
|
|
||||||
|
securityDescriptor, err := winio.SddlToSecurityDescriptor(sddlString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var daclPresent uint32
|
||||||
|
var daclDefaulted uint32
|
||||||
|
var dacl *byte
|
||||||
|
|
||||||
|
err = system.GetSecurityDescriptorDacl(&securityDescriptor[0], &daclPresent, &dacl, &daclDefaulted)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return system.SetNamedSecurityInfo(windows.StringToUTF16Ptr(destination), system.SE_FILE_OBJECT, system.OWNER_SECURITY_INFORMATION|system.DACL_SECURITY_INFORMATION, sid, nil, dacl, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error {
|
func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error {
|
||||||
|
|
|
@ -37,7 +37,7 @@ type Archiver interface {
|
||||||
UntarPath(src, dst string) error
|
UntarPath(src, dst string) error
|
||||||
CopyWithTar(src, dst string) error
|
CopyWithTar(src, dst string) error
|
||||||
CopyFileWithTar(src, dst string) error
|
CopyFileWithTar(src, dst string) error
|
||||||
IDMappings() *idtools.IDMappings
|
IdentityMapping() *idtools.IdentityMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
// The builder will use the following interfaces if the container fs implements
|
// The builder will use the following interfaces if the container fs implements
|
||||||
|
@ -68,11 +68,11 @@ func tarFunc(i interface{}) containerfs.TarFunc {
|
||||||
func (b *Builder) getArchiver(src, dst containerfs.Driver) Archiver {
|
func (b *Builder) getArchiver(src, dst containerfs.Driver) Archiver {
|
||||||
t, u := tarFunc(src), untarFunc(dst)
|
t, u := tarFunc(src), untarFunc(dst)
|
||||||
return &containerfs.Archiver{
|
return &containerfs.Archiver{
|
||||||
SrcDriver: src,
|
SrcDriver: src,
|
||||||
DstDriver: dst,
|
DstDriver: dst,
|
||||||
Tar: t,
|
Tar: t,
|
||||||
Untar: u,
|
Untar: u,
|
||||||
IDMappingsVar: b.idMappings,
|
IDMapping: b.idMapping,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,14 +185,18 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
chownPair := b.idMappings.RootPair()
|
identity := b.idMapping.RootPair()
|
||||||
// if a chown was requested, perform the steps to get the uid, gid
|
// if a chown was requested, perform the steps to get the uid, gid
|
||||||
// translated (if necessary because of user namespaces), and replace
|
// translated (if necessary because of user namespaces), and replace
|
||||||
// the root pair with the chown pair for copy operations
|
// the root pair with the chown pair for copy operations
|
||||||
if inst.chownStr != "" {
|
if inst.chownStr != "" {
|
||||||
chownPair, err = parseChownFlag(inst.chownStr, destInfo.root.Path(), b.idMappings)
|
identity, err = parseChownFlag(b, state, inst.chownStr, destInfo.root.Path(), b.idMapping)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
|
if b.options.Platform != "windows" {
|
||||||
|
return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Wrapf(err, "unable to map container user account name to SID")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +204,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
|
||||||
opts := copyFileOptions{
|
opts := copyFileOptions{
|
||||||
decompress: inst.allowLocalDecompression,
|
decompress: inst.allowLocalDecompression,
|
||||||
archiver: b.getArchiver(info.root, destInfo.root),
|
archiver: b.getArchiver(info.root, destInfo.root),
|
||||||
chownPair: chownPair,
|
identity: identity,
|
||||||
}
|
}
|
||||||
if err := performCopyForInfo(destInfo, info, opts); err != nil {
|
if err := performCopyForInfo(destInfo, info, opts); err != nil {
|
||||||
return errors.Wrapf(err, "failed to copy files")
|
return errors.Wrapf(err, "failed to copy files")
|
||||||
|
|
|
@ -11,11 +11,11 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
|
func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) {
|
||||||
var userStr, grpStr string
|
var userStr, grpStr string
|
||||||
parts := strings.Split(chown, ":")
|
parts := strings.Split(chown, ":")
|
||||||
if len(parts) > 2 {
|
if len(parts) > 2 {
|
||||||
return idtools.IDPair{}, errors.New("invalid chown string format: " + chown)
|
return idtools.Identity{}, errors.New("invalid chown string format: " + chown)
|
||||||
}
|
}
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
// if no group specified, use the user spec as group as well
|
// if no group specified, use the user spec as group as well
|
||||||
|
@ -26,25 +26,25 @@ func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (
|
||||||
|
|
||||||
passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
|
passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs")
|
return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs")
|
||||||
}
|
}
|
||||||
groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath)
|
groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
|
return idtools.Identity{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
|
||||||
}
|
}
|
||||||
uid, err := lookupUser(userStr, passwdPath)
|
uid, err := lookupUser(userStr, passwdPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return idtools.IDPair{}, errors.Wrapf(err, "can't find uid for user "+userStr)
|
return idtools.Identity{}, errors.Wrapf(err, "can't find uid for user "+userStr)
|
||||||
}
|
}
|
||||||
gid, err := lookupGroup(grpStr, groupPath)
|
gid, err := lookupGroup(grpStr, groupPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return idtools.IDPair{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
|
return idtools.Identity{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert as necessary because of user namespaces
|
// convert as necessary because of user namespaces
|
||||||
chownPair, err := idMappings.ToHost(idtools.IDPair{UID: uid, GID: gid})
|
chownPair, err := identityMapping.ToHost(idtools.Identity{UID: uid, GID: gid})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return idtools.IDPair{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
|
return idtools.Identity{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
|
||||||
}
|
}
|
||||||
return chownPair, nil
|
return chownPair, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
is "gotest.tools/assert/cmp"
|
is "gotest.tools/assert/cmp"
|
||||||
|
@ -34,7 +35,7 @@ othergrp:x:6666:
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
|
remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
|
||||||
unmapped := &idtools.IDMappings{}
|
unmapped := &idtools.IdentityMapping{}
|
||||||
|
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
|
contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
@ -49,56 +50,72 @@ othergrp:x:6666:
|
||||||
|
|
||||||
// positive tests
|
// positive tests
|
||||||
for _, testcase := range []struct {
|
for _, testcase := range []struct {
|
||||||
|
builder *Builder
|
||||||
name string
|
name string
|
||||||
chownStr string
|
chownStr string
|
||||||
idMapping *idtools.IDMappings
|
idMapping *idtools.IdentityMapping
|
||||||
expected idtools.IDPair
|
state *dispatchState
|
||||||
|
expected idtools.Identity
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "UIDNoMap",
|
name: "UIDNoMap",
|
||||||
chownStr: "1",
|
chownStr: "1",
|
||||||
idMapping: unmapped,
|
idMapping: unmapped,
|
||||||
expected: idtools.IDPair{UID: 1, GID: 1},
|
state: &dispatchState{},
|
||||||
|
expected: idtools.Identity{UID: 1, GID: 1},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "UIDGIDNoMap",
|
name: "UIDGIDNoMap",
|
||||||
chownStr: "0:1",
|
chownStr: "0:1",
|
||||||
idMapping: unmapped,
|
idMapping: unmapped,
|
||||||
expected: idtools.IDPair{UID: 0, GID: 1},
|
state: &dispatchState{},
|
||||||
|
expected: idtools.Identity{UID: 0, GID: 1},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "UIDWithMap",
|
name: "UIDWithMap",
|
||||||
chownStr: "0",
|
chownStr: "0",
|
||||||
idMapping: remapped,
|
idMapping: remapped,
|
||||||
expected: idtools.IDPair{UID: 100000, GID: 100000},
|
state: &dispatchState{},
|
||||||
|
expected: idtools.Identity{UID: 100000, GID: 100000},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "UIDGIDWithMap",
|
name: "UIDGIDWithMap",
|
||||||
chownStr: "1:33",
|
chownStr: "1:33",
|
||||||
idMapping: remapped,
|
idMapping: remapped,
|
||||||
expected: idtools.IDPair{UID: 100001, GID: 100033},
|
state: &dispatchState{},
|
||||||
|
expected: idtools.Identity{UID: 100001, GID: 100033},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "UserNoMap",
|
name: "UserNoMap",
|
||||||
chownStr: "bin:5555",
|
chownStr: "bin:5555",
|
||||||
idMapping: unmapped,
|
idMapping: unmapped,
|
||||||
expected: idtools.IDPair{UID: 1, GID: 5555},
|
state: &dispatchState{},
|
||||||
|
expected: idtools.Identity{UID: 1, GID: 5555},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "GroupWithMap",
|
name: "GroupWithMap",
|
||||||
chownStr: "0:unicorn",
|
chownStr: "0:unicorn",
|
||||||
idMapping: remapped,
|
idMapping: remapped,
|
||||||
expected: idtools.IDPair{UID: 100000, GID: 101002},
|
state: &dispatchState{},
|
||||||
|
expected: idtools.Identity{UID: 100000, GID: 101002},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "UserOnlyWithMap",
|
name: "UserOnlyWithMap",
|
||||||
chownStr: "unicorn",
|
chownStr: "unicorn",
|
||||||
idMapping: remapped,
|
idMapping: remapped,
|
||||||
expected: idtools.IDPair{UID: 101001, GID: 101002},
|
state: &dispatchState{},
|
||||||
|
expected: idtools.Identity{UID: 101001, GID: 101002},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(testcase.name, func(t *testing.T) {
|
t.Run(testcase.name, func(t *testing.T) {
|
||||||
idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
|
idPair, err := parseChownFlag(testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
|
||||||
assert.NilError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
|
assert.NilError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
|
||||||
assert.Check(t, is.DeepEqual(testcase.expected, idPair), "chown flag mapping failure")
|
assert.Check(t, is.DeepEqual(testcase.expected, idPair), "chown flag mapping failure")
|
||||||
})
|
})
|
||||||
|
@ -106,32 +123,40 @@ othergrp:x:6666:
|
||||||
|
|
||||||
// error tests
|
// error tests
|
||||||
for _, testcase := range []struct {
|
for _, testcase := range []struct {
|
||||||
|
builder *Builder
|
||||||
name string
|
name string
|
||||||
chownStr string
|
chownStr string
|
||||||
idMapping *idtools.IDMappings
|
idMapping *idtools.IdentityMapping
|
||||||
|
state *dispatchState
|
||||||
descr string
|
descr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "BadChownFlagFormat",
|
name: "BadChownFlagFormat",
|
||||||
chownStr: "bob:1:555",
|
chownStr: "bob:1:555",
|
||||||
idMapping: unmapped,
|
idMapping: unmapped,
|
||||||
|
state: &dispatchState{},
|
||||||
descr: "invalid chown string format: bob:1:555",
|
descr: "invalid chown string format: bob:1:555",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "UserNoExist",
|
name: "UserNoExist",
|
||||||
chownStr: "bob",
|
chownStr: "bob",
|
||||||
idMapping: unmapped,
|
idMapping: unmapped,
|
||||||
|
state: &dispatchState{},
|
||||||
descr: "can't find uid for user bob: no such user: bob",
|
descr: "can't find uid for user bob: no such user: bob",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}},
|
||||||
name: "GroupNoExist",
|
name: "GroupNoExist",
|
||||||
chownStr: "root:bob",
|
chownStr: "root:bob",
|
||||||
idMapping: unmapped,
|
idMapping: unmapped,
|
||||||
|
state: &dispatchState{},
|
||||||
descr: "can't find gid for group bob: no such group: bob",
|
descr: "can't find gid for group bob: no such group: bob",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(testcase.name, func(t *testing.T) {
|
t.Run(testcase.name, func(t *testing.T) {
|
||||||
_, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
|
_, err := parseChownFlag(testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
|
||||||
assert.Check(t, is.Error(err, testcase.descr), "Expected error string doesn't match")
|
assert.Check(t, is.Error(err, testcase.descr), "Expected error string doesn't match")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,120 @@
|
||||||
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
package dockerfile // import "github.com/docker/docker/builder/dockerfile"
|
||||||
|
|
||||||
import "github.com/docker/docker/pkg/idtools"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
|
"github.com/containerd/containerd/platforms"
|
||||||
return idMappings.RootPair(), nil
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping *idtools.IdentityMapping) (idtools.Identity, error) {
|
||||||
|
if builder.options.Platform == "windows" {
|
||||||
|
return getAccountIdentity(builder, chown, ctrRootPath, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
return identityMapping.RootPair(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (idtools.Identity, error) {
|
||||||
|
// If this is potentially a string SID then attempt to convert it to verify
|
||||||
|
// this, otherwise continue looking for the account.
|
||||||
|
if strings.HasPrefix(accountName, "S-") || strings.HasPrefix(accountName, "s-") {
|
||||||
|
sid, err := windows.StringToSid(accountName)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
accountSid, err := sid.String()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return idtools.Identity{SID: ""}, errors.Wrapf(err, "error converting SID to string")
|
||||||
|
}
|
||||||
|
|
||||||
|
return idtools.Identity{SID: accountSid}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to obtain the SID using the name.
|
||||||
|
sid, _, accType, err := windows.LookupSID("", accountName)
|
||||||
|
|
||||||
|
// If this is a SID that is built-in and hence the same across all systems then use that.
|
||||||
|
if err == nil && (accType == windows.SidTypeAlias || accType == windows.SidTypeWellKnownGroup) {
|
||||||
|
accountSid, err := sid.String()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return idtools.Identity{SID: ""}, errors.Wrapf(err, "error converting SID to string")
|
||||||
|
}
|
||||||
|
|
||||||
|
return idtools.Identity{SID: accountSid}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the account name is one unique to containers.
|
||||||
|
if strings.EqualFold(accountName, "ContainerAdministrator") {
|
||||||
|
return idtools.Identity{SID: system.ContainerAdministratorSidString}, nil
|
||||||
|
|
||||||
|
} else if strings.EqualFold(accountName, "ContainerUser") {
|
||||||
|
return idtools.Identity{SID: system.ContainerUserSidString}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other lookups failed, so therefore determine if the account in
|
||||||
|
// question exists in the container and if so, obtain its SID.
|
||||||
|
return lookupNTAccount(builder, accountName, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupNTAccount(builder *Builder, accountName string, state *dispatchState) (idtools.Identity, error) {
|
||||||
|
|
||||||
|
source, _ := filepath.Split(os.Args[0])
|
||||||
|
|
||||||
|
target := "C:\\Docker"
|
||||||
|
targetExecutable := target + "\\containerutility.exe"
|
||||||
|
|
||||||
|
optionsPlatform, err := platforms.Parse(builder.options.Platform)
|
||||||
|
if err != nil {
|
||||||
|
return idtools.Identity{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
runConfig := copyRunConfig(state.runConfig,
|
||||||
|
withCmdCommentString("internal run to obtain NT account information.", optionsPlatform.OS))
|
||||||
|
|
||||||
|
runConfig.ArgsEscaped = true
|
||||||
|
runConfig.Cmd = []string{targetExecutable, "getaccountsid", accountName}
|
||||||
|
|
||||||
|
hostConfig := &container.HostConfig{Mounts: []mount.Mount{
|
||||||
|
{
|
||||||
|
Type: mount.TypeBind,
|
||||||
|
Source: source,
|
||||||
|
Target: target,
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := builder.containerManager.Create(runConfig, hostConfig)
|
||||||
|
if err != nil {
|
||||||
|
return idtools.Identity{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout := new(bytes.Buffer)
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
|
||||||
|
if err := builder.containerManager.Run(builder.clientCtx, container.ID, stdout, stderr); err != nil {
|
||||||
|
if err, ok := err.(*statusCodeError); ok {
|
||||||
|
return idtools.Identity{}, &jsonmessage.JSONError{
|
||||||
|
Message: stderr.String(),
|
||||||
|
Code: err.StatusCode(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idtools.Identity{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountSid := stdout.String()
|
||||||
|
|
||||||
|
return idtools.Identity{SID: accountSid}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,7 +267,7 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
|
||||||
return opts, errors.Wrap(err, "failed to create fscache")
|
return opts, errors.Wrap(err, "failed to create fscache")
|
||||||
}
|
}
|
||||||
|
|
||||||
manager, err := dockerfile.NewBuildManager(daemon.BuilderBackend(), sm, buildCache, daemon.IDMappings())
|
manager, err := dockerfile.NewBuildManager(daemon.BuilderBackend(), sm, buildCache, daemon.IdentityMapping())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return opts, err
|
return opts, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,7 +254,7 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, 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(rootIDs idtools.IDPair) error {
|
func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity) error {
|
||||||
// TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting.
|
// TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting.
|
||||||
// We will need to do remote filesystem operations here.
|
// We will need to do remote filesystem operations here.
|
||||||
if container.OS != runtime.GOOS {
|
if container.OS != runtime.GOOS {
|
||||||
|
@ -271,7 +271,7 @@ func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChownNew(pth, 0755, rootIdentity); 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 errors.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
|
return errors.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
|
func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
|
||||||
return &archive.TarOptions{
|
return &archive.TarOptions{
|
||||||
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
||||||
UIDMaps: daemon.idMappings.UIDs(),
|
UIDMaps: daemon.idMapping.UIDs(),
|
||||||
GIDMaps: daemon.idMappings.GIDs(),
|
GIDMaps: daemon.idMapping.GIDs(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,10 @@ func (daemon *Daemon) tarCopyOptions(container *container.Container, noOverwrite
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
identity := idtools.Identity{UID: user.Uid, GID: user.Gid}
|
||||||
|
|
||||||
return &archive.TarOptions{
|
return &archive.TarOptions{
|
||||||
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
NoOverwriteDirNonDir: noOverwriteDirNonDir,
|
||||||
ChownOpts: &idtools.IDPair{UID: user.Uid, GID: user.Gid},
|
ChownOpts: &identity,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
||||||
case ipcMode.IsShareable():
|
case ipcMode.IsShareable():
|
||||||
rootIDs := daemon.idMappings.RootPair()
|
rootIDs := daemon.idMapping.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 {
|
||||||
|
@ -179,7 +179,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve possible remapped range start for root UID, GID
|
// retrieve possible remapped range start for root UID, GID
|
||||||
rootIDs := daemon.idMappings.RootPair()
|
rootIDs := daemon.idMapping.RootPair()
|
||||||
|
|
||||||
for _, s := range c.SecretReferences {
|
for _, s := range c.SecretReferences {
|
||||||
// TODO (ehazlett): use type switch when more are supported
|
// TODO (ehazlett): use type switch when more are supported
|
||||||
|
@ -278,7 +278,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
|
||||||
// In practice this is using a tmpfs mount and is used for both "configs" and "secrets"
|
// In practice this is using a tmpfs mount and is used for both "configs" and "secrets"
|
||||||
func (daemon *Daemon) createSecretsDir(c *container.Container) error {
|
func (daemon *Daemon) createSecretsDir(c *container.Container) error {
|
||||||
// retrieve possible remapped range start for root UID, GID
|
// retrieve possible remapped range start for root UID, GID
|
||||||
rootIDs := daemon.idMappings.RootPair()
|
rootIDs := daemon.idMapping.RootPair()
|
||||||
dir, err := c.SecretMountPath()
|
dir, err := c.SecretMountPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error getting container secrets dir")
|
return errors.Wrap(err, "error getting container secrets dir")
|
||||||
|
@ -304,7 +304,7 @@ func (daemon *Daemon) remountSecretDir(c *container.Container) error {
|
||||||
if err := label.Relabel(dir, c.MountLabel, false); err != nil {
|
if err := label.Relabel(dir, c.MountLabel, false); err != nil {
|
||||||
logrus.WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label")
|
logrus.WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label")
|
||||||
}
|
}
|
||||||
rootIDs := daemon.idMappings.RootPair()
|
rootIDs := daemon.idMapping.RootPair()
|
||||||
tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
|
tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
|
||||||
|
|
||||||
// remount secrets ro
|
// remount secrets ro
|
||||||
|
@ -407,5 +407,5 @@ func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return idtools.MkdirAllAndChown(p, 0700, daemon.idMappings.RootPair())
|
return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair())
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,13 +155,14 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set RWLayer for container after mount labels have been set
|
// Set RWLayer for container after mount labels have been set
|
||||||
rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMappings))
|
rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMapping))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errdefs.System(err)
|
return nil, errdefs.System(err)
|
||||||
}
|
}
|
||||||
container.RWLayer = rwLayer
|
container.RWLayer = rwLayer
|
||||||
|
|
||||||
rootIDs := daemon.idMappings.RootPair()
|
rootIDs := daemon.idMapping.RootPair()
|
||||||
|
|
||||||
if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil {
|
if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
|
||||||
}
|
}
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
rootIDs := daemon.idMappings.RootPair()
|
rootIDs := daemon.idMapping.RootPair()
|
||||||
if err := container.SetupWorkingDirectory(rootIDs); err != nil {
|
if err := container.SetupWorkingDirectory(rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ type Daemon struct {
|
||||||
seccompEnabled bool
|
seccompEnabled bool
|
||||||
apparmorEnabled bool
|
apparmorEnabled bool
|
||||||
shutdown bool
|
shutdown bool
|
||||||
idMappings *idtools.IDMappings
|
idMapping *idtools.IdentityMapping
|
||||||
// TODO: move graphDrivers field to an InfoService
|
// TODO: move graphDrivers field to an InfoService
|
||||||
graphDrivers map[string]string // By operating system
|
graphDrivers map[string]string // By operating system
|
||||||
|
|
||||||
|
@ -594,11 +594,11 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
idMappings, err := setupRemappedRoot(config)
|
idMapping, err := setupRemappedRoot(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rootIDs := idMappings.RootPair()
|
rootIDs := idMapping.RootPair()
|
||||||
if err := setupDaemonProcess(config); err != nil {
|
if err := setupDaemonProcess(config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -749,7 +749,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: gd,
|
GraphDriver: gd,
|
||||||
GraphDriverOptions: config.GraphOptions,
|
GraphDriverOptions: config.GraphOptions,
|
||||||
IDMappings: idMappings,
|
IDMapping: idMapping,
|
||||||
PluginGetter: d.PluginStore,
|
PluginGetter: d.PluginStore,
|
||||||
ExperimentalEnabled: config.Experimental,
|
ExperimentalEnabled: config.Experimental,
|
||||||
OS: operatingSystem,
|
OS: operatingSystem,
|
||||||
|
@ -856,7 +856,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
|
||||||
|
|
||||||
d.EventsService = events.New()
|
d.EventsService = events.New()
|
||||||
d.root = config.Root
|
d.root = config.Root
|
||||||
d.idMappings = idMappings
|
d.idMapping = idMapping
|
||||||
d.seccompEnabled = sysInfo.Seccomp
|
d.seccompEnabled = sysInfo.Seccomp
|
||||||
d.apparmorEnabled = sysInfo.AppArmor
|
d.apparmorEnabled = sysInfo.AppArmor
|
||||||
|
|
||||||
|
@ -1106,7 +1106,7 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) {
|
||||||
// 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, rootIDs idtools.IDPair) (string, error) {
|
func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (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")
|
||||||
|
@ -1126,7 +1126,7 @@ func prepareTempDir(rootDir string, rootIDs idtools.IDPair) (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.MkdirAllAndChown(tmpDir, 0700, rootIDs)
|
return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIdentity)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) setGenericResources(conf *config.Config) error {
|
func (daemon *Daemon) setGenericResources(conf *config.Config) error {
|
||||||
|
@ -1274,11 +1274,11 @@ func CreateDaemonRoot(config *config.Config) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
idMappings, err := setupRemappedRoot(config)
|
idMapping, err := setupRemappedRoot(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return setupDaemonRoot(config, realRoot, idMappings.RootPair())
|
return setupDaemonRoot(config, realRoot, idMapping.RootPair())
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkpointAndSave grabs a container lock to safely call container.CheckpointTo
|
// checkpointAndSave grabs a container lock to safely call container.CheckpointTo
|
||||||
|
@ -1304,9 +1304,9 @@ func (daemon *Daemon) GetAttachmentStore() *network.AttachmentStore {
|
||||||
return &daemon.attachmentStore
|
return &daemon.attachmentStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDMappings returns uid/gid mappings for the builder
|
// IdentityMapping returns uid/gid mapping or a SID (in the case of Windows) for the builder
|
||||||
func (daemon *Daemon) IDMappings() *idtools.IDMappings {
|
func (daemon *Daemon) IdentityMapping() *idtools.IdentityMapping {
|
||||||
return daemon.idMappings
|
return daemon.idMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageService returns the Daemon's ImageService
|
// ImageService returns the Daemon's ImageService
|
||||||
|
|
|
@ -124,7 +124,7 @@ func TestTmpfsDevShmSizeOverride(t *testing.T) {
|
||||||
mnt := "/dev/shm"
|
mnt := "/dev/shm"
|
||||||
|
|
||||||
d := Daemon{
|
d := Daemon{
|
||||||
idMappings: &idtools.IDMappings{},
|
idMapping: &idtools.IdentityMapping{},
|
||||||
}
|
}
|
||||||
c := &container.Container{
|
c := &container.Container{
|
||||||
HostConfig: &containertypes.HostConfig{
|
HostConfig: &containertypes.HostConfig{
|
||||||
|
|
|
@ -118,7 +118,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
|
||||||
repository: tmp,
|
repository: tmp,
|
||||||
root: tmp,
|
root: tmp,
|
||||||
}
|
}
|
||||||
daemon.volumes, err = volumesservice.NewVolumeService(tmp, nil, idtools.IDPair{UID: 0, GID: 0}, daemon)
|
daemon.volumes, err = volumesservice.NewVolumeService(tmp, nil, idtools.Identity{UID: 0, GID: 0}, daemon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1003,9 +1003,9 @@ func removeDefaultBridgeInterface() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupInitLayer(idMappings *idtools.IDMappings) func(containerfs.ContainerFS) error {
|
func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error {
|
||||||
return func(initPath containerfs.ContainerFS) error {
|
return func(initPath containerfs.ContainerFS) error {
|
||||||
return initlayer.Setup(initPath, idMappings.RootPair())
|
return initlayer.Setup(initPath, idMapping.RootPair())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1102,7 +1102,7 @@ func parseRemappedRoot(usergrp string) (string, string, error) {
|
||||||
return username, groupname, nil
|
return username, groupname, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
|
func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) {
|
||||||
if runtime.GOOS != "linux" && config.RemappedRoot != "" {
|
if runtime.GOOS != "linux" && config.RemappedRoot != "" {
|
||||||
return nil, fmt.Errorf("User namespaces are only supported on Linux")
|
return nil, fmt.Errorf("User namespaces are only supported on Linux")
|
||||||
}
|
}
|
||||||
|
@ -1118,22 +1118,22 @@ func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
|
||||||
// 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 &idtools.IDMappings{}, nil
|
return &idtools.IdentityMapping{}, 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)
|
||||||
|
|
||||||
mappings, err := idtools.NewIDMappings(username, groupname)
|
mappings, err := idtools.NewIdentityMapping(username, groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Can't create ID mappings")
|
return nil, errors.Wrap(err, "Can't create ID mappings")
|
||||||
}
|
}
|
||||||
return mappings, nil
|
return mappings, nil
|
||||||
}
|
}
|
||||||
return &idtools.IDMappings{}, nil
|
return &idtools.IdentityMapping{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
|
func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) 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
|
||||||
|
@ -1158,10 +1158,10 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPa
|
||||||
// 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", rootIDs.UID, rootIDs.GID))
|
config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIdentity.UID, rootIdentity.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.MkdirAllAndChown(config.Root, 0700, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIdentity); 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
|
||||||
|
@ -1174,7 +1174,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPa
|
||||||
if dirPath == "/" {
|
if dirPath == "/" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !idtools.CanAccess(dirPath, rootIDs) {
|
if !idtools.CanAccess(dirPath, rootIdentity) {
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ func parseSecurityOpt(container *container.Container, config *containertypes.Hos
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupInitLayer(idMappings *idtools.IDMappings) func(containerfs.ContainerFS) error {
|
func setupInitLayer(idMapping *idtools.IdentityMapping) func(containerfs.ContainerFS) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,11 +465,11 @@ func (daemon *Daemon) cleanupMounts() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
|
func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) {
|
||||||
return &idtools.IDMappings{}, nil
|
return &idtools.IdentityMapping{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
|
func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) 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, system.SddlAdministratorsLocalSystem); err != nil {
|
if err := system.MkdirAllWithACL(config.Root, 0, system.SddlAdministratorsLocalSystem); err != nil {
|
||||||
|
|
|
@ -68,8 +68,8 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R
|
||||||
|
|
||||||
archive, err := archivePath(basefs, basefs.Path(), &archive.TarOptions{
|
archive, err := archivePath(basefs, basefs.Path(), &archive.TarOptions{
|
||||||
Compression: archive.Uncompressed,
|
Compression: archive.Uncompressed,
|
||||||
UIDMaps: daemon.idMappings.UIDs(),
|
UIDMaps: daemon.idMapping.UIDs(),
|
||||||
GIDMaps: daemon.idMappings.GIDs(),
|
GIDMaps: daemon.idMapping.GIDs(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rwlayer.Unmount()
|
rwlayer.Unmount()
|
||||||
|
|
|
@ -135,13 +135,13 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create the root aufs driver dir
|
// Create the root aufs driver dir
|
||||||
if err := idtools.MkdirAllAndChown(root, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(root, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate the dir structure
|
// Populate the dir structure
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,7 @@ func (a *Driver) createDirsFor(id string) error {
|
||||||
// The path of directories are <aufs_root_path>/mnt/<image_id>
|
// The path of directories are <aufs_root_path>/mnt/<image_id>
|
||||||
// and <aufs_root_path>/diff/<image_id>
|
// and <aufs_root_path>/diff/<image_id>
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +502,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if parent == "" {
|
if parent == "" {
|
||||||
|
@ -537,7 +537,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||||
if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil {
|
if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil {
|
if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil {
|
||||||
|
|
|
@ -268,7 +268,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAllAndChown(dirname, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil {
|
if err := idtools.MkdirAllAndChown(dirname, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1691,7 +1691,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAndChown(devices.root, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil {
|
if err := idtools.MkdirAndChown(devices.root, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil {
|
if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil {
|
||||||
|
|
|
@ -200,11 +200,11 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the target directories if they don't exist
|
// Create the target directories if they don't exist
|
||||||
if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil {
|
if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, idtools.Identity{UID: uid, GID: gid}); err != nil {
|
||||||
d.ctr.Decrement(mp)
|
d.ctr.Decrement(mp)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAndChown(mp, 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
|
if err := idtools.MkdirAndChown(mp, 0755, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
|
||||||
d.ctr.Decrement(mp)
|
d.ctr.Decrement(mp)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := idtools.MkdirAllAndChown(rootFs, 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil {
|
if err := idtools.MkdirAllAndChown(rootFs, 0755, idtools.Identity{UID: uid, GID: gid}); err != nil {
|
||||||
d.ctr.Decrement(mp)
|
d.ctr.Decrement(mp)
|
||||||
d.DeviceSet.UnmountDevice(id, mp)
|
d.DeviceSet.UnmountDevice(id, mp)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -183,17 +183,17 @@ func InitDriver(dataRoot string, options []string, _, _ []idtools.IDMap) (graphd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the dataRoot directory is created
|
// Make sure the dataRoot directory is created
|
||||||
if err := idtools.MkdirAllAndChown(dataRoot, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
|
if err := idtools.MkdirAllAndChown(dataRoot, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
|
||||||
return nil, fmt.Errorf("%s failed to create '%s': %v", title, dataRoot, err)
|
return nil, fmt.Errorf("%s failed to create '%s': %v", title, dataRoot, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the cache directory is created under dataRoot
|
// Make sure the cache directory is created under dataRoot
|
||||||
if err := idtools.MkdirAllAndChown(cd, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
|
if err := idtools.MkdirAllAndChown(cd, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
|
||||||
return nil, fmt.Errorf("%s failed to create '%s': %v", title, cd, err)
|
return nil, fmt.Errorf("%s failed to create '%s': %v", title, cd, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the scratch directory is created under dataRoot
|
// Make sure the scratch directory is created under dataRoot
|
||||||
if err := idtools.MkdirAllAndChown(sd, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
|
if err := idtools.MkdirAllAndChown(sd, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
|
||||||
return nil, fmt.Errorf("%s failed to create '%s': %v", title, sd, err)
|
return nil, fmt.Errorf("%s failed to create '%s': %v", title, sd, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create the driver home dir
|
// Create the driver home dir
|
||||||
if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
root := idtools.IDPair{UID: rootUID, GID: rootGID}
|
root := idtools.Identity{UID: rootUID, GID: rootGID}
|
||||||
|
|
||||||
if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
|
if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -413,7 +413,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -200,7 +200,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create the driver home dir
|
// Create the driver home dir
|
||||||
if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,7 +378,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
root := idtools.IDPair{UID: rootUID, GID: rootGID}
|
root := idtools.Identity{UID: rootUID, GID: rootGID}
|
||||||
|
|
||||||
if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
|
if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -586,7 +586,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,10 @@ 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,
|
||||||
idMappings: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps),
|
idMapping: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps),
|
||||||
}
|
}
|
||||||
rootIDs := d.idMappings.RootPair()
|
rootIDs := d.idMapping.RootPair()
|
||||||
if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -46,8 +46,8 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
// 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 {
|
||||||
driverQuota
|
driverQuota
|
||||||
home string
|
home string
|
||||||
idMappings *idtools.IDMappings
|
idMapping *idtools.IdentityMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) String() string {
|
func (d *Driver) String() string {
|
||||||
|
@ -105,7 +105,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
|
||||||
|
|
||||||
func (d *Driver) create(id, parent string, size uint64) error {
|
func (d *Driver) create(id, parent string, size uint64) error {
|
||||||
dir := d.dir(id)
|
dir := d.dir(id)
|
||||||
rootIDs := d.idMappings.RootPair()
|
rootIDs := d.idMapping.RootPair()
|
||||||
if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap)
|
||||||
return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home)
|
return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil {
|
if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil {
|
||||||
return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err)
|
return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to get root uid/guid: %v", err)
|
return nil, fmt.Errorf("Failed to get root uid/guid: %v", err)
|
||||||
}
|
}
|
||||||
if err := idtools.MkdirAllAndChown(base, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(base, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, fmt.Errorf("Failed to create '%s': %v", base, err)
|
return nil, fmt.Errorf("Failed to create '%s': %v", base, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +385,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create the target directories if they don't exist
|
// Create the target directories if they don't exist
|
||||||
if err := idtools.MkdirAllAndChown(mountpoint, 0755, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
|
if err := idtools.MkdirAllAndChown(mountpoint, 0755, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInf
|
||||||
if selinuxEnabled() {
|
if selinuxEnabled() {
|
||||||
securityOptions = append(securityOptions, "name=selinux")
|
securityOptions = append(securityOptions, "name=selinux")
|
||||||
}
|
}
|
||||||
if rootIDs := daemon.idMappings.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
|
if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
|
||||||
securityOptions = append(securityOptions, "name=userns")
|
securityOptions = append(securityOptions, "name=userns")
|
||||||
}
|
}
|
||||||
v.SecurityOptions = securityOptions
|
v.SecurityOptions = securityOptions
|
||||||
|
|
|
@ -17,7 +17,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(initLayerFs containerfs.ContainerFS, rootIDs idtools.IDPair) error {
|
func Setup(initLayerFs containerfs.ContainerFS, rootIdentity idtools.Identity) error {
|
||||||
// Since all paths are local to the container, we can just extract initLayerFs.Path()
|
// Since all paths are local to the container, we can just extract initLayerFs.Path()
|
||||||
initLayer := initLayerFs.Path()
|
initLayer := initLayerFs.Path()
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@ func Setup(initLayerFs containerfs.ContainerFS, rootIDs idtools.IDPair) 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.MkdirAllAndChownNew(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootIdentity); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch typ {
|
switch typ {
|
||||||
case "dir":
|
case "dir":
|
||||||
if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, pth), 0755, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChownNew(filepath.Join(initLayer, pth), 0755, rootIdentity); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "file":
|
case "file":
|
||||||
|
@ -55,7 +55,7 @@ func Setup(initLayerFs containerfs.ContainerFS, rootIDs idtools.IDPair) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.Chown(rootIDs.UID, rootIDs.GID)
|
f.Chown(rootIdentity.UID, rootIdentity.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 {
|
||||||
|
|
|
@ -11,6 +11,6 @@ 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 containerfs.ContainerFS, rootIDs idtools.IDPair) error {
|
func Setup(initLayer containerfs.ContainerFS, rootIDs idtools.Identity) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,13 +217,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 := daemon.idMappings.UIDs()
|
uidMap := daemon.idMapping.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(daemon.idMappings.GIDs())
|
s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDs())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// network
|
// network
|
||||||
|
@ -619,7 +619,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.idMappings.UIDs(); uidMap != nil || c.HostConfig.Privileged {
|
if uidMap := daemon.idMapping.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])
|
||||||
|
@ -642,7 +642,7 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
|
||||||
Path: c.BaseFS.Path(),
|
Path: c.BaseFS.Path(),
|
||||||
Readonly: c.HostConfig.ReadonlyRootfs,
|
Readonly: c.HostConfig.ReadonlyRootfs,
|
||||||
}
|
}
|
||||||
if err := c.SetupWorkingDirectory(daemon.idMappings.RootPair()); err != nil {
|
if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cwd := c.Config.WorkingDir
|
cwd := c.Config.WorkingDir
|
||||||
|
|
|
@ -21,7 +21,7 @@ func TestTmpfsDevShmNoDupMount(t *testing.T) {
|
||||||
d := Daemon{
|
d := Daemon{
|
||||||
// some empty structs to avoid getting a panic
|
// some empty structs to avoid getting a panic
|
||||||
// caused by a null pointer dereference
|
// caused by a null pointer dereference
|
||||||
idMappings: &idtools.IDMappings{},
|
idMapping: &idtools.IdentityMapping{},
|
||||||
configStore: &config.Config{},
|
configStore: &config.Config{},
|
||||||
}
|
}
|
||||||
c := &container.Container{
|
c := &container.Container{
|
||||||
|
@ -58,7 +58,7 @@ func TestIpcPrivateVsReadonly(t *testing.T) {
|
||||||
d := Daemon{
|
d := Daemon{
|
||||||
// some empty structs to avoid getting a panic
|
// some empty structs to avoid getting a panic
|
||||||
// caused by a null pointer dereference
|
// caused by a null pointer dereference
|
||||||
idMappings: &idtools.IDMappings{},
|
idMapping: &idtools.IdentityMapping{},
|
||||||
configStore: &config.Config{},
|
configStore: &config.Config{},
|
||||||
}
|
}
|
||||||
c := &container.Container{
|
c := &container.Container{
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := m.Setup(c.MountLabel, daemon.idMappings.RootPair(), checkfunc)
|
path, err := m.Setup(c.MountLabel, daemon.idMapping.RootPair(), checkfunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ 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)
|
||||||
rootIDs := daemon.idMappings.RootPair()
|
rootIDs := daemon.idMapping.RootPair()
|
||||||
for _, mount := range netMounts {
|
for _, mount := range netMounts {
|
||||||
// we should only modify ownership of network files within our own container
|
// we should only modify ownership of network files within our own container
|
||||||
// metadata repository. If the user specifies a mount path external, it is
|
// metadata repository. If the user specifies a mount path external, it is
|
||||||
|
|
|
@ -24,7 +24,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, idtools.IDPair{UID: 0, GID: 0}, nil)
|
s, err := mount.Setup(c.MountLabel, idtools.Identity{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,5 @@ func (daemon *Daemon) ContainerCreateWorkdir(cID string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
return container.SetupWorkingDirectory(daemon.idMappings.RootPair())
|
return container.SetupWorkingDirectory(daemon.idMapping.RootPair())
|
||||||
}
|
}
|
||||||
|
|
20
hack/make/containerutility
Normal file
20
hack/make/containerutility
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CONTAINER_UTILITY_COMMIT=e004a1415a433447369e315b9d7df357102be0d2 # v0.9.0
|
||||||
|
|
||||||
|
(
|
||||||
|
git clone https://github.com/docker/windows-container-utility.git "$GOPATH/src/github.com/docker/windows-container-utility"
|
||||||
|
cd "$GOPATH/src/github.com/docker/windows-container-utility"
|
||||||
|
git checkout -q "$CONTAINER_UTILITY_COMMIT"
|
||||||
|
|
||||||
|
echo Building: ${DEST}/containerutility.exe
|
||||||
|
|
||||||
|
(
|
||||||
|
make
|
||||||
|
)
|
||||||
|
|
||||||
|
mkdir -p ${ABS_DEST}
|
||||||
|
|
||||||
|
cp containerutility.exe ${ABS_DEST}/containerutility.exe
|
||||||
|
)
|
|
@ -25,5 +25,7 @@ for platform in $DOCKER_CROSSPLATFORMS; do
|
||||||
mkdir -p "$DEST"
|
mkdir -p "$DEST"
|
||||||
ABS_DEST="$(cd "$DEST" && pwd -P)"
|
ABS_DEST="$(cd "$DEST" && pwd -P)"
|
||||||
source "${MAKEDIR}/binary-daemon"
|
source "${MAKEDIR}/binary-daemon"
|
||||||
|
|
||||||
|
source "${MAKEDIR}/cross-platform-dependent"
|
||||||
)
|
)
|
||||||
done
|
done
|
||||||
|
|
6
hack/make/cross-platform-dependent
Normal file
6
hack/make/cross-platform-dependent
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $platform == "windows/amd64" ]; then
|
||||||
|
source "${MAKEDIR}/containerutility"
|
||||||
|
fi
|
|
@ -45,7 +45,7 @@ type StoreOptions struct {
|
||||||
MetadataStorePathTemplate string
|
MetadataStorePathTemplate string
|
||||||
GraphDriver string
|
GraphDriver string
|
||||||
GraphDriverOptions []string
|
GraphDriverOptions []string
|
||||||
IDMappings *idtools.IDMappings
|
IDMapping *idtools.IdentityMapping
|
||||||
PluginGetter plugingetter.PluginGetter
|
PluginGetter plugingetter.PluginGetter
|
||||||
ExperimentalEnabled bool
|
ExperimentalEnabled bool
|
||||||
OS string
|
OS string
|
||||||
|
@ -56,8 +56,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.Root,
|
Root: options.Root,
|
||||||
DriverOptions: options.GraphDriverOptions,
|
DriverOptions: options.GraphDriverOptions,
|
||||||
UIDMaps: options.IDMappings.UIDs(),
|
UIDMaps: options.IDMapping.UIDs(),
|
||||||
GIDMaps: options.IDMappings.GIDs(),
|
GIDMaps: options.IDMapping.GIDs(),
|
||||||
ExperimentalEnabled: options.ExperimentalEnabled,
|
ExperimentalEnabled: options.ExperimentalEnabled,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -58,7 +58,7 @@ func getSpecUser(ociSpec *specs.Spec) (int, int) {
|
||||||
func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (string, error) {
|
func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (string, error) {
|
||||||
uid, gid := getSpecUser(ociSpec)
|
uid, gid := getSpecUser(ociSpec)
|
||||||
if uid == 0 && gid == 0 {
|
if uid == 0 && gid == 0 {
|
||||||
return bundleDir, idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.IDPair{UID: 0, GID: 0})
|
return bundleDir, idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.Identity{UID: 0, GID: 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
p := string(filepath.Separator)
|
p := string(filepath.Separator)
|
||||||
|
@ -71,7 +71,7 @@ func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (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.MkdirAndChown(p, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
|
if err := idtools.MkdirAndChown(p, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ type (
|
||||||
NoLchown bool
|
NoLchown bool
|
||||||
UIDMaps []idtools.IDMap
|
UIDMaps []idtools.IDMap
|
||||||
GIDMaps []idtools.IDMap
|
GIDMaps []idtools.IDMap
|
||||||
ChownOpts *idtools.IDPair
|
ChownOpts *idtools.Identity
|
||||||
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
|
||||||
|
@ -72,13 +72,13 @@ type (
|
||||||
// this package with a pluggable Untar function. Also, to facilitate the passing of specific id
|
// 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.
|
// mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations.
|
||||||
type Archiver struct {
|
type Archiver struct {
|
||||||
Untar func(io.Reader, string, *TarOptions) error
|
Untar func(io.Reader, string, *TarOptions) error
|
||||||
IDMappingsVar *idtools.IDMappings
|
IDMapping *idtools.IdentityMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultArchiver returns a new Archiver without any IDMappings
|
// NewDefaultArchiver returns a new Archiver without any IdentityMapping
|
||||||
func NewDefaultArchiver() *Archiver {
|
func NewDefaultArchiver() *Archiver {
|
||||||
return &Archiver{Untar: Untar, IDMappingsVar: &idtools.IDMappings{}}
|
return &Archiver{Untar: Untar, IDMapping: &idtools.IdentityMapping{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// breakoutError is used to differentiate errors related to breaking out
|
// breakoutError is used to differentiate errors related to breaking out
|
||||||
|
@ -420,9 +420,9 @@ type tarAppender struct {
|
||||||
Buffer *bufio.Writer
|
Buffer *bufio.Writer
|
||||||
|
|
||||||
// for hardlink mapping
|
// for hardlink mapping
|
||||||
SeenFiles map[uint64]string
|
SeenFiles map[uint64]string
|
||||||
IDMappings *idtools.IDMappings
|
IdentityMapping *idtools.IdentityMapping
|
||||||
ChownOpts *idtools.IDPair
|
ChownOpts *idtools.Identity
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -431,13 +431,13 @@ type tarAppender struct {
|
||||||
WhiteoutConverter tarWhiteoutConverter
|
WhiteoutConverter tarWhiteoutConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTarAppender(idMapping *idtools.IDMappings, writer io.Writer, chownOpts *idtools.IDPair) *tarAppender {
|
func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender {
|
||||||
return &tarAppender{
|
return &tarAppender{
|
||||||
SeenFiles: make(map[uint64]string),
|
SeenFiles: make(map[uint64]string),
|
||||||
TarWriter: tar.NewWriter(writer),
|
TarWriter: tar.NewWriter(writer),
|
||||||
Buffer: pools.BufioWriter32KPool.Get(nil),
|
Buffer: pools.BufioWriter32KPool.Get(nil),
|
||||||
IDMappings: idMapping,
|
IdentityMapping: idMapping,
|
||||||
ChownOpts: chownOpts,
|
ChownOpts: chownOpts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,14 +502,12 @@ 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 !isOverlayWhiteout &&
|
if !isOverlayWhiteout && !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() {
|
||||||
!strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) &&
|
|
||||||
!ta.IDMappings.Empty() {
|
|
||||||
fileIDPair, err := getFileUIDGID(fi.Sys())
|
fileIDPair, err := getFileUIDGID(fi.Sys())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr.Uid, hdr.Gid, err = ta.IDMappings.ToContainer(fileIDPair)
|
hdr.Uid, hdr.Gid, err = ta.IdentityMapping.ToContainer(fileIDPair)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -572,7 +570,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 *idtools.IDPair, inUserns bool) error {
|
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.Identity, 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)
|
||||||
|
@ -652,7 +650,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 = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
|
chownOpts = &idtools.Identity{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
|
||||||
|
@ -901,8 +899,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
|
||||||
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||||
rootIDs := idMappings.RootPair()
|
rootIDs := idMapping.RootPair()
|
||||||
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
|
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
|
||||||
|
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
|
@ -981,7 +979,7 @@ loop:
|
||||||
}
|
}
|
||||||
trBuf.Reset(tr)
|
trBuf.Reset(tr)
|
||||||
|
|
||||||
if err := remapIDs(idMappings, hdr); err != nil {
|
if err := remapIDs(idMapping, hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,8 +1066,8 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
|
||||||
}
|
}
|
||||||
defer archive.Close()
|
defer archive.Close()
|
||||||
options := &TarOptions{
|
options := &TarOptions{
|
||||||
UIDMaps: archiver.IDMappingsVar.UIDs(),
|
UIDMaps: archiver.IDMapping.UIDs(),
|
||||||
GIDMaps: archiver.IDMappingsVar.GIDs(),
|
GIDMaps: archiver.IDMapping.GIDs(),
|
||||||
}
|
}
|
||||||
return archiver.Untar(archive, dst, options)
|
return archiver.Untar(archive, dst, options)
|
||||||
}
|
}
|
||||||
|
@ -1082,8 +1080,8 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
|
||||||
}
|
}
|
||||||
defer archive.Close()
|
defer archive.Close()
|
||||||
options := &TarOptions{
|
options := &TarOptions{
|
||||||
UIDMaps: archiver.IDMappingsVar.UIDs(),
|
UIDMaps: archiver.IDMapping.UIDs(),
|
||||||
GIDMaps: archiver.IDMappingsVar.GIDs(),
|
GIDMaps: archiver.IDMapping.GIDs(),
|
||||||
}
|
}
|
||||||
return archiver.Untar(archive, dst, options)
|
return archiver.Untar(archive, dst, options)
|
||||||
}
|
}
|
||||||
|
@ -1104,7 +1102,7 @@ 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
|
||||||
rootIDs := archiver.IDMappingsVar.RootPair()
|
rootIDs := archiver.IDMapping.RootPair()
|
||||||
// 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.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
|
||||||
|
@ -1164,7 +1162,7 @@ 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)))
|
||||||
|
|
||||||
if err := remapIDs(archiver.IDMappingsVar, hdr); err != nil {
|
if err := remapIDs(archiver.IDMapping, hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1192,13 +1190,13 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDMappings returns the IDMappings of the archiver.
|
// IdentityMapping returns the IdentityMapping of the archiver.
|
||||||
func (archiver *Archiver) IDMappings() *idtools.IDMappings {
|
func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
|
||||||
return archiver.IDMappingsVar
|
return archiver.IDMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error {
|
func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error {
|
||||||
ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
|
ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
|
||||||
hdr.Uid, hdr.Gid = ids.UID, ids.GID
|
hdr.Uid, hdr.Gid = ids.UID, ids.GID
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -755,11 +755,11 @@ func TestTarWithOptionsChownOptsAlwaysOverridesIdPair(t *testing.T) {
|
||||||
expectedUID int
|
expectedUID int
|
||||||
expectedGID int
|
expectedGID int
|
||||||
}{
|
}{
|
||||||
{&TarOptions{ChownOpts: &idtools.IDPair{UID: 1337, GID: 42}}, 1337, 42},
|
{&TarOptions{ChownOpts: &idtools.Identity{UID: 1337, GID: 42}}, 1337, 42},
|
||||||
{&TarOptions{ChownOpts: &idtools.IDPair{UID: 100001, GID: 100001}, UIDMaps: idMaps, GIDMaps: idMaps}, 100001, 100001},
|
{&TarOptions{ChownOpts: &idtools.Identity{UID: 100001, GID: 100001}, UIDMaps: idMaps, GIDMaps: idMaps}, 100001, 100001},
|
||||||
{&TarOptions{ChownOpts: &idtools.IDPair{UID: 0, GID: 0}, NoLchown: false}, 0, 0},
|
{&TarOptions{ChownOpts: &idtools.Identity{UID: 0, GID: 0}, NoLchown: false}, 0, 0},
|
||||||
{&TarOptions{ChownOpts: &idtools.IDPair{UID: 1, GID: 1}, NoLchown: true}, 1, 1},
|
{&TarOptions{ChownOpts: &idtools.Identity{UID: 1, GID: 1}, NoLchown: true}, 1, 1},
|
||||||
{&TarOptions{ChownOpts: &idtools.IDPair{UID: 1000, GID: 1000}, NoLchown: true}, 1000, 1000},
|
{&TarOptions{ChownOpts: &idtools.Identity{UID: 1000, GID: 1000}, NoLchown: true}, 1000, 1000},
|
||||||
}
|
}
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
reader, err := TarWithOptions(filePath, testCase.opts)
|
reader, err := TarWithOptions(filePath, testCase.opts)
|
||||||
|
|
|
@ -68,13 +68,13 @@ func getInodeFromStat(stat interface{}) (inode uint64, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
|
func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
|
||||||
s, ok := stat.(*syscall.Stat_t)
|
s, ok := stat.(*syscall.Stat_t)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return idtools.IDPair{}, errors.New("cannot convert stat value to syscall.Stat_t")
|
return idtools.Identity{}, errors.New("cannot convert stat value to syscall.Stat_t")
|
||||||
}
|
}
|
||||||
return idtools.IDPair{UID: int(s.Uid), GID: int(s.Gid)}, nil
|
return idtools.Identity{UID: int(s.Uid), GID: int(s.Gid)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
||||||
|
|
|
@ -61,7 +61,7 @@ func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
|
func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
|
||||||
// no notion of file ownership mapping yet on Windows
|
// no notion of file ownership mapping yet on Windows
|
||||||
return idtools.IDPair{UID: 0, GID: 0}, nil
|
return idtools.Identity{UID: 0, GID: 0}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +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{}
|
||||||
}
|
}
|
||||||
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||||
|
|
||||||
aufsTempdir := ""
|
aufsTempdir := ""
|
||||||
aufsHardlinks := make(map[string]*tar.Header)
|
aufsHardlinks := make(map[string]*tar.Header)
|
||||||
|
@ -192,7 +192,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||||
srcData = tmpFile
|
srcData = tmpFile
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := remapIDs(idMappings, srcHdr); err != nil {
|
if err := remapIDs(idMapping, srcHdr); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewArchiver returns a new Archiver which uses chrootarchive.Untar
|
// NewArchiver returns a new Archiver which uses chrootarchive.Untar
|
||||||
func NewArchiver(idMappings *idtools.IDMappings) *archive.Archiver {
|
func NewArchiver(idMapping *idtools.IdentityMapping) *archive.Archiver {
|
||||||
if idMappings == nil {
|
if idMapping == nil {
|
||||||
idMappings = &idtools.IDMappings{}
|
idMapping = &idtools.IdentityMapping{}
|
||||||
}
|
}
|
||||||
return &archive.Archiver{
|
return &archive.Archiver{
|
||||||
Untar: Untar,
|
Untar: Untar,
|
||||||
IDMappingsVar: idMappings,
|
IDMapping: idMapping,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions
|
||||||
options.ExcludePatterns = []string{}
|
options.ExcludePatterns = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||||
rootIDs := idMappings.RootPair()
|
rootIDs := idMapping.RootPair()
|
||||||
|
|
||||||
dest = filepath.Clean(dest)
|
dest = filepath.Clean(dest)
|
||||||
if _, err := os.Stat(dest); os.IsNotExist(err) {
|
if _, err := os.Stat(dest); os.IsNotExist(err) {
|
||||||
|
|
|
@ -22,11 +22,11 @@ type UntarFunc func(io.Reader, string, *archive.TarOptions) error
|
||||||
|
|
||||||
// Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
|
// Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
|
||||||
type Archiver struct {
|
type Archiver struct {
|
||||||
SrcDriver Driver
|
SrcDriver Driver
|
||||||
DstDriver Driver
|
DstDriver Driver
|
||||||
Tar TarFunc
|
Tar TarFunc
|
||||||
Untar UntarFunc
|
Untar UntarFunc
|
||||||
IDMappingsVar *idtools.IDMappings
|
IDMapping *idtools.IdentityMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
|
// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
|
||||||
|
@ -39,8 +39,8 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
|
||||||
}
|
}
|
||||||
defer tarArchive.Close()
|
defer tarArchive.Close()
|
||||||
options := &archive.TarOptions{
|
options := &archive.TarOptions{
|
||||||
UIDMaps: archiver.IDMappingsVar.UIDs(),
|
UIDMaps: archiver.IDMapping.UIDs(),
|
||||||
GIDMaps: archiver.IDMappingsVar.GIDs(),
|
GIDMaps: archiver.IDMapping.GIDs(),
|
||||||
}
|
}
|
||||||
return archiver.Untar(tarArchive, dst, options)
|
return archiver.Untar(tarArchive, dst, options)
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,8 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
|
||||||
}
|
}
|
||||||
defer tarArchive.Close()
|
defer tarArchive.Close()
|
||||||
options := &archive.TarOptions{
|
options := &archive.TarOptions{
|
||||||
UIDMaps: archiver.IDMappingsVar.UIDs(),
|
UIDMaps: archiver.IDMapping.UIDs(),
|
||||||
GIDMaps: archiver.IDMappingsVar.GIDs(),
|
GIDMaps: archiver.IDMapping.GIDs(),
|
||||||
}
|
}
|
||||||
return archiver.Untar(tarArchive, dst, options)
|
return archiver.Untar(tarArchive, dst, options)
|
||||||
}
|
}
|
||||||
|
@ -75,9 +75,11 @@ 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
|
||||||
rootIDs := archiver.IDMappingsVar.RootPair()
|
|
||||||
|
identity := idtools.Identity{UID: archiver.IDMapping.RootPair().UID, GID: archiver.IDMapping.RootPair().GID}
|
||||||
|
|
||||||
// Create dst, copy src's content into it
|
// Create dst, copy src's content into it
|
||||||
if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChownNew(dst, 0755, identity); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
||||||
|
@ -150,7 +152,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||||
hdr.Mode = int64(os.FileMode(hdr.Mode))
|
hdr.Mode = int64(os.FileMode(hdr.Mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := remapIDs(archiver.IDMappingsVar, hdr); err != nil {
|
if err := remapIDs(archiver.IDMapping, hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,13 +180,13 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDMappings returns the IDMappings of the archiver.
|
// IdentityMapping returns the IdentityMapping of the archiver.
|
||||||
func (archiver *Archiver) IDMappings() *idtools.IDMappings {
|
func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
|
||||||
return archiver.IDMappingsVar
|
return archiver.IDMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error {
|
func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error {
|
||||||
ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
|
ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
|
||||||
hdr.Uid, hdr.Gid = ids.UID, ids.GID
|
hdr.Uid, hdr.Gid = ids.UID, ids.GID
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,23 +37,23 @@ const (
|
||||||
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
// MkdirAllAndChown 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.
|
||||||
func MkdirAllAndChown(path string, mode os.FileMode, owner IDPair) error {
|
func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error {
|
||||||
return mkdirAs(path, mode, owner.UID, owner.GID, true, true)
|
return mkdirAs(path, mode, owner, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
|
// MkdirAndChown 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.
|
||||||
// Note that unlike os.Mkdir(), this function does not return IsExist error
|
// Note that unlike os.Mkdir(), this function does not return IsExist error
|
||||||
// in case path already exists.
|
// in case path already exists.
|
||||||
func MkdirAndChown(path string, mode os.FileMode, owner IDPair) error {
|
func MkdirAndChown(path string, mode os.FileMode, owner Identity) error {
|
||||||
return mkdirAs(path, mode, owner.UID, owner.GID, false, true)
|
return mkdirAs(path, mode, owner, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
|
// 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
|
// 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
|
// directories along the path exist, no change of ownership will be performed
|
||||||
func MkdirAllAndChownNew(path string, mode os.FileMode, owner IDPair) error {
|
func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error {
|
||||||
return mkdirAs(path, mode, owner.UID, owner.GID, true, false)
|
return mkdirAs(path, mode, owner, 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.
|
||||||
|
@ -102,22 +102,23 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDPair is a UID and GID pair
|
// Identity is either a UID and GID pair or a SID (but not both)
|
||||||
type IDPair struct {
|
type Identity struct {
|
||||||
UID int
|
UID int
|
||||||
GID int
|
GID int
|
||||||
|
SID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDMappings contains a mappings of UIDs and GIDs
|
// IdentityMapping contains a mappings of UIDs and GIDs
|
||||||
type IDMappings struct {
|
type IdentityMapping struct {
|
||||||
uids []IDMap
|
uids []IDMap
|
||||||
gids []IDMap
|
gids []IDMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIDMappings takes a requested user and group name and
|
// NewIdentityMapping 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 NewIDMappings(username, groupname string) (*IDMappings, error) {
|
func NewIdentityMapping(username, groupname string) (*IdentityMapping, error) {
|
||||||
subuidRanges, err := parseSubuid(username)
|
subuidRanges, err := parseSubuid(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -133,7 +134,7 @@ func NewIDMappings(username, groupname string) (*IDMappings, error) {
|
||||||
return nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
|
return nil, fmt.Errorf("No subgid ranges found for group %q", groupname)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &IDMappings{
|
return &IdentityMapping{
|
||||||
uids: createIDMap(subuidRanges),
|
uids: createIDMap(subuidRanges),
|
||||||
gids: createIDMap(subgidRanges),
|
gids: createIDMap(subgidRanges),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -141,21 +142,21 @@ func NewIDMappings(username, groupname string) (*IDMappings, error) {
|
||||||
|
|
||||||
// NewIDMappingsFromMaps creates a new mapping from two slices
|
// NewIDMappingsFromMaps creates a new mapping from two slices
|
||||||
// Deprecated: this is a temporary shim while transitioning to IDMapping
|
// Deprecated: this is a temporary shim while transitioning to IDMapping
|
||||||
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IDMappings {
|
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping {
|
||||||
return &IDMappings{uids: uids, gids: gids}
|
return &IdentityMapping{uids: uids, gids: gids}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RootPair returns a uid and gid pair for the root user. The error is ignored
|
// 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
|
// because a root user always exists, and the defaults are correct when the uid
|
||||||
// and gid maps are empty.
|
// and gid maps are empty.
|
||||||
func (i *IDMappings) RootPair() IDPair {
|
func (i *IdentityMapping) RootPair() Identity {
|
||||||
uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
|
uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
|
||||||
return IDPair{UID: uid, GID: gid}
|
return Identity{UID: uid, GID: gid}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToHost returns the host UID and GID for the container uid, 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
|
// Remapping is only performed if the ids aren't already the remapped root ids
|
||||||
func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) {
|
func (i *IdentityMapping) ToHost(pair Identity) (Identity, error) {
|
||||||
var err error
|
var err error
|
||||||
target := i.RootPair()
|
target := i.RootPair()
|
||||||
|
|
||||||
|
@ -173,7 +174,7 @@ func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToContainer returns the container UID and GID for the host uid and gid
|
// ToContainer returns the container UID and GID for the host uid and gid
|
||||||
func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) {
|
func (i *IdentityMapping) ToContainer(pair Identity) (int, int, error) {
|
||||||
uid, err := toContainer(pair.UID, i.uids)
|
uid, err := toContainer(pair.UID, i.uids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, -1, err
|
return -1, -1, err
|
||||||
|
@ -183,19 +184,19 @@ func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty returns true if there are no id mappings
|
// Empty returns true if there are no id mappings
|
||||||
func (i *IDMappings) Empty() bool {
|
func (i *IdentityMapping) Empty() bool {
|
||||||
return len(i.uids) == 0 && len(i.gids) == 0
|
return len(i.uids) == 0 && len(i.gids) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UIDs return the UID mapping
|
// UIDs return the UID mapping
|
||||||
// TODO: remove this once everything has been refactored to use pairs
|
// TODO: remove this once everything has been refactored to use pairs
|
||||||
func (i *IDMappings) UIDs() []IDMap {
|
func (i *IdentityMapping) UIDs() []IDMap {
|
||||||
return i.uids
|
return i.uids
|
||||||
}
|
}
|
||||||
|
|
||||||
// GIDs return the UID mapping
|
// GIDs return the UID mapping
|
||||||
// TODO: remove this once everything has been refactored to use pairs
|
// TODO: remove this once everything has been refactored to use pairs
|
||||||
func (i *IDMappings) GIDs() []IDMap {
|
func (i *IdentityMapping) GIDs() []IDMap {
|
||||||
return i.gids
|
return i.gids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,12 @@ var (
|
||||||
getentCmd string
|
getentCmd string
|
||||||
)
|
)
|
||||||
|
|
||||||
func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
|
func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error {
|
||||||
// make an array containing the original path asked for, plus (for mkAll == true)
|
// make an array containing the original path asked for, plus (for mkAll == true)
|
||||||
// all path components leading up to the complete path that don't exist before we MkdirAll
|
// all path components leading up to the complete path that don't exist before we MkdirAll
|
||||||
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
|
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
|
||||||
// chown the full directory path if it exists
|
// chown the full directory path if it exists
|
||||||
|
|
||||||
var paths []string
|
var paths []string
|
||||||
|
|
||||||
stat, err := system.Stat(path)
|
stat, err := system.Stat(path)
|
||||||
|
@ -38,7 +39,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
|
||||||
}
|
}
|
||||||
|
|
||||||
// short-circuit--we were called with an existing directory and chown was requested
|
// short-circuit--we were called with an existing directory and chown was requested
|
||||||
return lazyChown(path, ownerUID, ownerGID, stat)
|
return lazyChown(path, owner.UID, owner.GID, stat)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -69,7 +70,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
|
||||||
// even if it existed, we will chown the requested path + any subpaths that
|
// even if it existed, we will chown the requested path + any subpaths that
|
||||||
// didn't exist when we called MkdirAll
|
// didn't exist when we called MkdirAll
|
||||||
for _, pathComponent := range paths {
|
for _, pathComponent := range paths {
|
||||||
if err := lazyChown(pathComponent, ownerUID, ownerGID, nil); err != nil {
|
if err := lazyChown(pathComponent, owner.UID, owner.GID, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,7 @@ 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, pair IDPair) bool {
|
func CanAccess(path string, pair Identity) bool {
|
||||||
statInfo, err := system.Stat(path)
|
statInfo, err := system.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -46,7 +46,7 @@ func TestMkdirAllAndChown(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
|
if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0755, Identity{UID: 99, GID: 99}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
testTree["usr/share"] = node{99, 99}
|
testTree["usr/share"] = node{99, 99}
|
||||||
|
@ -59,7 +59,7 @@ func TestMkdirAllAndChown(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101}); err != nil {
|
if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0755, Identity{UID: 101, GID: 101}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
testTree["lib/some"] = node{101, 101}
|
testTree["lib/some"] = node{101, 101}
|
||||||
|
@ -73,7 +73,7 @@ func TestMkdirAllAndChown(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// test a directory that already exists; should be chowned, but nothing else
|
// test a directory that already exists; should be chowned, but nothing else
|
||||||
if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
|
if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0755, Identity{UID: 102, GID: 102}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
testTree["usr"] = node{102, 102}
|
testTree["usr"] = node{102, 102}
|
||||||
|
@ -102,7 +102,7 @@ func TestMkdirAllAndChownNew(t *testing.T) {
|
||||||
assert.NilError(t, buildTree(dirName, testTree))
|
assert.NilError(t, buildTree(dirName, testTree))
|
||||||
|
|
||||||
// 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
|
||||||
err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99})
|
err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, Identity{UID: 99, GID: 99})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
testTree["usr/share"] = node{99, 99}
|
testTree["usr/share"] = node{99, 99}
|
||||||
|
@ -111,7 +111,7 @@ func TestMkdirAllAndChownNew(t *testing.T) {
|
||||||
assert.NilError(t, compareTrees(testTree, verifyTree))
|
assert.NilError(t, compareTrees(testTree, verifyTree))
|
||||||
|
|
||||||
// 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
|
||||||
err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101})
|
err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, Identity{UID: 101, GID: 101})
|
||||||
assert.NilError(t, err)
|
assert.NilError(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}
|
||||||
|
@ -120,7 +120,7 @@ func TestMkdirAllAndChownNew(t *testing.T) {
|
||||||
assert.NilError(t, compareTrees(testTree, verifyTree))
|
assert.NilError(t, compareTrees(testTree, verifyTree))
|
||||||
|
|
||||||
// test a directory that already exists; should NOT be chowned
|
// test a directory that already exists; should NOT be chowned
|
||||||
err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102})
|
err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, Identity{UID: 102, GID: 102})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
verifyTree, err = readTree(dirName, "")
|
verifyTree, err = readTree(dirName, "")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
@ -143,7 +143,7 @@ func TestMkdirAndChown(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// test a directory that already exists; should just chown to the requested uid/gid
|
// test a directory that already exists; should just chown to the requested uid/gid
|
||||||
if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
|
if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0755, Identity{UID: 99, GID: 99}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
testTree["usr"] = node{99, 99}
|
testTree["usr"] = node{99, 99}
|
||||||
|
@ -156,12 +156,12 @@ func TestMkdirAndChown(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a subdir under a dir which doesn't exist--should fail
|
// create a subdir under a dir which doesn't exist--should fail
|
||||||
if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, IDPair{UID: 102, GID: 102}); err == nil {
|
if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, Identity{UID: 102, GID: 102}); err == nil {
|
||||||
t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed")
|
t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a subdir under an existing dir; should only change the ownership of the new subdir
|
// create a subdir under an existing dir; should only change the ownership of the new subdir
|
||||||
if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
|
if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0755, Identity{UID: 102, GID: 102}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
testTree["usr/bin"] = node{102, 102}
|
testTree["usr/bin"] = node{102, 102}
|
||||||
|
@ -326,19 +326,19 @@ func TestNewIDMappings(t *testing.T) {
|
||||||
group, err := user.LookupGroupId(string(gids[0]))
|
group, err := user.LookupGroupId(string(gids[0]))
|
||||||
assert.Check(t, err)
|
assert.Check(t, err)
|
||||||
|
|
||||||
idMappings, err := NewIDMappings(tempUser.Username, group.Name)
|
idMapping, err := NewIdentityMapping(tempUser.Username, group.Name)
|
||||||
assert.Check(t, err)
|
assert.Check(t, err)
|
||||||
|
|
||||||
rootUID, rootGID, err := GetRootUIDGID(idMappings.UIDs(), idMappings.GIDs())
|
rootUID, rootGID, err := GetRootUIDGID(idMapping.UIDs(), idMapping.GIDs())
|
||||||
assert.Check(t, err)
|
assert.Check(t, err)
|
||||||
|
|
||||||
dirName, err := ioutil.TempDir("", "mkdirall")
|
dirName, err := ioutil.TempDir("", "mkdirall")
|
||||||
assert.Check(t, err, "Couldn't create temp directory")
|
assert.Check(t, err, "Couldn't create temp directory")
|
||||||
defer os.RemoveAll(dirName)
|
defer os.RemoveAll(dirName)
|
||||||
|
|
||||||
err = MkdirAllAndChown(dirName, 0700, IDPair{UID: rootUID, GID: rootGID})
|
err = MkdirAllAndChown(dirName, 0700, Identity{UID: rootUID, GID: rootGID})
|
||||||
assert.Check(t, err, "Couldn't change ownership of file path. Got error")
|
assert.Check(t, err, "Couldn't change ownership of file path. Got error")
|
||||||
assert.Check(t, CanAccess(dirName, idMappings.RootPair()), fmt.Sprintf("Unable to access %s directory with user UID:%d and GID:%d", dirName, rootUID, rootGID))
|
assert.Check(t, CanAccess(dirName, idMapping.RootPair()), fmt.Sprintf("Unable to access %s directory with user UID:%d and GID:%d", dirName, rootUID, rootGID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLookupUserAndGroup(t *testing.T) {
|
func TestLookupUserAndGroup(t *testing.T) {
|
||||||
|
@ -388,7 +388,7 @@ func TestMkdirIsNotDir(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.Remove(file.Name())
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
err = mkdirAs(file.Name(), 0755, 0, 0, false, false)
|
err = mkdirAs(file.Name(), 0755, Identity{UID: 0, GID: 0}, false, false)
|
||||||
assert.Check(t, is.Error(err, "mkdir "+file.Name()+": not a directory"))
|
assert.Check(t, is.Error(err, "mkdir "+file.Name()+": not a directory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Platforms such as Windows do not support the UID/GID concept. So make this
|
// This is currently a wrapper around MkdirAll, however, since currently
|
||||||
// just a wrapper around system.MkdirAll.
|
// permissions aren't set through this path, the identity isn't utilized.
|
||||||
func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
|
// Ownership is handled elsewhere, but in the future could be support here
|
||||||
|
// too.
|
||||||
|
func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error {
|
||||||
if err := system.MkdirAll(path, mode, ""); err != nil {
|
if err := system.MkdirAll(path, mode, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,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, pair IDPair) bool {
|
func CanAccess(path string, identity Identity) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,62 @@ package system // import "github.com/docker/docker/pkg/system"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OWNER_SECURITY_INFORMATION = 0x00000001
|
||||||
|
GROUP_SECURITY_INFORMATION = 0x00000002
|
||||||
|
DACL_SECURITY_INFORMATION = 0x00000004
|
||||||
|
SACL_SECURITY_INFORMATION = 0x00000008
|
||||||
|
LABEL_SECURITY_INFORMATION = 0x00000010
|
||||||
|
ATTRIBUTE_SECURITY_INFORMATION = 0x00000020
|
||||||
|
SCOPE_SECURITY_INFORMATION = 0x00000040
|
||||||
|
PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080
|
||||||
|
ACCESS_FILTER_SECURITY_INFORMATION = 0x00000100
|
||||||
|
BACKUP_SECURITY_INFORMATION = 0x00010000
|
||||||
|
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
|
||||||
|
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000
|
||||||
|
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
|
||||||
|
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SE_UNKNOWN_OBJECT_TYPE = iota
|
||||||
|
SE_FILE_OBJECT
|
||||||
|
SE_SERVICE
|
||||||
|
SE_PRINTER
|
||||||
|
SE_REGISTRY_KEY
|
||||||
|
SE_LMSHARE
|
||||||
|
SE_KERNEL_OBJECT
|
||||||
|
SE_WINDOW_OBJECT
|
||||||
|
SE_DS_OBJECT
|
||||||
|
SE_DS_OBJECT_ALL
|
||||||
|
SE_PROVIDER_DEFINED_OBJECT
|
||||||
|
SE_WMIGUID_OBJECT
|
||||||
|
SE_REGISTRY_WOW64_32KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ContainerAdministratorSidString = "S-1-5-93-2-1"
|
||||||
|
ContainerUserSidString = "S-1-5-93-2-2"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ntuserApiset = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
|
ntuserApiset = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
|
||||||
procGetVersionExW = modkernel32.NewProc("GetVersionExW")
|
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||||
procGetProductInfo = modkernel32.NewProc("GetProductInfo")
|
procGetVersionExW = modkernel32.NewProc("GetVersionExW")
|
||||||
|
procGetProductInfo = modkernel32.NewProc("GetProductInfo")
|
||||||
|
procSetNamedSecurityInfo = modadvapi32.NewProc("SetNamedSecurityInfoW")
|
||||||
|
procGetSecurityDescriptorDacl = modadvapi32.NewProc("GetSecurityDescriptorDacl")
|
||||||
)
|
)
|
||||||
|
|
||||||
// OSVersion is a wrapper for Windows version information
|
// OSVersion is a wrapper for Windows version information
|
||||||
|
@ -125,3 +171,23 @@ func HasWin32KSupport() bool {
|
||||||
// APIs.
|
// APIs.
|
||||||
return ntuserApiset.Load() == nil
|
return ntuserApiset.Load() == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetNamedSecurityInfo(objectName *uint16, objectType uint32, securityInformation uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *byte, sacl *byte) (result error) {
|
||||||
|
r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfo.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
result = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSecurityDescriptorDacl(securityDescriptor *byte, daclPresent *uint32, dacl **byte, daclDefaulted *uint32) (result error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(securityDescriptor)), uintptr(unsafe.Pointer(daclPresent)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclDefaulted)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
result = syscall.Errno(e1)
|
||||||
|
} else {
|
||||||
|
result = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
|
rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
|
||||||
if err := initlayer.Setup(rootFS, idtools.IDPair{UID: 0, GID: 0}); err != nil {
|
if err := initlayer.Setup(rootFS, idtools.Identity{UID: 0, GID: 0}); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,18 +46,18 @@ 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, rootIDs idtools.IDPair) (*Root, error) {
|
func New(scope string, rootIdentity idtools.Identity) (*Root, error) {
|
||||||
rootDirectory := filepath.Join(scope, volumesPathName)
|
rootDirectory := filepath.Join(scope, volumesPathName)
|
||||||
|
|
||||||
if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIdentity); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &Root{
|
r := &Root{
|
||||||
scope: scope,
|
scope: scope,
|
||||||
path: rootDirectory,
|
path: rootDirectory,
|
||||||
volumes: make(map[string]*localVolume),
|
volumes: make(map[string]*localVolume),
|
||||||
rootIDs: rootIDs,
|
rootIdentity: rootIdentity,
|
||||||
}
|
}
|
||||||
|
|
||||||
dirs, err := ioutil.ReadDir(rootDirectory)
|
dirs, err := ioutil.ReadDir(rootDirectory)
|
||||||
|
@ -101,11 +101,11 @@ func New(scope string, rootIDs idtools.IDPair) (*Root, error) {
|
||||||
// manages the creation/removal of volumes. It uses only standard vfs
|
// manages the creation/removal of volumes. It uses only standard vfs
|
||||||
// commands to create/remove dirs within its provided scope.
|
// commands to create/remove dirs within its provided scope.
|
||||||
type Root struct {
|
type Root struct {
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
scope string
|
scope string
|
||||||
path string
|
path string
|
||||||
volumes map[string]*localVolume
|
volumes map[string]*localVolume
|
||||||
rootIDs idtools.IDPair
|
rootIdentity idtools.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
// List lists all the volumes
|
// List lists all the volumes
|
||||||
|
@ -146,7 +146,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.MkdirAllAndChown(path, 0755, r.rootIDs); err != nil {
|
if err := idtools.MkdirAllAndChown(path, 0755, r.rootIdentity); err != nil {
|
||||||
return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path)
|
return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ func TestRemove(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, idtools.IDPair{UID: os.Geteuid(), GID: os.Getegid()})
|
r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ func TestInitializeWithVolumes(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, idtools.IDPair{UID: os.Geteuid(), GID: os.Getegid()})
|
r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func TestInitializeWithVolumes(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
|
r, err = New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ func TestCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
|
r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ func TestCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
|
r, err = New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ func TestCreateWithOpts(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
|
r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,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, idtools.IDPair{UID: 0, GID: 0})
|
r, err = New(rootDir, idtools.Identity{UID: 0, GID: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ func TestRelaodNoOpts(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
|
|
||||||
r, err := New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
|
r, err := New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -311,7 +311,7 @@ func TestRelaodNoOpts(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = New(rootDir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
|
r, err = New(rootDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,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, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) {
|
func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun func(m *MountPoint) error) (path string, err error) {
|
||||||
if m.SkipMountpointCreation {
|
if m.SkipMountpointCreation {
|
||||||
return m.Source, nil
|
return m.Source, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupDefaultDriver(store *drivers.Store, root string, rootIDs idtools.IDPair) error {
|
func setupDefaultDriver(store *drivers.Store, root string, rootIDs idtools.Identity) error {
|
||||||
d, err := local.New(root, rootIDs)
|
d, err := local.New(root, rootIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error setting up default driver")
|
return errors.Wrap(err, "error setting up default driver")
|
||||||
|
|
|
@ -7,4 +7,4 @@ import (
|
||||||
"github.com/docker/docker/volume/drivers"
|
"github.com/docker/docker/volume/drivers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupDefaultDriver(_ *drivers.Store, _ string, _ idtools.IDPair) error { return nil }
|
func setupDefaultDriver(_ *drivers.Store, _ string, _ idtools.Identity) error { return nil }
|
||||||
|
|
|
@ -35,7 +35,7 @@ type VolumesService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVolumeService creates a new volume service
|
// NewVolumeService creates a new volume service
|
||||||
func NewVolumeService(root string, pg plugingetter.PluginGetter, rootIDs idtools.IDPair, logger volumeEventLogger) (*VolumesService, error) {
|
func NewVolumeService(root string, pg plugingetter.PluginGetter, rootIDs idtools.Identity, logger volumeEventLogger) (*VolumesService, error) {
|
||||||
ds := drivers.NewStore(pg)
|
ds := drivers.NewStore(pg)
|
||||||
if err := setupDefaultDriver(ds, root, rootIDs); err != nil {
|
if err := setupDefaultDriver(ds, root, rootIDs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -25,7 +25,7 @@ func TestLocalVolumeSize(t *testing.T) {
|
||||||
assert.Assert(t, err)
|
assert.Assert(t, err)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
l, err := local.New(dir, idtools.IDPair{UID: os.Getuid(), GID: os.Getegid()})
|
l, err := local.New(dir, idtools.Identity{UID: os.Getuid(), GID: os.Getegid()})
|
||||||
assert.Assert(t, err)
|
assert.Assert(t, err)
|
||||||
assert.Assert(t, ds.Register(l, volume.DefaultDriverName))
|
assert.Assert(t, ds.Register(l, volume.DefaultDriverName))
|
||||||
assert.Assert(t, ds.Register(testutils.NewFakeDriver("fake"), "fake"))
|
assert.Assert(t, ds.Register(testutils.NewFakeDriver("fake"), "fake"))
|
||||||
|
|
Loading…
Reference in a new issue