Vendor in latest containers/storage and containers/image

Containers/storage brings in support for UserNS ID Mappings
This means we can start experimenting with User NS Support in
podman

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #596
Approved by: TomSweeneyRedHat
This commit is contained in:
Daniel J Walsh
2018-04-05 15:34:31 -04:00
committed by Atomic Bot
parent fbc9d189b1
commit c3e2b00333
31 changed files with 3742 additions and 184 deletions

View File

@ -10,8 +10,8 @@ github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943
github.com/containerd/continuity master github.com/containerd/continuity master
github.com/containernetworking/cni v0.4.0 github.com/containernetworking/cni v0.4.0
github.com/containernetworking/plugins master github.com/containernetworking/plugins master
github.com/containers/image fbc14df0f25a15b456c4f7ec69a1afbb19395544 github.com/containers/image 54ea27515e713429b1ae1bf5a63002c15c25a206
github.com/containers/storage ff8a6d2bf496daf46ab1a153f783a0f6b8762a54 github.com/containers/storage 5d52f079f1709f0408a6c086c603d259b0e5da3e
github.com/coreos/go-systemd v14 github.com/coreos/go-systemd v14
github.com/cri-o/ocicni master github.com/cri-o/ocicni master
github.com/cyphar/filepath-securejoin v0.2.1 github.com/cyphar/filepath-securejoin v0.2.1

View File

@ -27,18 +27,25 @@ func newDockerClient(ctx *types.SystemContext) (*dockerclient.Client, error) {
// regardless of the values in the *tls.Config), and we would have to call sockets.ConfigureTransport. // regardless of the values in the *tls.Config), and we would have to call sockets.ConfigureTransport.
// //
// We don't really want to configure anything for unix:// sockets, so just pass a nil *http.Client. // We don't really want to configure anything for unix:// sockets, so just pass a nil *http.Client.
//
// Similarly, if we want to communicate over plain HTTP on a TCP socket, we also need to set
// TLSClientConfig to nil. This can be achieved by using the form `http://`
proto, _, _, err := dockerclient.ParseHost(host) proto, _, _, err := dockerclient.ParseHost(host)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var httpClient *http.Client var httpClient *http.Client
if proto != "unix" { if proto != "unix" {
if proto == "http" {
httpClient = httpConfig()
} else {
hc, err := tlsConfig(ctx) hc, err := tlsConfig(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
httpClient = hc httpClient = hc
} }
}
return dockerclient.NewClient(host, defaultAPIVersion, httpClient, nil) return dockerclient.NewClient(host, defaultAPIVersion, httpClient, nil)
} }
@ -67,3 +74,12 @@ func tlsConfig(ctx *types.SystemContext) (*http.Client, error) {
CheckRedirect: dockerclient.CheckRedirect, CheckRedirect: dockerclient.CheckRedirect,
}, nil }, nil
} }
func httpConfig() *http.Client {
return &http.Client{
Transport: &http.Transport{
TLSClientConfig: nil,
},
CheckRedirect: dockerclient.CheckRedirect,
}
}

View File

@ -544,7 +544,7 @@ func (s *storageImageDestination) Commit() error {
return errors.Errorf("error applying blob %q: content not found", blob.Digest) return errors.Errorf("error applying blob %q: content not found", blob.Digest)
} }
// Build the new layer using the diff, regardless of where it came from. // Build the new layer using the diff, regardless of where it came from.
layer, _, err := s.imageRef.transport.store.PutLayer(id, lastLayer, nil, "", false, diff) layer, _, err := s.imageRef.transport.store.PutLayer(id, lastLayer, nil, "", false, nil, diff)
if err != nil { if err != nil {
return errors.Wrapf(err, "error adding layer with blob %q", blob.Digest) return errors.Wrapf(err, "error adding layer with blob %q", blob.Digest)
} }

View File

@ -7,6 +7,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/truncindex" "github.com/containers/storage/pkg/truncindex"
@ -56,6 +57,12 @@ type Container struct {
// is set before using it. // is set before using it.
Created time.Time `json:"created,omitempty"` Created time.Time `json:"created,omitempty"`
// UIDMap and GIDMap are used for setting up a container's root
// filesystem for use inside of a user namespace where UID mapping is
// being used.
UIDMap []idtools.IDMap `json:"uidmap,omitempty"`
GIDMap []idtools.IDMap `json:"gidmap,omitempty"`
Flags map[string]interface{} `json:"flags,omitempty"` Flags map[string]interface{} `json:"flags,omitempty"`
} }
@ -70,7 +77,9 @@ type ContainerStore interface {
// random one if an empty value is supplied) and optional names, // random one if an empty value is supplied) and optional names,
// based on the specified image, using the specified layer as its // based on the specified image, using the specified layer as its
// read-write layer. // read-write layer.
Create(id string, names []string, image, layer, metadata string) (*Container, error) // The maps in the container's options structure are recorded for the
// convenience of the caller, nothing more.
Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error)
// SetNames updates the list of names associated with the container // SetNames updates the list of names associated with the container
// with the specified ID. // with the specified ID.
@ -117,6 +126,8 @@ func copyContainer(c *Container) *Container {
BigDataSizes: copyStringInt64Map(c.BigDataSizes), BigDataSizes: copyStringInt64Map(c.BigDataSizes),
BigDataDigests: copyStringDigestMap(c.BigDataDigests), BigDataDigests: copyStringDigestMap(c.BigDataDigests),
Created: c.Created, Created: c.Created,
UIDMap: copyIDMap(c.UIDMap),
GIDMap: copyIDMap(c.GIDMap),
Flags: copyStringInterfaceMap(c.Flags), Flags: copyStringInterfaceMap(c.Flags),
} }
} }
@ -252,7 +263,7 @@ func (r *containerStore) SetFlag(id string, flag string, value interface{}) erro
return r.Save() return r.Save()
} }
func (r *containerStore) Create(id string, names []string, image, layer, metadata string) (container *Container, err error) { func (r *containerStore) Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (container *Container, err error) {
if id == "" { if id == "" {
id = stringid.GenerateRandomID() id = stringid.GenerateRandomID()
_, idInUse := r.byid[id] _, idInUse := r.byid[id]
@ -282,6 +293,8 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
BigDataDigests: make(map[string]digest.Digest), BigDataDigests: make(map[string]digest.Digest),
Created: time.Now().UTC(), Created: time.Now().UTC(),
Flags: make(map[string]interface{}), Flags: make(map[string]interface{}),
UIDMap: copyIDMap(options.UIDMap),
GIDMap: copyIDMap(options.GIDMap),
} }
r.containers = append(r.containers, container) r.containers = append(r.containers, container)
r.byid[id] = container r.byid[id] = container

View File

@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: ./containers.go // source: containers.go
package storage package storage
@ -7,6 +7,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/containers/storage/pkg/idtools"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
fflib "github.com/pquerna/ffjson/fflib/v1" fflib "github.com/pquerna/ffjson/fflib/v1"
) )
@ -126,6 +127,46 @@ func (j *Container) MarshalJSONBuf(buf fflib.EncodingBuffer) error {
} }
buf.WriteByte(',') buf.WriteByte(',')
} }
if len(j.UIDMap) != 0 {
buf.WriteString(`"uidmap":`)
if j.UIDMap != nil {
buf.WriteString(`[`)
for i, v := range j.UIDMap {
if i != 0 {
buf.WriteString(`,`)
}
/* Struct fall back. type=idtools.IDMap kind=struct */
err = buf.Encode(&v)
if err != nil {
return err
}
}
buf.WriteString(`]`)
} else {
buf.WriteString(`null`)
}
buf.WriteByte(',')
}
if len(j.GIDMap) != 0 {
buf.WriteString(`"gidmap":`)
if j.GIDMap != nil {
buf.WriteString(`[`)
for i, v := range j.GIDMap {
if i != 0 {
buf.WriteString(`,`)
}
/* Struct fall back. type=idtools.IDMap kind=struct */
err = buf.Encode(&v)
if err != nil {
return err
}
}
buf.WriteString(`]`)
} else {
buf.WriteString(`null`)
}
buf.WriteByte(',')
}
if len(j.Flags) != 0 { if len(j.Flags) != 0 {
buf.WriteString(`"flags":`) buf.WriteString(`"flags":`)
/* Falling back. type=map[string]interface {} kind=map */ /* Falling back. type=map[string]interface {} kind=map */
@ -162,6 +203,10 @@ const (
ffjtContainerCreated ffjtContainerCreated
ffjtContainerUIDMap
ffjtContainerGIDMap
ffjtContainerFlags ffjtContainerFlags
) )
@ -183,6 +228,10 @@ var ffjKeyContainerBigDataDigests = []byte("big-data-digests")
var ffjKeyContainerCreated = []byte("created") var ffjKeyContainerCreated = []byte("created")
var ffjKeyContainerUIDMap = []byte("uidmap")
var ffjKeyContainerGIDMap = []byte("gidmap")
var ffjKeyContainerFlags = []byte("flags") var ffjKeyContainerFlags = []byte("flags")
// UnmarshalJSON umarshall json - template of ffjson // UnmarshalJSON umarshall json - template of ffjson
@ -280,6 +329,14 @@ mainparse:
goto mainparse goto mainparse
} }
case 'g':
if bytes.Equal(ffjKeyContainerGIDMap, kn) {
currentKey = ffjtContainerGIDMap
state = fflib.FFParse_want_colon
goto mainparse
}
case 'i': case 'i':
if bytes.Equal(ffjKeyContainerID, kn) { if bytes.Equal(ffjKeyContainerID, kn) {
@ -317,6 +374,14 @@ mainparse:
goto mainparse goto mainparse
} }
case 'u':
if bytes.Equal(ffjKeyContainerUIDMap, kn) {
currentKey = ffjtContainerUIDMap
state = fflib.FFParse_want_colon
goto mainparse
}
} }
if fflib.EqualFoldRight(ffjKeyContainerFlags, kn) { if fflib.EqualFoldRight(ffjKeyContainerFlags, kn) {
@ -325,6 +390,18 @@ mainparse:
goto mainparse goto mainparse
} }
if fflib.SimpleLetterEqualFold(ffjKeyContainerGIDMap, kn) {
currentKey = ffjtContainerGIDMap
state = fflib.FFParse_want_colon
goto mainparse
}
if fflib.SimpleLetterEqualFold(ffjKeyContainerUIDMap, kn) {
currentKey = ffjtContainerUIDMap
state = fflib.FFParse_want_colon
goto mainparse
}
if fflib.SimpleLetterEqualFold(ffjKeyContainerCreated, kn) { if fflib.SimpleLetterEqualFold(ffjKeyContainerCreated, kn) {
currentKey = ffjtContainerCreated currentKey = ffjtContainerCreated
state = fflib.FFParse_want_colon state = fflib.FFParse_want_colon
@ -423,6 +500,12 @@ mainparse:
case ffjtContainerCreated: case ffjtContainerCreated:
goto handle_Created goto handle_Created
case ffjtContainerUIDMap:
goto handle_UIDMap
case ffjtContainerGIDMap:
goto handle_GIDMap
case ffjtContainerFlags: case ffjtContainerFlags:
goto handle_Flags goto handle_Flags
@ -931,6 +1014,142 @@ handle_Created:
state = fflib.FFParse_after_value state = fflib.FFParse_after_value
goto mainparse goto mainparse
handle_UIDMap:
/* handler: j.UIDMap type=[]idtools.IDMap kind=slice quoted=false*/
{
{
if tok != fflib.FFTok_left_brace && tok != fflib.FFTok_null {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for ", tok))
}
}
if tok == fflib.FFTok_null {
j.UIDMap = nil
} else {
j.UIDMap = []idtools.IDMap{}
wantVal := true
for {
var tmpJUIDMap idtools.IDMap
tok = fs.Scan()
if tok == fflib.FFTok_error {
goto tokerror
}
if tok == fflib.FFTok_right_brace {
break
}
if tok == fflib.FFTok_comma {
if wantVal == true {
// TODO(pquerna): this isn't an ideal error message, this handles
// things like [,,,] as an array value.
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
}
continue
} else {
wantVal = true
}
/* handler: tmpJUIDMap type=idtools.IDMap kind=struct quoted=false*/
{
/* Falling back. type=idtools.IDMap kind=struct */
tbuf, err := fs.CaptureField(tok)
if err != nil {
return fs.WrapErr(err)
}
err = json.Unmarshal(tbuf, &tmpJUIDMap)
if err != nil {
return fs.WrapErr(err)
}
}
j.UIDMap = append(j.UIDMap, tmpJUIDMap)
wantVal = false
}
}
}
state = fflib.FFParse_after_value
goto mainparse
handle_GIDMap:
/* handler: j.GIDMap type=[]idtools.IDMap kind=slice quoted=false*/
{
{
if tok != fflib.FFTok_left_brace && tok != fflib.FFTok_null {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for ", tok))
}
}
if tok == fflib.FFTok_null {
j.GIDMap = nil
} else {
j.GIDMap = []idtools.IDMap{}
wantVal := true
for {
var tmpJGIDMap idtools.IDMap
tok = fs.Scan()
if tok == fflib.FFTok_error {
goto tokerror
}
if tok == fflib.FFTok_right_brace {
break
}
if tok == fflib.FFTok_comma {
if wantVal == true {
// TODO(pquerna): this isn't an ideal error message, this handles
// things like [,,,] as an array value.
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
}
continue
} else {
wantVal = true
}
/* handler: tmpJGIDMap type=idtools.IDMap kind=struct quoted=false*/
{
/* Falling back. type=idtools.IDMap kind=struct */
tbuf, err := fs.CaptureField(tok)
if err != nil {
return fs.WrapErr(err)
}
err = json.Unmarshal(tbuf, &tmpJGIDMap)
if err != nil {
return fs.WrapErr(err)
}
}
j.GIDMap = append(j.GIDMap, tmpJGIDMap)
wantVal = false
}
}
}
state = fflib.FFParse_after_value
goto mainparse
handle_Flags: handle_Flags:
/* handler: j.Flags type=map[string]interface {} kind=map quoted=false*/ /* handler: j.Flags type=map[string]interface {} kind=map quoted=false*/

View File

@ -167,7 +167,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
} }
} }
a.naiveDiff = graphdriver.NewNaiveDiffDriver(a, uidMaps, gidMaps) a.naiveDiff = graphdriver.NewNaiveDiffDriver(a, a)
return a, nil return a, nil
} }
@ -250,7 +250,7 @@ func (a *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
return fmt.Errorf("--storage-opt is not supported for aufs") return fmt.Errorf("--storage-opt is not supported for aufs")
} }
if err := a.createDirsFor(id); err != nil { if err := a.createDirsFor(id, parent); err != nil {
return err return err
} }
// Write the layers metadata // Write the layers metadata
@ -281,21 +281,26 @@ func (a *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
// createDirsFor creates two directories for the given id. // createDirsFor creates two directories for the given id.
// mnt and diff // mnt and diff
func (a *Driver) createDirsFor(id string) error { func (a *Driver) createDirsFor(id, parent string) error {
paths := []string{ paths := []string{
"mnt", "mnt",
"diff", "diff",
} }
rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps)
if err != nil {
return err
}
// Directory permission is 0755. // Directory permission is 0755.
// 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.MkdirAllAs(path.Join(a.rootPath(), p, id), 0755, rootUID, rootGID); err != nil { rootPair := idtools.NewIDMappingsFromMaps(a.uidMaps, a.gidMaps).RootPair()
if parent != "" {
st, err := system.Stat(path.Join(a.rootPath(), p, parent))
if err != nil {
return err
}
rootPair.UID = int(st.UID())
rootPair.GID = int(st.GID())
}
if err := idtools.MkdirAllAndChownNew(path.Join(a.rootPath(), p, id), os.FileMode(0755), rootPair); err != nil {
return err return err
} }
} }
@ -463,17 +468,21 @@ func (a *Driver) isParent(id, parent string) bool {
// Diff produces an archive of the changes between the specified // Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "". // layer and its parent layer which may be "".
func (a *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) { func (a *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error) {
if !a.isParent(id, parent) { if !a.isParent(id, parent) {
return a.naiveDiff.Diff(id, parent, mountLabel) return a.naiveDiff.Diff(id, idMappings, parent, parentMappings, mountLabel)
}
if idMappings == nil {
idMappings = &idtools.IDMappings{}
} }
// AUFS doesn't need the parent layer to produce a diff. // AUFS doesn't need the parent layer to produce a diff.
return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
Compression: archive.Uncompressed, Compression: archive.Uncompressed,
ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir}, ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir},
UIDMaps: a.uidMaps, UIDMaps: idMappings.UIDs(),
GIDMaps: a.gidMaps, GIDMaps: idMappings.GIDs(),
}) })
} }
@ -492,19 +501,22 @@ func (a *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
return fileGetNilCloser{storage.NewPathFileGetter(p)}, nil return fileGetNilCloser{storage.NewPathFileGetter(p)}, nil
} }
func (a *Driver) applyDiff(id string, diff io.Reader) error { func (a *Driver) applyDiff(id string, idMappings *idtools.IDMappings, diff io.Reader) error {
if idMappings == nil {
idMappings = &idtools.IDMappings{}
}
return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
UIDMaps: a.uidMaps, UIDMaps: idMappings.UIDs(),
GIDMaps: a.gidMaps, GIDMaps: idMappings.GIDs(),
}) })
} }
// DiffSize calculates the changes between the specified id // DiffSize calculates the changes between the specified id
// and its parent and returns the size in bytes of the changes // and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory. // relative to its base filesystem directory.
func (a *Driver) DiffSize(id, parent, mountLabel string) (size int64, err error) { func (a *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) {
if !a.isParent(id, parent) { if !a.isParent(id, parent) {
return a.naiveDiff.DiffSize(id, parent, mountLabel) return a.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel)
} }
// AUFS doesn't need the parent layer to calculate the diff size. // AUFS doesn't need the parent layer to calculate the diff size.
return directory.Size(path.Join(a.rootPath(), "diff", id)) return directory.Size(path.Join(a.rootPath(), "diff", id))
@ -513,24 +525,24 @@ func (a *Driver) DiffSize(id, parent, mountLabel string) (size int64, err error)
// ApplyDiff extracts the changeset from the given diff into the // ApplyDiff extracts the changeset from the given diff into the
// layer with the specified id and parent, returning the size of the // layer with the specified id and parent, returning the size of the
// new layer in bytes. // new layer in bytes.
func (a *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) { func (a *Driver) ApplyDiff(id string, idMappings *idtools.IDMappings, parent, mountLabel string, diff io.Reader) (size int64, err error) {
if !a.isParent(id, parent) { if !a.isParent(id, parent) {
return a.naiveDiff.ApplyDiff(id, parent, mountLabel, diff) return a.naiveDiff.ApplyDiff(id, idMappings, parent, mountLabel, diff)
} }
// AUFS doesn't need the parent id to apply the diff if it is the direct parent. // AUFS doesn't need the parent id to apply the diff if it is the direct parent.
if err = a.applyDiff(id, diff); err != nil { if err = a.applyDiff(id, idMappings, diff); err != nil {
return return
} }
return a.DiffSize(id, parent, mountLabel) return directory.Size(path.Join(a.rootPath(), "diff", id))
} }
// Changes produces a list of changes between the specified layer // Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes. // and its parent layer. If parent is "", then all changes will be ADD changes.
func (a *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error) { func (a *Driver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
if !a.isParent(id, parent) { if !a.isParent(id, parent) {
return a.naiveDiff.Changes(id, parent, mountLabel) return a.naiveDiff.Changes(id, idMappings, parent, parentMappings, mountLabel)
} }
// AUFS doesn't have snapshots, so we need to get changes from all parent // AUFS doesn't have snapshots, so we need to get changes from all parent
@ -689,3 +701,9 @@ func useDirperm() bool {
}) })
return enableDirperm return enableDirperm
} }
// UpdateLayerIDMap updates ID mappings in a layer from matching the ones
// specified by toContainer to those specified by toHost.
func (a *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
return fmt.Errorf("aufs doesn't support changing ID mappings")
}

View File

@ -90,7 +90,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
} }
} }
return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil return graphdriver.NewNaiveDiffDriver(driver, graphdriver.NewNaiveLayerIDMapUpdater(driver)), nil
} }
func parseOptions(opt []string) (btrfsOptions, bool, error) { func parseOptions(opt []string) (btrfsOptions, bool, error) {

167
vendor/github.com/containers/storage/drivers/chown.go generated vendored Normal file
View File

@ -0,0 +1,167 @@
package graphdriver
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/reexec"
)
const (
chownByMapsCmd = "storage-chown-by-maps"
)
func init() {
reexec.Register(chownByMapsCmd, chownByMapsMain)
}
func chownByMapsMain() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "requires mapping configuration on stdin and directory path")
os.Exit(1)
}
// Read and decode our configuration.
discreteMaps := [4][]idtools.IDMap{}
config := bytes.Buffer{}
if _, err := config.ReadFrom(os.Stdin); err != nil {
fmt.Fprintf(os.Stderr, "error reading configuration: %v", err)
os.Exit(1)
}
if err := json.Unmarshal(config.Bytes(), &discreteMaps); err != nil {
fmt.Fprintf(os.Stderr, "error decoding configuration: %v", err)
os.Exit(1)
}
// Try to chroot. This may not be possible, and on some systems that
// means we just Chdir() to the directory, so from here on we should be
// using relative paths.
if err := chrootOrChdir(os.Args[1]); err != nil {
fmt.Fprintf(os.Stderr, "error chrooting to %q: %v", os.Args[1], err)
os.Exit(1)
}
// Build the mapping objects.
toContainer := idtools.NewIDMappingsFromMaps(discreteMaps[0], discreteMaps[1])
if len(toContainer.UIDs()) == 0 && len(toContainer.GIDs()) == 0 {
toContainer = nil
}
toHost := idtools.NewIDMappingsFromMaps(discreteMaps[2], discreteMaps[3])
if len(toHost.UIDs()) == 0 && len(toHost.GIDs()) == 0 {
toHost = nil
}
chown := func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error walking to %q: %v", path, err)
}
sysinfo := info.Sys()
if st, ok := sysinfo.(*syscall.Stat_t); ok {
// Map an on-disk UID/GID pair from host to container
// using the first map, then back to the host using the
// second map. Skip that first step if they're 0, to
// compensate for cases where a parent layer should
// have had a mapped value, but didn't.
uid, gid := int(st.Uid), int(st.Gid)
if toContainer != nil {
pair := idtools.IDPair{
UID: uid,
GID: gid,
}
mappedUid, mappedGid, err := toContainer.ToContainer(pair)
if err != nil {
if (uid != 0) || (gid != 0) {
return fmt.Errorf("error mapping host ID pair %#v for %q to container: %v", pair, path, err)
}
mappedUid, mappedGid = uid, gid
}
uid, gid = mappedUid, mappedGid
}
if toHost != nil {
pair := idtools.IDPair{
UID: uid,
GID: gid,
}
mappedPair, err := toHost.ToHost(pair)
if err != nil {
return fmt.Errorf("error mapping container ID pair %#v for %q to host: %v", pair, path, err)
}
uid, gid = mappedPair.UID, mappedPair.GID
}
if uid != int(st.Uid) || gid != int(st.Gid) {
// Make the change.
if err := syscall.Lchown(path, uid, gid); err != nil {
return fmt.Errorf("%s: chown(%q): %v", os.Args[0], path, err)
}
}
}
return nil
}
if err := filepath.Walk(".", chown); err != nil {
fmt.Fprintf(os.Stderr, "error during chown: %v", err)
os.Exit(1)
}
os.Exit(0)
}
// ChownPathByMaps walks the filesystem tree, changing the ownership
// information using the toContainer and toHost mappings, using them to replace
// on-disk owner UIDs and GIDs which are "host" values in the first map with
// UIDs and GIDs for "host" values from the second map which correspond to the
// same "container" IDs.
func ChownPathByMaps(path string, toContainer, toHost *idtools.IDMappings) error {
if toContainer == nil {
toContainer = &idtools.IDMappings{}
}
if toHost == nil {
toHost = &idtools.IDMappings{}
}
config, err := json.Marshal([4][]idtools.IDMap{toContainer.UIDs(), toContainer.GIDs(), toHost.UIDs(), toHost.GIDs()})
if err != nil {
return err
}
cmd := reexec.Command(chownByMapsCmd, path)
cmd.Stdin = bytes.NewReader(config)
output, err := cmd.CombinedOutput()
if len(output) > 0 && err != nil {
return fmt.Errorf("%v: %s", err, string(output))
}
if err != nil {
return err
}
if len(output) > 0 {
return fmt.Errorf("%s", string(output))
}
return nil
}
type naiveLayerIDMapUpdater struct {
ProtoDriver
}
// NewNaiveLayerIDMapUpdater wraps the ProtoDriver in a LayerIDMapUpdater that
// uses ChownPathByMaps to update the ownerships in a layer's filesystem tree.
func NewNaiveLayerIDMapUpdater(driver ProtoDriver) LayerIDMapUpdater {
return &naiveLayerIDMapUpdater{ProtoDriver: driver}
}
// UpdateLayerIDMap walks the layer's filesystem tree, changing the ownership
// information using the toContainer and toHost mappings, using them to replace
// on-disk owner UIDs and GIDs which are "host" values in the first map with
// UIDs and GIDs for "host" values from the second map which correspond to the
// same "container" IDs.
func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
driver := n.ProtoDriver
layerFs, err := driver.Get(id, mountLabel)
if err != nil {
return err
}
defer func() {
driver.Put(id)
}()
return ChownPathByMaps(layerFs, toContainer, toHost)
}

View File

@ -0,0 +1,21 @@
// +build linux darwin freebsd solaris
package graphdriver
import (
"fmt"
"os"
"syscall"
)
// chrootOrChdir() is either a chdir() to the specified path, or a chroot() to the
// specified path followed by chdir() to the new root directory
func chrootOrChdir(path string) error {
if err := syscall.Chroot(path); err != nil {
return fmt.Errorf("error chrooting to %q: %v", path, err)
}
if err := syscall.Chdir(string(os.PathSeparator)); err != nil {
return fmt.Errorf("error changing to %q: %v", path, err)
}
return nil
}

View File

@ -0,0 +1,15 @@
package graphdriver
import (
"os"
"syscall"
)
// chrootOrChdir() is either a chdir() to the specified path, or a chroot() to the
// specified path followed by chdir() to the new root directory
func chrootOrChdir(path string) error {
if err := syscall.Chdir(path); err != nil {
return fmt.Errorf("error changing to %q: %v", path, err)
}
return nil
}

View File

@ -54,7 +54,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
locker: locker.New(), locker: locker.New(),
} }
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil
} }
func (d *Driver) String() string { func (d *Driver) String() string {

View File

@ -92,25 +92,39 @@ type ProtoDriver interface {
type DiffDriver interface { type DiffDriver interface {
// Diff produces an archive of the changes between the specified // Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "". // layer and its parent layer which may be "".
Diff(id, parent, mountLabel string) (io.ReadCloser, error) Diff(id string, idMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error)
// Changes produces a list of changes between the specified layer // Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes. // and its parent layer. If parent is "", then all changes will be ADD changes.
Changes(id, parent, mountLabel string) ([]archive.Change, error) Changes(id string, idMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error)
// ApplyDiff extracts the changeset from the given diff into the // ApplyDiff extracts the changeset from the given diff into the
// layer with the specified id and parent, returning the size of the // layer with the specified id and parent, returning the size of the
// new layer in bytes. // new layer in bytes.
// The io.Reader must be an uncompressed stream. // The io.Reader must be an uncompressed stream.
ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) ApplyDiff(id string, idMappings *idtools.IDMappings, parent string, mountLabel string, diff io.Reader) (size int64, err error)
// DiffSize calculates the changes between the specified id // DiffSize calculates the changes between the specified id
// and its parent and returns the size in bytes of the changes // and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory. // relative to its base filesystem directory.
DiffSize(id, parent, mountLabel string) (size int64, err error) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, mountLabel string) (size int64, err error)
}
// LayerIDMapUpdater is the interface that implements ID map changes for layers.
type LayerIDMapUpdater interface {
// UpdateLayerIDMap walks the layer's filesystem tree, changing the ownership
// information using the toContainer and toHost mappings, using them to replace
// on-disk owner UIDs and GIDs which are "host" values in the first map with
// UIDs and GIDs for "host" values from the second map which correspond to the
// same "container" IDs. This method should only be called after a layer is
// first created and populated, and before it is mounted, as other changes made
// relative to a parent layer, but before this method is called, may be discarded
// by Diff().
UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error
} }
// Driver is the interface for layered/snapshot file system drivers. // Driver is the interface for layered/snapshot file system drivers.
type Driver interface { type Driver interface {
ProtoDriver ProtoDriver
DiffDriver DiffDriver
LayerIDMapUpdater
} }
// Capabilities defines a list of capabilities a driver may implement. // Capabilities defines a list of capabilities a driver may implement.

View File

@ -24,29 +24,33 @@ var (
// Notably, the AUFS driver doesn't need to be wrapped like this. // Notably, the AUFS driver doesn't need to be wrapped like this.
type NaiveDiffDriver struct { type NaiveDiffDriver struct {
ProtoDriver ProtoDriver
uidMaps []idtools.IDMap LayerIDMapUpdater
gidMaps []idtools.IDMap
} }
// NewNaiveDiffDriver returns a fully functional driver that wraps the // NewNaiveDiffDriver returns a fully functional driver that wraps the
// given ProtoDriver and adds the capability of the following methods which // given ProtoDriver and adds the capability of the following methods which
// it may or may not support on its own: // it may or may not support on its own:
// Diff(id, parent, mountLabel string) (io.ReadCloser, error) // Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error)
// Changes(id, parent, mountLabel string) ([]archive.Change, error) // Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error)
// ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) // ApplyDiff(id string, idMappings *idtools.IDMappings, parent, mountLabel string, diff io.Reader) (size int64, err error)
// DiffSize(id, parent, mountLabel string) (size int64, err error) // DiffSize(id string, idMappings *idtools.IDMappings, parent, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error)
func NewNaiveDiffDriver(driver ProtoDriver, uidMaps, gidMaps []idtools.IDMap) Driver { func NewNaiveDiffDriver(driver ProtoDriver, updater LayerIDMapUpdater) Driver {
return &NaiveDiffDriver{ProtoDriver: driver, return &NaiveDiffDriver{ProtoDriver: driver, LayerIDMapUpdater: updater}
uidMaps: uidMaps,
gidMaps: gidMaps}
} }
// Diff produces an archive of the changes between the specified // Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "". // layer and its parent layer which may be "".
func (gdw *NaiveDiffDriver) Diff(id, parent, mountLabel string) (arch io.ReadCloser, err error) { func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (arch io.ReadCloser, err error) {
startTime := time.Now() startTime := time.Now()
driver := gdw.ProtoDriver driver := gdw.ProtoDriver
if idMappings == nil {
idMappings = &idtools.IDMappings{}
}
if parentMappings == nil {
parentMappings = &idtools.IDMappings{}
}
layerFs, err := driver.Get(id, mountLabel) layerFs, err := driver.Get(id, mountLabel)
if err != nil { if err != nil {
return nil, err return nil, err
@ -59,7 +63,11 @@ func (gdw *NaiveDiffDriver) Diff(id, parent, mountLabel string) (arch io.ReadClo
}() }()
if parent == "" { if parent == "" {
archive, err := archive.Tar(layerFs, archive.Uncompressed) archive, err := archive.TarWithOptions(layerFs, &archive.TarOptions{
Compression: archive.Uncompressed,
UIDMaps: idMappings.UIDs(),
GIDMaps: idMappings.GIDs(),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -76,12 +84,12 @@ func (gdw *NaiveDiffDriver) Diff(id, parent, mountLabel string) (arch io.ReadClo
} }
defer driver.Put(parent) defer driver.Put(parent)
changes, err := archive.ChangesDirs(layerFs, parentFs) changes, err := archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
if err != nil { if err != nil {
return nil, err return nil, err
} }
archive, err := archive.ExportChanges(layerFs, changes, gdw.uidMaps, gdw.gidMaps) archive, err := archive.ExportChanges(layerFs, changes, idMappings.UIDs(), idMappings.GIDs())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -101,9 +109,16 @@ func (gdw *NaiveDiffDriver) Diff(id, parent, mountLabel string) (arch io.ReadClo
// Changes produces a list of changes between the specified layer // Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes. // and its parent layer. If parent is "", then all changes will be ADD changes.
func (gdw *NaiveDiffDriver) Changes(id, parent, mountLabel string) ([]archive.Change, error) { func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
driver := gdw.ProtoDriver driver := gdw.ProtoDriver
if idMappings == nil {
idMappings = &idtools.IDMappings{}
}
if parentMappings == nil {
parentMappings = &idtools.IDMappings{}
}
layerFs, err := driver.Get(id, mountLabel) layerFs, err := driver.Get(id, mountLabel)
if err != nil { if err != nil {
return nil, err return nil, err
@ -120,15 +135,19 @@ func (gdw *NaiveDiffDriver) Changes(id, parent, mountLabel string) ([]archive.Ch
defer driver.Put(parent) defer driver.Put(parent)
} }
return archive.ChangesDirs(layerFs, parentFs) return archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings)
} }
// ApplyDiff extracts the changeset from the given diff into the // ApplyDiff extracts the changeset from the given diff into the
// layer with the specified id and parent, returning the size of the // layer with the specified id and parent, returning the size of the
// new layer in bytes. // new layer in bytes.
func (gdw *NaiveDiffDriver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) { func (gdw *NaiveDiffDriver) ApplyDiff(id string, applyMappings *idtools.IDMappings, parent, mountLabel string, diff io.Reader) (size int64, err error) {
driver := gdw.ProtoDriver driver := gdw.ProtoDriver
if applyMappings == nil {
applyMappings = &idtools.IDMappings{}
}
// Mount the root filesystem so we can apply the diff/layer. // Mount the root filesystem so we can apply the diff/layer.
layerFs, err := driver.Get(id, mountLabel) layerFs, err := driver.Get(id, mountLabel)
if err != nil { if err != nil {
@ -136,8 +155,11 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent, mountLabel string, diff io.Rea
} }
defer driver.Put(id) defer driver.Put(id)
options := &archive.TarOptions{UIDMaps: gdw.uidMaps, options := &archive.TarOptions{}
GIDMaps: gdw.gidMaps} if applyMappings != nil {
options.UIDMaps = applyMappings.UIDs()
options.GIDMaps = applyMappings.GIDs()
}
start := time.Now().UTC() start := time.Now().UTC()
logrus.Debug("Start untar layer") logrus.Debug("Start untar layer")
if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil { if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil {
@ -151,10 +173,17 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent, mountLabel string, diff io.Rea
// DiffSize calculates the changes between the specified layer // DiffSize calculates the changes between the specified layer
// and its parent and returns the size in bytes of the changes // and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory. // relative to its base filesystem directory.
func (gdw *NaiveDiffDriver) DiffSize(id, parent, mountLabel string) (size int64, err error) { func (gdw *NaiveDiffDriver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) {
driver := gdw.ProtoDriver driver := gdw.ProtoDriver
changes, err := gdw.Changes(id, parent, mountLabel) if idMappings == nil {
idMappings = &idtools.IDMappings{}
}
if parentMappings == nil {
parentMappings = &idtools.IDMappings{}
}
changes, err := gdw.Changes(id, idMappings, parent, parentMappings, mountLabel)
if err != nil { if err != nil {
return return
} }

View File

@ -169,7 +169,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
options: *opts, options: *opts,
} }
d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, d)
if backingFs == "xfs" { if backingFs == "xfs" {
// Try to enable project quota support over xfs. // Try to enable project quota support over xfs.
@ -267,16 +267,22 @@ func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGI
_ = idtools.MkdirAs(lower2Dir, 0700, rootUID, rootGID) _ = idtools.MkdirAs(lower2Dir, 0700, rootUID, rootGID)
flags := fmt.Sprintf("lowerdir=%s:%s", lower1Dir, lower2Dir) flags := fmt.Sprintf("lowerdir=%s:%s", lower1Dir, lower2Dir)
if len(flags) < unix.Getpagesize() { if len(flags) < unix.Getpagesize() {
if mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags) == nil { err := mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags)
if err == nil {
logrus.Debugf("overlay test mount with multiple lowers succeeded") logrus.Debugf("overlay test mount with multiple lowers succeeded")
return supportsDType, nil return supportsDType, nil
} else {
logrus.Debugf("overlay test mount with multiple lowers failed %v", err)
} }
} }
flags = fmt.Sprintf("lowerdir=%s", lower1Dir) flags = fmt.Sprintf("lowerdir=%s", lower1Dir)
if len(flags) < unix.Getpagesize() { if len(flags) < unix.Getpagesize() {
if mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags) == nil { err := mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags)
if err == nil {
logrus.Errorf("overlay test mount with multiple lowers failed, but succeeded with a single lower") logrus.Errorf("overlay test mount with multiple lowers failed, but succeeded with a single lower")
return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "kernel too old to provide multiple lowers feature for overlay") return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "kernel too old to provide multiple lowers feature for overlay")
} else {
logrus.Debugf("overlay test mount with a single lower failed %v", err)
} }
} }
logrus.Errorf("'overlay' is not supported over %s at %q", backingFs, home) logrus.Errorf("'overlay' is not supported over %s at %q", backingFs, home)
@ -387,6 +393,14 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil {
return err return err
} }
if parent != "" {
st, err := system.Stat(d.dir(parent))
if err != nil {
return err
}
rootUID = int(st.UID())
rootGID = int(st.GID())
}
if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil {
return err return err
} }
@ -562,8 +576,32 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) {
return "", err return "", err
} }
newlowers := "" // absLowers is the list of lowers as absolute paths, which works well with additional stores.
absLowers := []string{}
// relLowers is the list of lowers as paths relative to the driver's home directory.
relLowers := []string{}
// Check if $link/../diff{1-*} exist. If they do, add them, in order, as the front of the lowers
// lists that we're building. "diff" itself is the upper, so it won't be in the lists.
link, err := ioutil.ReadFile(path.Join(dir, "link"))
if err != nil {
return "", err
}
diffN := 1
_, err = os.Stat(filepath.Join(dir, nameWithSuffix("diff", diffN)))
for err == nil {
absLowers = append(absLowers, filepath.Join(dir, nameWithSuffix("diff", diffN)))
relLowers = append(relLowers, dumbJoin(string(link), "..", nameWithSuffix("diff", diffN)))
diffN++
_, err = os.Stat(filepath.Join(dir, nameWithSuffix("diff", diffN)))
}
// For each lower, resolve its path, and append it and any additional diffN
// directories to the lowers list.
for _, l := range strings.Split(string(lowers), ":") { for _, l := range strings.Split(string(lowers), ":") {
if l == "" {
continue
}
lower := "" lower := ""
newpath := path.Join(d.home, l) newpath := path.Join(d.home, l)
if _, err := os.Stat(newpath); err != nil { if _, err := os.Stat(newpath); err != nil {
@ -580,15 +618,23 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) {
} else { } else {
lower = newpath lower = newpath
} }
if newlowers == "" { absLowers = append(absLowers, lower)
newlowers = lower relLowers = append(relLowers, l)
} else { diffN = 1
newlowers = newlowers + ":" + lower _, err = os.Stat(dumbJoin(lower, "..", nameWithSuffix("diff", diffN)))
for err == nil {
absLowers = append(absLowers, dumbJoin(lower, "..", nameWithSuffix("diff", diffN)))
relLowers = append(relLowers, dumbJoin(l, "..", nameWithSuffix("diff", diffN)))
diffN++
_, err = os.Stat(dumbJoin(lower, "..", nameWithSuffix("diff", diffN)))
} }
} }
if len(lowers) == 0 {
newlowers = path.Join(dir, "empty") // If the lowers list is still empty, use an empty lower so that we can still force an
lowers = []byte(newlowers) // SELinux context for the mount.
if len(absLowers) == 0 {
absLowers = append(absLowers, path.Join(dir, "empty"))
relLowers = append(relLowers, path.Join(id, "empty"))
} }
mergedDir := path.Join(dir, "merged") mergedDir := path.Join(dir, "merged")
@ -606,7 +652,7 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) {
}() }()
workDir := path.Join(dir, "work") workDir := path.Join(dir, "work")
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", newlowers, diffDir, workDir) opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workDir)
mountData := label.FormatMountLabel(opts, mountLabel) mountData := label.FormatMountLabel(opts, mountLabel)
mount := unix.Mount mount := unix.Mount
mountTarget := mergedDir mountTarget := mergedDir
@ -619,7 +665,7 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) {
// smaller at the expense of requiring a fork exec to chroot. // smaller at the expense of requiring a fork exec to chroot.
if len(mountData) > pageSize { if len(mountData) > pageSize {
//FIXME: We need to figure out to get this to work with additional stores //FIXME: We need to figure out to get this to work with additional stores
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work")) opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), path.Join(id, "diff"), path.Join(id, "work"))
mountData = label.FormatMountLabel(opts, mountLabel) mountData = label.FormatMountLabel(opts, mountLabel)
if len(mountData) > pageSize { if len(mountData) > pageSize {
return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
@ -697,9 +743,13 @@ func (d *Driver) isParent(id, parent string) bool {
} }
// ApplyDiff applies the new layer into a root // ApplyDiff applies the new layer into a root
func (d *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) { func (d *Driver) ApplyDiff(id string, idMappings *idtools.IDMappings, parent string, mountLabel string, diff io.Reader) (size int64, err error) {
if !d.isParent(id, parent) { if !d.isParent(id, parent) {
return d.naiveDiff.ApplyDiff(id, parent, mountLabel, diff) return d.naiveDiff.ApplyDiff(id, idMappings, parent, mountLabel, diff)
}
if idMappings == nil {
idMappings = &idtools.IDMappings{}
} }
applyDir := d.getDiffPath(id) applyDir := d.getDiffPath(id)
@ -707,8 +757,8 @@ func (d *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size
logrus.Debugf("Applying tar in %s", applyDir) logrus.Debugf("Applying tar in %s", applyDir)
// Overlay doesn't need the parent id to apply the diff // Overlay doesn't need the parent id to apply the diff
if err := untar(diff, applyDir, &archive.TarOptions{ if err := untar(diff, applyDir, &archive.TarOptions{
UIDMaps: d.uidMaps, UIDMaps: idMappings.UIDs(),
GIDMaps: d.gidMaps, GIDMaps: idMappings.GIDs(),
WhiteoutFormat: archive.OverlayWhiteoutFormat, WhiteoutFormat: archive.OverlayWhiteoutFormat,
}); err != nil { }); err != nil {
return 0, err return 0, err
@ -726,18 +776,22 @@ func (d *Driver) getDiffPath(id string) string {
// DiffSize calculates the changes between the specified id // DiffSize calculates the changes between the specified id
// and its parent and returns the size in bytes of the changes // and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory. // relative to its base filesystem directory.
func (d *Driver) DiffSize(id, parent, mountLabel string) (size int64, err error) { func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) { if useNaiveDiff(d.home) || !d.isParent(id, parent) {
return d.naiveDiff.DiffSize(id, parent, mountLabel) return d.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel)
} }
return directory.Size(d.getDiffPath(id)) return directory.Size(d.getDiffPath(id))
} }
// Diff produces an archive of the changes between the specified // Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "". // layer and its parent layer which may be "".
func (d *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) { func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) { if useNaiveDiff(d.home) || !d.isParent(id, parent) {
return d.naiveDiff.Diff(id, parent, mountLabel) return d.naiveDiff.Diff(id, idMappings, parent, parentMappings, mountLabel)
}
if idMappings == nil {
idMappings = &idtools.IDMappings{}
} }
lowerDirs, err := d.getLowerDirs(id) lowerDirs, err := d.getLowerDirs(id)
@ -749,8 +803,8 @@ func (d *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) {
logrus.Debugf("Tar with options on %s", diffPath) logrus.Debugf("Tar with options on %s", diffPath)
return archive.TarWithOptions(diffPath, &archive.TarOptions{ return archive.TarWithOptions(diffPath, &archive.TarOptions{
Compression: archive.Uncompressed, Compression: archive.Uncompressed,
UIDMaps: d.uidMaps, UIDMaps: idMappings.UIDs(),
GIDMaps: d.gidMaps, GIDMaps: idMappings.GIDs(),
WhiteoutFormat: archive.OverlayWhiteoutFormat, WhiteoutFormat: archive.OverlayWhiteoutFormat,
WhiteoutData: lowerDirs, WhiteoutData: lowerDirs,
}) })
@ -758,9 +812,9 @@ func (d *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) {
// Changes produces a list of changes between the specified layer // Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes. // and its parent layer. If parent is "", then all changes will be ADD changes.
func (d *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error) { func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) { if useNaiveDiff(d.home) || !d.isParent(id, parent) {
return d.naiveDiff.Changes(id, parent, mountLabel) return d.naiveDiff.Changes(id, idMappings, parent, parentMappings, mountLabel)
} }
// Overlay doesn't have snapshots, so we need to get changes from all parent // Overlay doesn't have snapshots, so we need to get changes from all parent
// layers. // layers.
@ -777,3 +831,73 @@ func (d *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error
func (d *Driver) AdditionalImageStores() []string { func (d *Driver) AdditionalImageStores() []string {
return d.options.imageStores return d.options.imageStores
} }
// UpdateLayerIDMap updates ID mappings in a from matching the ones specified
// by toContainer to those specified by toHost.
func (d *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
var err error
dir := d.dir(id)
diffDir := filepath.Join(dir, "diff")
rootUID, rootGID := 0, 0
if toHost != nil {
rootUID, rootGID, err = idtools.GetRootUIDGID(toHost.UIDs(), toHost.GIDs())
if err != nil {
return err
}
}
// Mount the new layer and handle ownership changes and possible copy_ups in it.
layerFs, err := d.Get(id, mountLabel)
if err != nil {
return err
}
err = graphdriver.ChownPathByMaps(layerFs, toContainer, toHost)
if err != nil {
if err2 := d.Put(id); err2 != nil {
logrus.Errorf("%v; error unmounting %v: %v", err, id, err2)
}
return err
}
if err = d.Put(id); err != nil {
return err
}
// Rotate the diff directories.
i := 0
_, err = os.Stat(nameWithSuffix(diffDir, i))
for err == nil {
i++
_, err = os.Stat(nameWithSuffix(diffDir, i))
}
for i > 0 {
err = os.Rename(nameWithSuffix(diffDir, i-1), nameWithSuffix(diffDir, i))
if err != nil {
return err
}
i--
}
// Re-create the directory that we're going to use as the upper layer.
if err := idtools.MkdirAs(diffDir, 0755, rootUID, rootGID); err != nil {
return err
}
return nil
}
// dumbJoin is more or less a dumber version of filepath.Join, but one which
// won't Clean() the path, allowing us to append ".." as a component and trust
// pathname resolution to do some non-obvious work.
func dumbJoin(names ...string) string {
if len(names) == 0 {
return string(os.PathSeparator)
}
return strings.Join(names, string(os.PathSeparator))
}
func nameWithSuffix(name string, number int) string {
if number == 0 {
return name
}
return fmt.Sprintf("%s%d", name, number)
}

View File

@ -43,7 +43,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
continue continue
} }
} }
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil
} }
// Driver holds information about the driver, home directory of the driver. // Driver holds information about the driver, home directory of the driver.
@ -91,6 +91,14 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
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
} }
if parent != "" {
st, err := system.Stat(d.dir(parent))
if err != nil {
return err
}
rootIDs.UID = int(st.UID())
rootIDs.GID = int(st.GID())
}
if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil { if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil {
return err return err
} }

View File

@ -472,7 +472,7 @@ func (d *Driver) Cleanup() error {
// Diff produces an archive of the changes between the specified // Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "". // layer and its parent layer which may be "".
// The layer should be mounted when calling this function // The layer should be mounted when calling this function
func (d *Driver) Diff(id, parent, mountLabel string) (_ io.ReadCloser, err error) { func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (_ io.ReadCloser, err error) {
panicIfUsedByLcow() panicIfUsedByLcow()
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
if err != nil { if err != nil {
@ -509,7 +509,7 @@ func (d *Driver) Diff(id, parent, mountLabel string) (_ io.ReadCloser, err error
// Changes produces a list of changes between the specified layer // Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes. // and its parent layer. If parent is "", then all changes will be ADD changes.
// The layer should not be mounted when calling this function. // The layer should not be mounted when calling this function.
func (d *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error) { func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
panicIfUsedByLcow() panicIfUsedByLcow()
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
if err != nil { if err != nil {
@ -565,7 +565,7 @@ func (d *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error
// layer with the specified id and parent, returning the size of the // layer with the specified id and parent, returning the size of the
// new layer in bytes. // new layer in bytes.
// The layer should not be mounted when calling this function // The layer should not be mounted when calling this function
func (d *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (int64, error) { func (d *Driver) ApplyDiff(id string, idMappings *idtools.IDMappings, parent, mountLabel string, diff io.Reader) (int64, error) {
panicIfUsedByLcow() panicIfUsedByLcow()
var layerChain []string var layerChain []string
if parent != "" { if parent != "" {
@ -600,14 +600,14 @@ func (d *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (int64
// DiffSize calculates the changes between the specified layer // DiffSize calculates the changes between the specified layer
// and its parent and returns the size in bytes of the changes // and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory. // relative to its base filesystem directory.
func (d *Driver) DiffSize(id, parent, mountLabel string) (size int64, err error) { func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) {
panicIfUsedByLcow() panicIfUsedByLcow()
rPId, err := d.resolveID(parent) rPId, err := d.resolveID(parent)
if err != nil { if err != nil {
return return
} }
changes, err := d.Changes(id, rPId, mountLabel) changes, err := d.Changes(id, idMappings, rPId, parentMappings, mountLabel)
if err != nil { if err != nil {
return return
} }
@ -940,6 +940,17 @@ func (d *Driver) AdditionalImageStores() []string {
return nil return nil
} }
// AdditionalImageStores returns additional image stores supported by the driver
func (d *Driver) AdditionalImageStores() []string {
return nil
}
// UpdateLayerIDMap changes ownerships in the layer's filesystem tree from
// matching those in toContainer to matching those in toHost.
func (d *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
return fmt.Errorf("windows doesn't support changing ID mappings")
}
type storageOptions struct { type storageOptions struct {
size uint64 size uint64
} }

View File

@ -119,7 +119,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
gidMaps: gidMaps, gidMaps: gidMaps,
ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
} }
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil
} }
func parseOptions(opt []string) (zfsOptions, error) { func parseOptions(opt []string) (zfsOptions, error) {

View File

@ -53,4 +53,6 @@ var (
ErrInvalidBigDataName = errors.New("not a valid name for a big data item") ErrInvalidBigDataName = errors.New("not a valid name for a big data item")
// ErrDigestUnknown indicates that we were unable to compute the digest of a specified item. // ErrDigestUnknown indicates that we were unable to compute the digest of a specified item.
ErrDigestUnknown = errors.New("could not compute digest of item") ErrDigestUnknown = errors.New("could not compute digest of item")
// ErrLayerNotMounted is returned when the requested information can only be computed for a mounted layer, and the layer is not mounted.
ErrLayerNotMounted = errors.New("layer is not mounted")
) )

View File

@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: ./images.go // source: images.go
package storage package storage

View File

@ -8,12 +8,16 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"sort"
"time" "time"
drivers "github.com/containers/storage/drivers" drivers "github.com/containers/storage/drivers"
"github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/system"
"github.com/containers/storage/pkg/truncindex" "github.com/containers/storage/pkg/truncindex"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -93,6 +97,11 @@ type Layer struct {
// Flags is arbitrary data about the layer. // Flags is arbitrary data about the layer.
Flags map[string]interface{} `json:"flags,omitempty"` Flags map[string]interface{} `json:"flags,omitempty"`
// UIDMap and GIDMap are used for setting up a layer's contents
// for use inside of a user namespace where UID mapping is being used.
UIDMap []idtools.IDMap `json:"uidmap,omitempty"`
GIDMap []idtools.IDMap `json:"gidmap,omitempty"`
} }
type layerMountPoint struct { type layerMountPoint struct {
@ -178,13 +187,13 @@ type LayerStore interface {
// underlying drivers can accept a "size" option. At this time, most // underlying drivers can accept a "size" option. At this time, most
// underlying drivers do not themselves distinguish between writeable // underlying drivers do not themselves distinguish between writeable
// and read-only layers. // and read-only layers.
Create(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool) (*Layer, error) Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (*Layer, error)
// CreateWithFlags combines the functions of Create and SetFlag. // CreateWithFlags combines the functions of Create and SetFlag.
CreateWithFlags(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}) (layer *Layer, err error) CreateWithFlags(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}) (layer *Layer, err error)
// Put combines the functions of CreateWithFlags and ApplyDiff. // Put combines the functions of CreateWithFlags and ApplyDiff.
Put(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}, diff io.Reader) (*Layer, int64, error) Put(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (*Layer, int64, error)
// SetNames replaces the list of names associated with a layer with the // SetNames replaces the list of names associated with a layer with the
// supplied values. // supplied values.
@ -204,6 +213,10 @@ type LayerStore interface {
// Unmount unmounts a layer when it is no longer in use. // Unmount unmounts a layer when it is no longer in use.
Unmount(id string) error Unmount(id string) error
// ParentOwners returns the UIDs and GIDs of parents of the layer's mountpoint
// for which the layer's UID and GID maps don't contain corresponding entries.
ParentOwners(id string) (uids, gids []int, err error)
// ApplyDiff reads a tarstream which was created by a previous call to Diff and // ApplyDiff reads a tarstream which was created by a previous call to Diff and
// applies its changes to a specified layer. // applies its changes to a specified layer.
ApplyDiff(to string, diff io.Reader) (int64, error) ApplyDiff(to string, diff io.Reader) (int64, error)
@ -221,6 +234,8 @@ type layerStore struct {
bymount map[string]*Layer bymount map[string]*Layer
bycompressedsum map[digest.Digest][]string bycompressedsum map[digest.Digest][]string
byuncompressedsum map[digest.Digest][]string byuncompressedsum map[digest.Digest][]string
uidMap []idtools.IDMap
gidMap []idtools.IDMap
} }
func copyLayer(l *Layer) *Layer { func copyLayer(l *Layer) *Layer {
@ -239,6 +254,8 @@ func copyLayer(l *Layer) *Layer {
UncompressedSize: l.UncompressedSize, UncompressedSize: l.UncompressedSize,
CompressionType: l.CompressionType, CompressionType: l.CompressionType,
Flags: copyStringInterfaceMap(l.Flags), Flags: copyStringInterfaceMap(l.Flags),
UIDMap: copyIDMap(l.UIDMap),
GIDMap: copyIDMap(l.GIDMap),
} }
} }
@ -382,7 +399,7 @@ func (r *layerStore) Save() error {
return ioutils.AtomicWriteFile(mpath, jmdata, 0600) return ioutils.AtomicWriteFile(mpath, jmdata, 0600)
} }
func newLayerStore(rundir string, layerdir string, driver drivers.Driver) (LayerStore, error) { func newLayerStore(rundir string, layerdir string, driver drivers.Driver, uidMap, gidMap []idtools.IDMap) (LayerStore, error) {
if err := os.MkdirAll(rundir, 0700); err != nil { if err := os.MkdirAll(rundir, 0700); err != nil {
return nil, err return nil, err
} }
@ -403,6 +420,8 @@ func newLayerStore(rundir string, layerdir string, driver drivers.Driver) (Layer
byid: make(map[string]*Layer), byid: make(map[string]*Layer),
bymount: make(map[string]*Layer), bymount: make(map[string]*Layer),
byname: make(map[string]*Layer), byname: make(map[string]*Layer),
uidMap: copyIDMap(uidMap),
gidMap: copyIDMap(gidMap),
} }
if err := rlstore.Load(); err != nil { if err := rlstore.Load(); err != nil {
return nil, err return nil, err
@ -489,7 +508,7 @@ func (r *layerStore) Status() ([][2]string, error) {
return r.driver.Status(), nil return r.driver.Status(), nil
} }
func (r *layerStore) Put(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}, diff io.Reader) (layer *Layer, size int64, err error) { func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (layer *Layer, size int64, err error) {
if !r.IsReadWrite() { if !r.IsReadWrite() {
return nil, -1, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new layers at %q", r.layerspath()) return nil, -1, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new layers at %q", r.layerspath())
} }
@ -500,11 +519,6 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
if err := os.MkdirAll(r.layerdir, 0700); err != nil { if err := os.MkdirAll(r.layerdir, 0700); err != nil {
return nil, -1, err return nil, -1, err
} }
if parent != "" {
if parentLayer, ok := r.lookup(parent); ok {
parent = parentLayer.ID
}
}
if id == "" { if id == "" {
id = stringid.GenerateRandomID() id = stringid.GenerateRandomID()
_, idInUse := r.byid[id] _, idInUse := r.byid[id]
@ -522,6 +536,15 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
return nil, -1, ErrDuplicateName return nil, -1, ErrDuplicateName
} }
} }
parent := ""
var parentMappings *idtools.IDMappings
if parentLayer != nil {
parent = parentLayer.ID
parentMappings = idtools.NewIDMappingsFromMaps(parentLayer.UIDMap, parentLayer.GIDMap)
} else {
parentMappings = &idtools.IDMappings{}
}
idMappings := idtools.NewIDMappingsFromMaps(moreOptions.UIDMap, moreOptions.GIDMap)
opts := drivers.CreateOpts{ opts := drivers.CreateOpts{
MountLabel: mountLabel, MountLabel: mountLabel,
StorageOpt: options, StorageOpt: options,
@ -531,6 +554,15 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
} else { } else {
err = r.driver.Create(id, parent, &opts) err = r.driver.Create(id, parent, &opts)
} }
if !reflect.DeepEqual(parentMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(parentMappings.GIDs(), idMappings.GIDs()) {
err = r.driver.UpdateLayerIDMap(id, parentMappings, idMappings, mountLabel)
if err != nil {
// We don't have a record of this layer, but at least
// try to clean it up underneath us.
r.driver.Remove(id)
return nil, -1, err
}
}
if err == nil { if err == nil {
layer = &Layer{ layer = &Layer{
ID: id, ID: id,
@ -539,6 +571,8 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
MountLabel: mountLabel, MountLabel: mountLabel,
Created: time.Now().UTC(), Created: time.Now().UTC(),
Flags: make(map[string]interface{}), Flags: make(map[string]interface{}),
UIDMap: copyIDMap(moreOptions.UIDMap),
GIDMap: copyIDMap(moreOptions.GIDMap),
} }
r.layers = append(r.layers, layer) r.layers = append(r.layers, layer)
r.idindex.Add(id) r.idindex.Add(id)
@ -580,13 +614,13 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
return copyLayer(layer), size, err return copyLayer(layer), size, err
} }
func (r *layerStore) CreateWithFlags(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}) (layer *Layer, err error) { func (r *layerStore) CreateWithFlags(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}) (layer *Layer, err error) {
layer, _, err = r.Put(id, parent, names, mountLabel, options, writeable, flags, nil) layer, _, err = r.Put(id, parent, names, mountLabel, options, moreOptions, writeable, flags, nil)
return layer, err return layer, err
} }
func (r *layerStore) Create(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool) (layer *Layer, err error) { func (r *layerStore) Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (layer *Layer, err error) {
return r.CreateWithFlags(id, parent, names, mountLabel, options, writeable, nil) return r.CreateWithFlags(id, parent, names, mountLabel, options, moreOptions, writeable, nil)
} }
func (r *layerStore) Mount(id, mountLabel string) (string, error) { func (r *layerStore) Mount(id, mountLabel string) (string, error) {
@ -645,6 +679,67 @@ func (r *layerStore) Unmount(id string) error {
return err return err
} }
func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) {
layer, ok := r.lookup(id)
if !ok {
return nil, nil, ErrLayerUnknown
}
if len(layer.UIDMap) == 0 && len(layer.GIDMap) == 0 {
// We're not using any mappings, so there aren't any unmapped IDs on parent directories.
return nil, nil, nil
}
if layer.MountPoint == "" {
// We don't know which directories to examine.
return nil, nil, ErrLayerNotMounted
}
rootuid, rootgid, err := idtools.GetRootUIDGID(layer.UIDMap, layer.GIDMap)
if err != nil {
return nil, nil, errors.Wrapf(err, "error reading root ID values for layer %q", layer.ID)
}
m := idtools.NewIDMappingsFromMaps(layer.UIDMap, layer.GIDMap)
fsuids := make(map[int]struct{})
fsgids := make(map[int]struct{})
for dir := filepath.Dir(layer.MountPoint); dir != "" && dir != string(os.PathSeparator); dir = filepath.Dir(dir) {
st, err := system.Stat(dir)
if err != nil {
return nil, nil, errors.Wrapf(err, "error reading ownership of directory %q", dir)
}
lst, err := system.Lstat(dir)
if err != nil {
return nil, nil, errors.Wrapf(err, "error reading ownership of directory-in-case-it's-a-symlink %q", dir)
}
fsuid := int(st.UID())
fsgid := int(st.GID())
if _, _, err := m.ToContainer(idtools.IDPair{UID: fsuid, GID: rootgid}); err != nil {
fsuids[fsuid] = struct{}{}
}
if _, _, err := m.ToContainer(idtools.IDPair{UID: rootuid, GID: fsgid}); err != nil {
fsgids[fsgid] = struct{}{}
}
fsuid = int(lst.UID())
fsgid = int(lst.GID())
if _, _, err := m.ToContainer(idtools.IDPair{UID: fsuid, GID: rootgid}); err != nil {
fsuids[fsuid] = struct{}{}
}
if _, _, err := m.ToContainer(idtools.IDPair{UID: rootuid, GID: fsgid}); err != nil {
fsgids[fsgid] = struct{}{}
}
}
for uid := range fsuids {
uids = append(uids, uid)
}
for gid := range fsgids {
gids = append(gids, gid)
}
if len(uids) > 1 {
sort.Ints(uids)
}
if len(gids) > 1 {
sort.Ints(gids)
}
return uids, gids, nil
}
func (r *layerStore) removeName(layer *Layer, name string) { func (r *layerStore) removeName(layer *Layer, name string) {
layer.Names = stringSliceWithoutValue(layer.Names, name) layer.Names = stringSliceWithoutValue(layer.Names, name)
} }
@ -771,12 +866,11 @@ func (r *layerStore) Wipe() error {
return nil return nil
} }
func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID string, toLayer *Layer, err error) { func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID string, fromLayer, toLayer *Layer, err error) {
var ok bool var ok bool
var fromLayer *Layer
toLayer, ok = r.lookup(to) toLayer, ok = r.lookup(to)
if !ok { if !ok {
return "", "", nil, ErrLayerUnknown return "", "", nil, nil, ErrLayerUnknown
} }
to = toLayer.ID to = toLayer.ID
if from == "" { if from == "" {
@ -793,15 +887,22 @@ func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID st
} }
} }
} }
return from, to, toLayer, nil return from, to, fromLayer, toLayer, nil
}
func (r *layerStore) layerMappings(layer *Layer) *idtools.IDMappings {
if layer == nil {
return &idtools.IDMappings{}
}
return idtools.NewIDMappingsFromMaps(layer.UIDMap, layer.GIDMap)
} }
func (r *layerStore) Changes(from, to string) ([]archive.Change, error) { func (r *layerStore) Changes(from, to string) ([]archive.Change, error) {
from, to, toLayer, err := r.findParentAndLayer(from, to) from, to, fromLayer, toLayer, err := r.findParentAndLayer(from, to)
if err != nil { if err != nil {
return nil, ErrLayerUnknown return nil, ErrLayerUnknown
} }
return r.driver.Changes(to, from, toLayer.MountLabel) return r.driver.Changes(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel)
} }
type simpleGetCloser struct { type simpleGetCloser struct {
@ -836,7 +937,7 @@ func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) { func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, error) {
var metadata storage.Unpacker var metadata storage.Unpacker
from, to, toLayer, err := r.findParentAndLayer(from, to) from, to, fromLayer, toLayer, err := r.findParentAndLayer(from, to)
if err != nil { if err != nil {
return nil, ErrLayerUnknown return nil, ErrLayerUnknown
} }
@ -874,7 +975,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
} }
if from != toLayer.Parent { if from != toLayer.Parent {
diff, err := r.driver.Diff(to, from, toLayer.MountLabel) diff, err := r.driver.Diff(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -886,7 +987,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return nil, err return nil, err
} }
diff, err := r.driver.Diff(to, from, toLayer.MountLabel) diff, err := r.driver.Diff(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -925,12 +1026,12 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
} }
func (r *layerStore) DiffSize(from, to string) (size int64, err error) { func (r *layerStore) DiffSize(from, to string) (size int64, err error) {
var toLayer *Layer var fromLayer, toLayer *Layer
from, to, toLayer, err = r.findParentAndLayer(from, to) from, to, fromLayer, toLayer, err = r.findParentAndLayer(from, to)
if err != nil { if err != nil {
return -1, ErrLayerUnknown return -1, ErrLayerUnknown
} }
return r.driver.DiffSize(to, from, toLayer.MountLabel) return r.driver.DiffSize(to, r.layerMappings(toLayer), from, r.layerMappings(fromLayer), toLayer.MountLabel)
} }
func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error) { func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error) {
@ -970,7 +1071,7 @@ func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error
if err != nil { if err != nil {
return -1, err return -1, err
} }
size, err = r.driver.ApplyDiff(layer.ID, layer.Parent, layer.MountLabel, payload) size, err = r.driver.ApplyDiff(layer.ID, r.layerMappings(layer), layer.Parent, layer.MountLabel, payload)
if err != nil { if err != nil {
return -1, err return -1, err
} }

View File

@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: ./layers.go // source: layers.go
package storage package storage
@ -8,6 +8,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
fflib "github.com/pquerna/ffjson/fflib/v1" fflib "github.com/pquerna/ffjson/fflib/v1"
) )
@ -323,6 +324,46 @@ func (j *Layer) MarshalJSONBuf(buf fflib.EncodingBuffer) error {
} }
buf.WriteByte(',') buf.WriteByte(',')
} }
if len(j.UIDMap) != 0 {
buf.WriteString(`"uidmap":`)
if j.UIDMap != nil {
buf.WriteString(`[`)
for i, v := range j.UIDMap {
if i != 0 {
buf.WriteString(`,`)
}
/* Struct fall back. type=idtools.IDMap kind=struct */
err = buf.Encode(&v)
if err != nil {
return err
}
}
buf.WriteString(`]`)
} else {
buf.WriteString(`null`)
}
buf.WriteByte(',')
}
if len(j.GIDMap) != 0 {
buf.WriteString(`"gidmap":`)
if j.GIDMap != nil {
buf.WriteString(`[`)
for i, v := range j.GIDMap {
if i != 0 {
buf.WriteString(`,`)
}
/* Struct fall back. type=idtools.IDMap kind=struct */
err = buf.Encode(&v)
if err != nil {
return err
}
}
buf.WriteString(`]`)
} else {
buf.WriteString(`null`)
}
buf.WriteByte(',')
}
buf.Rewind(1) buf.Rewind(1)
buf.WriteByte('}') buf.WriteByte('}')
return nil return nil
@ -355,6 +396,10 @@ const (
ffjtLayerCompressionType ffjtLayerCompressionType
ffjtLayerFlags ffjtLayerFlags
ffjtLayerUIDMap
ffjtLayerGIDMap
) )
var ffjKeyLayerID = []byte("id") var ffjKeyLayerID = []byte("id")
@ -381,6 +426,10 @@ var ffjKeyLayerCompressionType = []byte("compression")
var ffjKeyLayerFlags = []byte("flags") var ffjKeyLayerFlags = []byte("flags")
var ffjKeyLayerUIDMap = []byte("uidmap")
var ffjKeyLayerGIDMap = []byte("gidmap")
// UnmarshalJSON umarshall json - template of ffjson // UnmarshalJSON umarshall json - template of ffjson
func (j *Layer) UnmarshalJSON(input []byte) error { func (j *Layer) UnmarshalJSON(input []byte) error {
fs := fflib.NewFFLexer(input) fs := fflib.NewFFLexer(input)
@ -486,6 +535,14 @@ mainparse:
goto mainparse goto mainparse
} }
case 'g':
if bytes.Equal(ffjKeyLayerGIDMap, kn) {
currentKey = ffjtLayerGIDMap
state = fflib.FFParse_want_colon
goto mainparse
}
case 'i': case 'i':
if bytes.Equal(ffjKeyLayerID, kn) { if bytes.Equal(ffjKeyLayerID, kn) {
@ -523,6 +580,26 @@ mainparse:
goto mainparse goto mainparse
} }
case 'u':
if bytes.Equal(ffjKeyLayerUIDMap, kn) {
currentKey = ffjtLayerUIDMap
state = fflib.FFParse_want_colon
goto mainparse
}
}
if fflib.SimpleLetterEqualFold(ffjKeyLayerGIDMap, kn) {
currentKey = ffjtLayerGIDMap
state = fflib.FFParse_want_colon
goto mainparse
}
if fflib.SimpleLetterEqualFold(ffjKeyLayerUIDMap, kn) {
currentKey = ffjtLayerUIDMap
state = fflib.FFParse_want_colon
goto mainparse
} }
if fflib.EqualFoldRight(ffjKeyLayerFlags, kn) { if fflib.EqualFoldRight(ffjKeyLayerFlags, kn) {
@ -650,6 +727,12 @@ mainparse:
case ffjtLayerFlags: case ffjtLayerFlags:
goto handle_Flags goto handle_Flags
case ffjtLayerUIDMap:
goto handle_UIDMap
case ffjtLayerGIDMap:
goto handle_GIDMap
case ffjtLayernosuchkey: case ffjtLayernosuchkey:
err = fs.SkipField(tok) err = fs.SkipField(tok)
if err != nil { if err != nil {
@ -1108,6 +1191,142 @@ handle_Flags:
state = fflib.FFParse_after_value state = fflib.FFParse_after_value
goto mainparse goto mainparse
handle_UIDMap:
/* handler: j.UIDMap type=[]idtools.IDMap kind=slice quoted=false*/
{
{
if tok != fflib.FFTok_left_brace && tok != fflib.FFTok_null {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for ", tok))
}
}
if tok == fflib.FFTok_null {
j.UIDMap = nil
} else {
j.UIDMap = []idtools.IDMap{}
wantVal := true
for {
var tmpJUIDMap idtools.IDMap
tok = fs.Scan()
if tok == fflib.FFTok_error {
goto tokerror
}
if tok == fflib.FFTok_right_brace {
break
}
if tok == fflib.FFTok_comma {
if wantVal == true {
// TODO(pquerna): this isn't an ideal error message, this handles
// things like [,,,] as an array value.
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
}
continue
} else {
wantVal = true
}
/* handler: tmpJUIDMap type=idtools.IDMap kind=struct quoted=false*/
{
/* Falling back. type=idtools.IDMap kind=struct */
tbuf, err := fs.CaptureField(tok)
if err != nil {
return fs.WrapErr(err)
}
err = json.Unmarshal(tbuf, &tmpJUIDMap)
if err != nil {
return fs.WrapErr(err)
}
}
j.UIDMap = append(j.UIDMap, tmpJUIDMap)
wantVal = false
}
}
}
state = fflib.FFParse_after_value
goto mainparse
handle_GIDMap:
/* handler: j.GIDMap type=[]idtools.IDMap kind=slice quoted=false*/
{
{
if tok != fflib.FFTok_left_brace && tok != fflib.FFTok_null {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for ", tok))
}
}
if tok == fflib.FFTok_null {
j.GIDMap = nil
} else {
j.GIDMap = []idtools.IDMap{}
wantVal := true
for {
var tmpJGIDMap idtools.IDMap
tok = fs.Scan()
if tok == fflib.FFTok_error {
goto tokerror
}
if tok == fflib.FFTok_right_brace {
break
}
if tok == fflib.FFTok_comma {
if wantVal == true {
// TODO(pquerna): this isn't an ideal error message, this handles
// things like [,,,] as an array value.
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
}
continue
} else {
wantVal = true
}
/* handler: tmpJGIDMap type=idtools.IDMap kind=struct quoted=false*/
{
/* Falling back. type=idtools.IDMap kind=struct */
tbuf, err := fs.CaptureField(tok)
if err != nil {
return fs.WrapErr(err)
}
err = json.Unmarshal(tbuf, &tmpJGIDMap)
if err != nil {
return fs.WrapErr(err)
}
}
j.GIDMap = append(j.GIDMap, tmpJGIDMap)
wantVal = false
}
}
}
state = fflib.FFParse_after_value
goto mainparse
wantedvalue: wantedvalue:
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
wrongtokenerror: wrongtokenerror:

View File

@ -59,18 +59,21 @@ type (
} }
) )
// Archiver allows the reuse of most utility functions of this package // Archiver allows the reuse of most utility functions of this package with a
// with a pluggable Untar function. Also, to facilitate the passing of // pluggable Untar function. To facilitate the passing of specific id mappings
// specific id mappings for untar, an archiver can be created with maps // for untar, an archiver can be created with maps which will then be passed to
// which will then be passed to Untar operations // Untar operations. If ChownOpts is set, its values are mapped using
// UntarIDMappings before being used to create files and directories on disk.
type Archiver struct { type Archiver struct {
Untar func(io.Reader, string, *TarOptions) error Untar func(io.Reader, string, *TarOptions) error
IDMappings *idtools.IDMappings TarIDMappings *idtools.IDMappings
ChownOpts *idtools.IDPair
UntarIDMappings *idtools.IDMappings
} }
// NewDefaultArchiver returns a new Archiver without any IDMappings // NewDefaultArchiver returns a new Archiver without any IDMappings
func NewDefaultArchiver() *Archiver { func NewDefaultArchiver() *Archiver {
return &Archiver{Untar: Untar, IDMappings: &idtools.IDMappings{}} return &Archiver{Untar: Untar, TarIDMappings: &idtools.IDMappings{}, UntarIDMappings: &idtools.IDMappings{}}
} }
// breakoutError is used to differentiate errors related to breaking out // breakoutError is used to differentiate errors related to breaking out
@ -942,7 +945,7 @@ loop:
} }
trBuf.Reset(tr) trBuf.Reset(tr)
if err := remapIDs(idMappings, hdr); err != nil { if err := remapIDs(nil, idMappings, options.ChownOpts, hdr); err != nil {
return err return err
} }
@ -1023,14 +1026,28 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
// If either Tar or Untar fails, TarUntar aborts and returns the error. // If either Tar or Untar fails, TarUntar aborts and returns the error.
func (archiver *Archiver) TarUntar(src, dst string) error { func (archiver *Archiver) TarUntar(src, dst string) error {
logrus.Debugf("TarUntar(%s %s)", src, dst) logrus.Debugf("TarUntar(%s %s)", src, dst)
archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed}) tarMappings := archiver.TarIDMappings
if tarMappings == nil {
tarMappings = &idtools.IDMappings{}
}
options := &TarOptions{
UIDMaps: tarMappings.UIDs(),
GIDMaps: tarMappings.GIDs(),
Compression: Uncompressed,
}
archive, err := TarWithOptions(src, options)
if err != nil { if err != nil {
return err return err
} }
defer archive.Close() defer archive.Close()
options := &TarOptions{ untarMappings := archiver.UntarIDMappings
UIDMaps: archiver.IDMappings.UIDs(), if untarMappings == nil {
GIDMaps: archiver.IDMappings.GIDs(), untarMappings = &idtools.IDMappings{}
}
options = &TarOptions{
UIDMaps: untarMappings.UIDs(),
GIDMaps: untarMappings.GIDs(),
ChownOpts: archiver.ChownOpts,
} }
return archiver.Untar(archive, dst, options) return archiver.Untar(archive, dst, options)
} }
@ -1042,9 +1059,14 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
return err return err
} }
defer archive.Close() defer archive.Close()
untarMappings := archiver.UntarIDMappings
if untarMappings == nil {
untarMappings = &idtools.IDMappings{}
}
options := &TarOptions{ options := &TarOptions{
UIDMaps: archiver.IDMappings.UIDs(), UIDMaps: untarMappings.UIDs(),
GIDMaps: archiver.IDMappings.GIDs(), GIDMaps: untarMappings.GIDs(),
ChownOpts: archiver.ChownOpts,
} }
return archiver.Untar(archive, dst, options) return archiver.Untar(archive, dst, options)
} }
@ -1065,7 +1087,10 @@ 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.IDMappings.RootPair() rootIDs := archiver.UntarIDMappings.RootPair()
if archiver.ChownOpts != nil {
rootIDs = *archiver.ChownOpts
}
// 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 {
@ -1116,7 +1141,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.IDMappings, hdr); err != nil { if err := remapIDs(archiver.TarIDMappings, archiver.UntarIDMappings, archiver.ChownOpts, hdr); err != nil {
return err return err
} }
@ -1143,11 +1168,30 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
return err return err
} }
func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error { func remapIDs(readIDMappings, writeIDMappings *idtools.IDMappings, chownOpts *idtools.IDPair, hdr *tar.Header) (err error) {
ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}) var uid, gid int
hdr.Uid, hdr.Gid = ids.UID, ids.GID if chownOpts != nil {
uid, gid = chownOpts.UID, chownOpts.GID
} else {
if readIDMappings != nil && !readIDMappings.Empty() {
uid, gid, err = readIDMappings.ToContainer(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
if err != nil {
return err return err
} }
} else {
uid, gid = hdr.Uid, hdr.Gid
}
}
ids := idtools.IDPair{UID: uid, GID: gid}
if writeIDMappings != nil && !writeIDMappings.Empty() {
ids, err = writeIDMappings.ToHost(ids)
if err != nil {
return err
}
}
hdr.Uid, hdr.Gid = ids.UID, ids.GID
return nil
}
// cmdStream executes a command, and returns its stdout as a stream. // cmdStream executes a command, and returns its stdout as a stream.
// If the command fails to run or doesn't complete successfully, an error // If the command fails to run or doesn't complete successfully, an error

File diff suppressed because it is too large Load Diff

View File

@ -257,6 +257,7 @@ func changes(layers []string, rw string, dc deleteChange, sc skipChange, wc whit
// FileInfo describes the information of a file. // FileInfo describes the information of a file.
type FileInfo struct { type FileInfo struct {
parent *FileInfo parent *FileInfo
idMappings *idtools.IDMappings
name string name string
stat *system.StatT stat *system.StatT
children map[string]*FileInfo children map[string]*FileInfo
@ -329,7 +330,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
// be visible when actually comparing the stat fields. The only time this // be visible when actually comparing the stat fields. The only time this
// breaks down is if some code intentionally hides a change by setting // breaks down is if some code intentionally hides a change by setting
// back mtime // back mtime
if statDifferent(oldStat, newStat) || if statDifferent(oldStat, oldInfo, newStat, info) ||
!bytes.Equal(oldChild.capability, newChild.capability) { !bytes.Equal(oldChild.capability, newChild.capability) {
change := Change{ change := Change{
Path: newChild.path(), Path: newChild.path(),
@ -379,10 +380,11 @@ func (info *FileInfo) Changes(oldInfo *FileInfo) []Change {
return changes return changes
} }
func newRootFileInfo() *FileInfo { func newRootFileInfo(idMappings *idtools.IDMappings) *FileInfo {
// As this runs on the daemon side, file paths are OS specific. // As this runs on the daemon side, file paths are OS specific.
root := &FileInfo{ root := &FileInfo{
name: string(os.PathSeparator), name: string(os.PathSeparator),
idMappings: idMappings,
children: make(map[string]*FileInfo), children: make(map[string]*FileInfo),
} }
return root return root
@ -390,7 +392,7 @@ func newRootFileInfo() *FileInfo {
// ChangesDirs compares two directories and generates an array of Change objects describing the changes. // ChangesDirs compares two directories and generates an array of Change objects describing the changes.
// If oldDir is "", then all files in newDir will be Add-Changes. // If oldDir is "", then all files in newDir will be Add-Changes.
func ChangesDirs(newDir, oldDir string) ([]Change, error) { func ChangesDirs(newDir string, newMappings *idtools.IDMappings, oldDir string, oldMappings *idtools.IDMappings) ([]Change, error) {
var ( var (
oldRoot, newRoot *FileInfo oldRoot, newRoot *FileInfo
) )
@ -402,7 +404,7 @@ func ChangesDirs(newDir, oldDir string) ([]Change, error) {
defer os.Remove(emptyDir) defer os.Remove(emptyDir)
oldDir = emptyDir oldDir = emptyDir
} }
oldRoot, newRoot, err := collectFileInfoForChanges(oldDir, newDir) oldRoot, newRoot, err := collectFileInfoForChanges(oldDir, newDir, oldMappings, newMappings)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -9,6 +9,7 @@ import (
"syscall" "syscall"
"unsafe" "unsafe"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -26,6 +27,8 @@ type walker struct {
dir2 string dir2 string
root1 *FileInfo root1 *FileInfo
root2 *FileInfo root2 *FileInfo
idmap1 *idtools.IDMappings
idmap2 *idtools.IDMappings
} }
// collectFileInfoForChanges returns a complete representation of the trees // collectFileInfoForChanges returns a complete representation of the trees
@ -34,12 +37,12 @@ type walker struct {
// and dir2 will be pruned from the results. This method is *only* to be used // and dir2 will be pruned from the results. This method is *only* to be used
// to generating a list of changes between the two directories, as it does not // to generating a list of changes between the two directories, as it does not
// reflect the full contents. // reflect the full contents.
func collectFileInfoForChanges(dir1, dir2 string) (*FileInfo, *FileInfo, error) { func collectFileInfoForChanges(dir1, dir2 string, idmap1, idmap2 *idtools.IDMappings) (*FileInfo, *FileInfo, error) {
w := &walker{ w := &walker{
dir1: dir1, dir1: dir1,
dir2: dir2, dir2: dir2,
root1: newRootFileInfo(), root1: newRootFileInfo(idmap1),
root2: newRootFileInfo(), root2: newRootFileInfo(idmap2),
} }
i1, err := os.Lstat(w.dir1) i1, err := os.Lstat(w.dir1)
@ -72,6 +75,7 @@ func walkchunk(path string, fi os.FileInfo, dir string, root *FileInfo) error {
name: filepath.Base(path), name: filepath.Base(path),
children: make(map[string]*FileInfo), children: make(map[string]*FileInfo),
parent: parent, parent: parent,
idMappings: root.idMappings,
} }
cpath := filepath.Join(dir, path) cpath := filepath.Join(dir, path)
stat, err := system.FromStatT(fi.Sys().(*syscall.Stat_t)) stat, err := system.FromStatT(fi.Sys().(*syscall.Stat_t))

View File

@ -9,21 +9,22 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
) )
func collectFileInfoForChanges(oldDir, newDir string) (*FileInfo, *FileInfo, error) { func collectFileInfoForChanges(oldDir, newDir string, oldIDMap, newIDMap *idtools.IDMappings) (*FileInfo, *FileInfo, error) {
var ( var (
oldRoot, newRoot *FileInfo oldRoot, newRoot *FileInfo
err1, err2 error err1, err2 error
errs = make(chan error, 2) errs = make(chan error, 2)
) )
go func() { go func() {
oldRoot, err1 = collectFileInfo(oldDir) oldRoot, err1 = collectFileInfo(oldDir, oldIDMap)
errs <- err1 errs <- err1
}() }()
go func() { go func() {
newRoot, err2 = collectFileInfo(newDir) newRoot, err2 = collectFileInfo(newDir, newIDMap)
errs <- err2 errs <- err2
}() }()
@ -37,8 +38,8 @@ func collectFileInfoForChanges(oldDir, newDir string) (*FileInfo, *FileInfo, err
return oldRoot, newRoot, nil return oldRoot, newRoot, nil
} }
func collectFileInfo(sourceDir string) (*FileInfo, error) { func collectFileInfo(sourceDir string, idMappings *idtools.IDMappings) (*FileInfo, error) {
root := newRootFileInfo() root := newRootFileInfo(idMappings)
err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error { err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error {
if err != nil { if err != nil {
@ -76,6 +77,7 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) {
name: filepath.Base(relPath), name: filepath.Base(relPath),
children: make(map[string]*FileInfo), children: make(map[string]*FileInfo),
parent: parent, parent: parent,
idMappings: idMappings,
} }
s, err := system.Lstat(path) s, err := system.Lstat(path)

View File

@ -6,15 +6,26 @@ import (
"os" "os"
"syscall" "syscall"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func statDifferent(oldStat *system.StatT, newStat *system.StatT) bool { func statDifferent(oldStat *system.StatT, oldInfo *FileInfo, newStat *system.StatT, newInfo *FileInfo) bool {
// Don't look at size for dirs, its not a good measure of change // Don't look at size for dirs, its not a good measure of change
oldUid, oldGid := oldStat.UID(), oldStat.GID()
uid, gid := newStat.UID(), newStat.GID()
if cuid, cgid, err := newInfo.idMappings.ToContainer(idtools.IDPair{UID: int(uid), GID: int(gid)}); err == nil {
uid = uint32(cuid)
gid = uint32(cgid)
if oldcuid, oldcgid, err := oldInfo.idMappings.ToContainer(idtools.IDPair{UID: int(oldUid), GID: int(oldGid)}); err == nil {
oldUid = uint32(oldcuid)
oldGid = uint32(oldcgid)
}
}
ownerChanged := uid != oldUid || gid != oldGid
if oldStat.Mode() != newStat.Mode() || if oldStat.Mode() != newStat.Mode() ||
oldStat.UID() != newStat.UID() || ownerChanged ||
oldStat.GID() != newStat.GID() ||
oldStat.Rdev() != newStat.Rdev() || oldStat.Rdev() != newStat.Rdev() ||
// Don't look at size for dirs, its not a good measure of change // Don't look at size for dirs, its not a good measure of change
(oldStat.Mode()&unix.S_IFDIR != unix.S_IFDIR && (oldStat.Mode()&unix.S_IFDIR != unix.S_IFDIR &&

View File

@ -6,7 +6,7 @@ import (
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
) )
func statDifferent(oldStat *system.StatT, newStat *system.StatT) bool { func statDifferent(oldStat *system.StatT, oldInfo *FileInfo, newStat *system.StatT, newInfo *FileInfo) bool {
// Don't look at size for dirs, its not a good measure of change // Don't look at size for dirs, its not a good measure of change
if oldStat.Mtim() != newStat.Mtim() || if oldStat.Mtim() != newStat.Mtim() ||

View File

@ -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(nil, idMappings, options.ChownOpts, srcHdr); err != nil {
return 0, err return 0, err
} }

View File

@ -16,7 +16,18 @@ func NewArchiver(idMappings *idtools.IDMappings) *archive.Archiver {
if idMappings == nil { if idMappings == nil {
idMappings = &idtools.IDMappings{} idMappings = &idtools.IDMappings{}
} }
return &archive.Archiver{Untar: Untar, IDMappings: idMappings} return &archive.Archiver{Untar: Untar, TarIDMappings: idMappings, UntarIDMappings: idMappings}
}
// NewArchiverWithChown returns a new Archiver which uses chrootarchive.Untar and the provided ID mapping configuration on both ends
func NewArchiverWithChown(tarIDMappings *idtools.IDMappings, chownOpts *idtools.IDPair, untarIDMappings *idtools.IDMappings) *archive.Archiver {
if tarIDMappings == nil {
tarIDMappings = &idtools.IDMappings{}
}
if untarIDMappings == nil {
untarIDMappings = &idtools.IDMappings{}
}
return &archive.Archiver{Untar: Untar, TarIDMappings: tarIDMappings, ChownOpts: chownOpts, UntarIDMappings: untarIDMappings}
} }
// Untar reads a stream of bytes from `archive`, parses it as a tar archive, // Untar reads a stream of bytes from `archive`, parses it as a tar archive,

View File

@ -7,6 +7,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -136,9 +137,8 @@ type StoreOptions struct {
GraphDriverName string `json:"driver,omitempty"` GraphDriverName string `json:"driver,omitempty"`
// GraphDriverOptions are driver-specific options. // GraphDriverOptions are driver-specific options.
GraphDriverOptions []string `json:"driver-options,omitempty"` GraphDriverOptions []string `json:"driver-options,omitempty"`
// UIDMap and GIDMap are used mainly for deciding on the ownership of // UIDMap and GIDMap are used for setting up a container's root filesystem
// files in layers as they're stored on disk, which is often necessary // for use inside of a user namespace where UID mapping is being used.
// when user namespaces are being used.
UIDMap []idtools.IDMap `json:"uidmap,omitempty"` UIDMap []idtools.IDMap `json:"uidmap,omitempty"`
GIDMap []idtools.IDMap `json:"gidmap,omitempty"` GIDMap []idtools.IDMap `json:"gidmap,omitempty"`
} }
@ -152,6 +152,8 @@ type Store interface {
GraphRoot() string GraphRoot() string
GraphDriverName() string GraphDriverName() string
GraphOptions() []string GraphOptions() []string
UIDMap() []idtools.IDMap
GIDMap() []idtools.IDMap
// GraphDriver obtains and returns a handle to the graph Driver object used // GraphDriver obtains and returns a handle to the graph Driver object used
// by the Store. // by the Store.
@ -161,7 +163,7 @@ type Store interface {
// optionally having the specified ID (one will be assigned if none is // optionally having the specified ID (one will be assigned if none is
// specified), with the specified layer (or no layer) as its parent, // specified), with the specified layer (or no layer) as its parent,
// and with optional names. (The writeable flag is ignored.) // and with optional names. (The writeable flag is ignored.)
CreateLayer(id, parent string, names []string, mountLabel string, writeable bool) (*Layer, error) CreateLayer(id, parent string, names []string, mountLabel string, writeable bool, options *LayerOptions) (*Layer, error)
// PutLayer combines the functions of CreateLayer and ApplyDiff, // PutLayer combines the functions of CreateLayer and ApplyDiff,
// marking the layer for automatic removal if applying the diff fails // marking the layer for automatic removal if applying the diff fails
@ -174,7 +176,7 @@ type Store interface {
// if reexec.Init { // if reexec.Init {
// return // return
// } // }
PutLayer(id, parent string, names []string, mountLabel string, writeable bool, diff io.Reader) (*Layer, int64, error) PutLayer(id, parent string, names []string, mountLabel string, writeable bool, options *LayerOptions, diff io.Reader) (*Layer, int64, error)
// CreateImage creates a new image, optionally with the specified ID // CreateImage creates a new image, optionally with the specified ID
// (one will be assigned if none is specified), with optional names, // (one will be assigned if none is specified), with optional names,
@ -303,6 +305,11 @@ type Store interface {
// if we don't have a value on hand. // if we don't have a value on hand.
LayerSize(id string) (int64, error) LayerSize(id string) (int64, error)
// LayerParentOwners returns the UIDs and GIDs of owners of parents of
// the layer's mountpoint for which the layer's UID and GID maps (if
// any are defined) don't contain corresponding IDs.
LayerParentOwners(id string) ([]int, []int, error)
// Layers returns a list of the currently known layers. // Layers returns a list of the currently known layers.
Layers() ([]Layer, error) Layers() ([]Layer, error)
@ -413,6 +420,11 @@ type Store interface {
// directory. // directory.
FromContainerRunDirectory(id, file string) ([]byte, error) FromContainerRunDirectory(id, file string) ([]byte, error)
// ContainerParentOwners returns the UIDs and GIDs of owners of parents
// of the container's layer's mountpoint for which the layer's UID and
// GID maps (if any are defined) don't contain corresponding IDs.
ContainerParentOwners(id string) ([]int, []int, error)
// Lookup returns the ID of a layer, image, or container with the specified // Lookup returns the ID of a layer, image, or container with the specified
// name or ID. // name or ID.
Lookup(name string) (string, error) Lookup(name string) (string, error)
@ -429,6 +441,33 @@ type Store interface {
Version() ([][2]string, error) Version() ([][2]string, error)
} }
// IDMappingOptions are used for specifying how ID mapping should be set up for
// a layer or container.
type IDMappingOptions struct {
// UIDMap and GIDMap are used for setting up a layer's root filesystem
// for use inside of a user namespace where ID mapping is being used.
// If HostUIDMapping/HostGIDMapping is true, no mapping of the
// respective type will be used. Otherwise, if UIDMap and/or GIDMap
// contain at least one mapping, one or both will be used. By default,
// if neither of those conditions apply, if the layer has a parent
// layer, the parent layer's mapping will be used, and if it does not
// have a parent layer, the mapping which was passed to the Store
// object when it was initialized will be used.
HostUIDMapping bool
HostGIDMapping bool
UIDMap []idtools.IDMap
GIDMap []idtools.IDMap
}
// LayerOptions is used for passing options to a Store's CreateLayer() and PutLayer() methods.
type LayerOptions struct {
// IDMappingOptions specifies the type of ID mapping which should be
// used for this layer. If nothing is specified, the layer will
// inherit settings from its parent layer or, if it has no parent
// layer, the Store object.
IDMappingOptions
}
// ImageOptions is used for passing options to a Store's CreateImage() method. // ImageOptions is used for passing options to a Store's CreateImage() method.
type ImageOptions struct { type ImageOptions struct {
// CreationDate, if not zero, will override the default behavior of marking the image as having been // CreationDate, if not zero, will override the default behavior of marking the image as having been
@ -440,6 +479,11 @@ type ImageOptions struct {
// ContainerOptions is used for passing options to a Store's CreateContainer() method. // ContainerOptions is used for passing options to a Store's CreateContainer() method.
type ContainerOptions struct { type ContainerOptions struct {
// IDMappingOptions specifies the type of ID mapping which should be
// used for this container's layer. If nothing is specified, the
// container's layer will inherit settings from the image's top layer
// or, if it is not being created based on an image, the Store object.
IDMappingOptions
} }
type store struct { type store struct {
@ -558,6 +602,14 @@ func (s *store) GraphOptions() []string {
return s.graphOptions return s.graphOptions
} }
func (s *store) UIDMap() []idtools.IDMap {
return copyIDMap(s.uidMap)
}
func (s *store) GIDMap() []idtools.IDMap {
return copyIDMap(s.gidMap)
}
func (s *store) load() error { func (s *store) load() error {
driver, err := s.GraphDriver() driver, err := s.GraphDriver()
if err != nil { if err != nil {
@ -662,7 +714,7 @@ func (s *store) LayerStore() (LayerStore, error) {
if err := os.MkdirAll(glpath, 0700); err != nil { if err := os.MkdirAll(glpath, 0700); err != nil {
return nil, err return nil, err
} }
rls, err := newLayerStore(rlpath, glpath, driver) rls, err := newLayerStore(rlpath, glpath, driver, s.uidMap, s.gidMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -742,7 +794,8 @@ func (s *store) ContainerStore() (ContainerStore, error) {
return nil, ErrLoadError return nil, ErrLoadError
} }
func (s *store) PutLayer(id, parent string, names []string, mountLabel string, writeable bool, diff io.Reader) (*Layer, int64, error) { func (s *store) PutLayer(id, parent string, names []string, mountLabel string, writeable bool, options *LayerOptions, diff io.Reader) (*Layer, int64, error) {
var parentLayer *Layer
rlstore, err := s.LayerStore() rlstore, err := s.LayerStore()
if err != nil { if err != nil {
return nil, -1, err return nil, -1, err
@ -768,9 +821,27 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
if id == "" { if id == "" {
id = stringid.GenerateRandomID() id = stringid.GenerateRandomID()
} }
if options == nil {
options = &LayerOptions{}
}
if options.HostUIDMapping {
options.UIDMap = nil
}
if options.HostGIDMapping {
options.GIDMap = nil
}
uidMap := options.UIDMap
gidMap := options.GIDMap
if parent != "" { if parent != "" {
var ilayer *Layer var ilayer *Layer
for _, lstore := range append([]ROLayerStore{rlstore}, rlstores...) { for _, lstore := range append([]ROLayerStore{rlstore}, rlstores...) {
if lstore != rlstore {
lstore.Lock()
defer lstore.Unlock()
if modified, err := lstore.Modified(); modified || err != nil {
lstore.Load()
}
}
if l, err := lstore.Get(parent); err == nil && l != nil { if l, err := lstore.Get(parent); err == nil && l != nil {
ilayer = l ilayer = l
parent = ilayer.ID parent = ilayer.ID
@ -780,6 +851,7 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
if ilayer == nil { if ilayer == nil {
return nil, -1, ErrLayerUnknown return nil, -1, ErrLayerUnknown
} }
parentLayer = ilayer
containers, err := rcstore.Containers() containers, err := rcstore.Containers()
if err != nil { if err != nil {
return nil, -1, err return nil, -1, err
@ -789,12 +861,33 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
return nil, -1, ErrParentIsContainer return nil, -1, ErrParentIsContainer
} }
} }
if !options.HostUIDMapping && len(options.UIDMap) == 0 {
uidMap = ilayer.UIDMap
} }
return rlstore.Put(id, parent, names, mountLabel, nil, writeable, nil, diff) if !options.HostGIDMapping && len(options.GIDMap) == 0 {
gidMap = ilayer.GIDMap
}
} else {
if !options.HostUIDMapping && len(options.UIDMap) == 0 {
uidMap = s.uidMap
}
if !options.HostGIDMapping && len(options.GIDMap) == 0 {
gidMap = s.gidMap
}
}
layerOptions := &LayerOptions{
IDMappingOptions: IDMappingOptions{
HostUIDMapping: options.HostUIDMapping,
HostGIDMapping: options.HostGIDMapping,
UIDMap: copyIDMap(uidMap),
GIDMap: copyIDMap(gidMap),
},
}
return rlstore.Put(id, parentLayer, names, mountLabel, nil, layerOptions, writeable, nil, diff)
} }
func (s *store) CreateLayer(id, parent string, names []string, mountLabel string, writeable bool) (*Layer, error) { func (s *store) CreateLayer(id, parent string, names []string, mountLabel string, writeable bool, options *LayerOptions) (*Layer, error) {
layer, _, err := s.PutLayer(id, parent, names, mountLabel, writeable, nil) layer, _, err := s.PutLayer(id, parent, names, mountLabel, writeable, options, nil)
return layer, err return layer, err
} }
@ -849,6 +942,15 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o
} }
func (s *store) CreateContainer(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) { func (s *store) CreateContainer(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) {
if options == nil {
options = &ContainerOptions{}
}
if options.HostUIDMapping {
options.UIDMap = nil
}
if options.HostGIDMapping {
options.GIDMap = nil
}
rlstore, err := s.LayerStore() rlstore, err := s.LayerStore()
if err != nil { if err != nil {
return nil, err return nil, err
@ -862,8 +964,10 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
id = stringid.GenerateRandomID() id = stringid.GenerateRandomID()
} }
imageTopLayer := "" var imageTopLayer *Layer
imageID := "" imageID := ""
uidMap := options.UIDMap
gidMap := options.GIDMap
if image != "" { if image != "" {
istore, err := s.ImageStore() istore, err := s.ImageStore()
if err != nil { if err != nil {
@ -888,10 +992,53 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
if cimage == nil { if cimage == nil {
return nil, ErrImageUnknown return nil, ErrImageUnknown
} }
imageTopLayer = cimage.TopLayer
imageID = cimage.ID imageID = cimage.ID
lstores, err := s.ROLayerStores()
if err != nil {
return nil, err
} }
clayer, err := rlstore.Create(layer, imageTopLayer, nil, "", nil, true) var ilayer *Layer
for _, store := range append([]ROLayerStore{rlstore}, lstores...) {
if store != rlstore {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
store.Load()
}
}
ilayer, err = store.Get(cimage.TopLayer)
if err == nil {
break
}
}
if ilayer == nil {
return nil, ErrLayerUnknown
}
imageTopLayer = ilayer
if !options.HostUIDMapping && len(options.UIDMap) == 0 {
uidMap = ilayer.UIDMap
}
if !options.HostGIDMapping && len(options.GIDMap) == 0 {
gidMap = ilayer.GIDMap
}
} else {
if !options.HostUIDMapping && len(options.UIDMap) == 0 {
uidMap = s.uidMap
}
if !options.HostGIDMapping && len(options.GIDMap) == 0 {
gidMap = s.gidMap
}
}
layerOptions := &LayerOptions{
IDMappingOptions: IDMappingOptions{
HostUIDMapping: options.HostUIDMapping,
HostGIDMapping: options.HostGIDMapping,
UIDMap: copyIDMap(uidMap),
GIDMap: copyIDMap(gidMap),
},
}
clayer, err := rlstore.Create(layer, imageTopLayer, nil, "", nil, layerOptions, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -905,7 +1052,15 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
if modified, err := rcstore.Modified(); modified || err != nil { if modified, err := rcstore.Modified(); modified || err != nil {
rcstore.Load() rcstore.Load()
} }
container, err := rcstore.Create(id, names, imageID, layer, metadata) options = &ContainerOptions{
IDMappingOptions: IDMappingOptions{
HostUIDMapping: len(clayer.UIDMap) == 0,
HostGIDMapping: len(clayer.GIDMap) == 0,
UIDMap: copyIDMap(clayer.UIDMap),
GIDMap: copyIDMap(clayer.GIDMap),
},
}
container, err := rcstore.Create(id, names, imageID, layer, metadata, options)
if err != nil || container == nil { if err != nil || container == nil {
rlstore.Delete(layer) rlstore.Delete(layer)
} }
@ -1944,6 +2099,51 @@ func (s *store) LayerSize(id string) (int64, error) {
return -1, ErrLayerUnknown return -1, ErrLayerUnknown
} }
func (s *store) LayerParentOwners(id string) ([]int, []int, error) {
rlstore, err := s.LayerStore()
if err != nil {
return nil, nil, err
}
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
rlstore.Load()
}
if rlstore.Exists(id) {
return rlstore.ParentOwners(id)
}
return nil, nil, ErrLayerUnknown
}
func (s *store) ContainerParentOwners(id string) ([]int, []int, error) {
rlstore, err := s.LayerStore()
if err != nil {
return nil, nil, err
}
rcstore, err := s.ContainerStore()
if err != nil {
return nil, nil, err
}
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
rlstore.Load()
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
rcstore.Load()
}
container, err := rcstore.Get(id)
if err != nil {
return nil, nil, err
}
if rlstore.Exists(container.LayerID) {
return rlstore.ParentOwners(container.LayerID)
}
return nil, nil, ErrLayerUnknown
}
func (s *store) Layers() ([]Layer, error) { func (s *store) Layers() ([]Layer, error) {
var layers []Layer var layers []Layer
lstore, err := s.LayerStore() lstore, err := s.LayerStore()
@ -2399,6 +2599,18 @@ type OptionsConfig struct {
// OverrideKernelCheck // OverrideKernelCheck
OverrideKernelCheck string `toml:"override_kernel_check"` OverrideKernelCheck string `toml:"override_kernel_check"`
// RemapUIDs is a list of default UID mappings to use for layers.
RemapUIDs string `toml:"remap-uids"`
// RemapGIDs is a list of default GID mappings to use for layers.
RemapGIDs string `toml:"remap-gids"`
// RemapUser is the name of one or more entries in /etc/subuid which
// should be used to set up default UID mappings.
RemapUser string `toml:"remap-user"`
// RemapGroup is the name of one or more entries in /etc/subgid which
// should be used to set up default GID mappings.
RemapGroup string `toml:"remap-group"`
} }
// TOML-friendly explicit tables used for conversions. // TOML-friendly explicit tables used for conversions.
@ -2448,6 +2660,71 @@ func init() {
if config.Storage.Options.OverrideKernelCheck != "" { if config.Storage.Options.OverrideKernelCheck != "" {
DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("%s.override_kernel_check=%s", config.Storage.Driver, config.Storage.Options.OverrideKernelCheck)) DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("%s.override_kernel_check=%s", config.Storage.Driver, config.Storage.Options.OverrideKernelCheck))
} }
if config.Storage.Options.RemapUser != "" && config.Storage.Options.RemapGroup == "" {
config.Storage.Options.RemapGroup = config.Storage.Options.RemapUser
}
if config.Storage.Options.RemapGroup != "" && config.Storage.Options.RemapUser == "" {
config.Storage.Options.RemapUser = config.Storage.Options.RemapGroup
}
if config.Storage.Options.RemapUser != "" && config.Storage.Options.RemapGroup != "" {
mappings, err := idtools.NewIDMappings(config.Storage.Options.RemapUser, config.Storage.Options.RemapGroup)
if err != nil {
fmt.Printf("Error initializing ID mappings for %s:%s %v\n", config.Storage.Options.RemapUser, config.Storage.Options.RemapGroup, err)
return
}
DefaultStoreOptions.UIDMap = mappings.UIDs()
DefaultStoreOptions.GIDMap = mappings.GIDs()
}
nonDigitsToWhitespace := func(r rune) rune {
if strings.IndexRune("0123456789", r) == -1 {
return ' '
} else {
return r
}
}
parseTriple := func(spec []string) (container, host, size uint32, err error) {
cid, err := strconv.ParseUint(spec[0], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[0], err)
}
hid, err := strconv.ParseUint(spec[1], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[1], err)
}
sz, err := strconv.ParseUint(spec[2], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[2], err)
}
return uint32(cid), uint32(hid), uint32(sz), nil
}
parseIDMap := func(idMapSpec, mapSetting string) (idmap []idtools.IDMap) {
if len(idMapSpec) > 0 {
idSpec := strings.Fields(strings.Map(nonDigitsToWhitespace, idMapSpec))
if len(idSpec)%3 != 0 {
fmt.Printf("Error initializing ID mappings: %s setting is malformed.\n", mapSetting)
return nil
}
for i := range idSpec {
if i%3 != 0 {
continue
}
cid, hid, size, err := parseTriple(idSpec[i : i+3])
if err != nil {
fmt.Printf("Error initializing ID mappings: %s setting is malformed.\n", mapSetting)
return nil
}
mapping := idtools.IDMap{
ContainerID: int(cid),
HostID: int(hid),
Size: int(size),
}
idmap = append(idmap, mapping)
}
}
return idmap
}
DefaultStoreOptions.UIDMap = append(DefaultStoreOptions.UIDMap, parseIDMap(config.Storage.Options.RemapUIDs, "remap-uids")...)
DefaultStoreOptions.GIDMap = append(DefaultStoreOptions.GIDMap, parseIDMap(config.Storage.Options.RemapGIDs, "remap-gids")...)
if os.Getenv("STORAGE_DRIVER") != "" { if os.Getenv("STORAGE_DRIVER") != "" {
DefaultStoreOptions.GraphDriverName = os.Getenv("STORAGE_DRIVER") DefaultStoreOptions.GraphDriverName = os.Getenv("STORAGE_DRIVER")
} }