From c3e2b00333d42dc87a3385939715813006cc8af1 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Thu, 5 Apr 2018 15:34:31 -0400 Subject: 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 Closes: #596 Approved by: TomSweeneyRedHat --- vendor.conf | 4 +- .../containers/image/docker/daemon/client.go | 24 +- .../containers/image/storage/storage_image.go | 2 +- vendor/github.com/containers/storage/containers.go | 17 +- .../containers/storage/containers_ffjson.go | 221 +- .../containers/storage/drivers/aufs/aufs.go | 64 +- .../containers/storage/drivers/btrfs/btrfs.go | 2 +- .../github.com/containers/storage/drivers/chown.go | 167 ++ .../containers/storage/drivers/chroot_unix.go | 21 + .../containers/storage/drivers/chroot_windows.go | 15 + .../containers/storage/drivers/devmapper/driver.go | 2 +- .../containers/storage/drivers/driver.go | 22 +- .../containers/storage/drivers/fsdiff.go | 71 +- .../containers/storage/drivers/overlay/overlay.go | 174 +- .../containers/storage/drivers/vfs/driver.go | 10 +- .../containers/storage/drivers/windows/windows.go | 21 +- .../containers/storage/drivers/zfs/zfs.go | 2 +- vendor/github.com/containers/storage/errors.go | 2 + .../github.com/containers/storage/images_ffjson.go | 2 +- vendor/github.com/containers/storage/layers.go | 155 +- .../github.com/containers/storage/layers_ffjson.go | 221 +- .../containers/storage/pkg/archive/archive.go | 82 +- .../storage/pkg/archive/archive_ffjson.go | 2230 ++++++++++++++++++++ .../containers/storage/pkg/archive/changes.go | 14 +- .../storage/pkg/archive/changes_linux.go | 24 +- .../storage/pkg/archive/changes_other.go | 18 +- .../containers/storage/pkg/archive/changes_unix.go | 17 +- .../storage/pkg/archive/changes_windows.go | 2 +- .../containers/storage/pkg/archive/diff.go | 2 +- .../storage/pkg/chrootarchive/archive.go | 13 +- vendor/github.com/containers/storage/store.go | 305 ++- 31 files changed, 3742 insertions(+), 184 deletions(-) create mode 100644 vendor/github.com/containers/storage/drivers/chown.go create mode 100644 vendor/github.com/containers/storage/drivers/chroot_unix.go create mode 100644 vendor/github.com/containers/storage/drivers/chroot_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go diff --git a/vendor.conf b/vendor.conf index 3ce86618a..151b7669a 100644 --- a/vendor.conf +++ b/vendor.conf @@ -10,8 +10,8 @@ github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943 github.com/containerd/continuity master github.com/containernetworking/cni v0.4.0 github.com/containernetworking/plugins master -github.com/containers/image fbc14df0f25a15b456c4f7ec69a1afbb19395544 -github.com/containers/storage ff8a6d2bf496daf46ab1a153f783a0f6b8762a54 +github.com/containers/image 54ea27515e713429b1ae1bf5a63002c15c25a206 +github.com/containers/storage 5d52f079f1709f0408a6c086c603d259b0e5da3e github.com/coreos/go-systemd v14 github.com/cri-o/ocicni master github.com/cyphar/filepath-securejoin v0.2.1 diff --git a/vendor/github.com/containers/image/docker/daemon/client.go b/vendor/github.com/containers/image/docker/daemon/client.go index 82fab4b19..11f8f6420 100644 --- a/vendor/github.com/containers/image/docker/daemon/client.go +++ b/vendor/github.com/containers/image/docker/daemon/client.go @@ -27,17 +27,24 @@ func newDockerClient(ctx *types.SystemContext) (*dockerclient.Client, error) { // 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. + // + // 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) if err != nil { return nil, err } var httpClient *http.Client if proto != "unix" { - hc, err := tlsConfig(ctx) - if err != nil { - return nil, err + if proto == "http" { + httpClient = httpConfig() + } else { + hc, err := tlsConfig(ctx) + if err != nil { + return nil, err + } + httpClient = hc } - httpClient = hc } return dockerclient.NewClient(host, defaultAPIVersion, httpClient, nil) @@ -67,3 +74,12 @@ func tlsConfig(ctx *types.SystemContext) (*http.Client, error) { CheckRedirect: dockerclient.CheckRedirect, }, nil } + +func httpConfig() *http.Client { + return &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: nil, + }, + CheckRedirect: dockerclient.CheckRedirect, + } +} diff --git a/vendor/github.com/containers/image/storage/storage_image.go b/vendor/github.com/containers/image/storage/storage_image.go index cc99027a5..0823b7ed9 100644 --- a/vendor/github.com/containers/image/storage/storage_image.go +++ b/vendor/github.com/containers/image/storage/storage_image.go @@ -544,7 +544,7 @@ func (s *storageImageDestination) Commit() error { 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. - 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 { return errors.Wrapf(err, "error adding layer with blob %q", blob.Digest) } diff --git a/vendor/github.com/containers/storage/containers.go b/vendor/github.com/containers/storage/containers.go index 1d8b4d3f6..bf73054bc 100644 --- a/vendor/github.com/containers/storage/containers.go +++ b/vendor/github.com/containers/storage/containers.go @@ -7,6 +7,7 @@ import ( "path/filepath" "time" + "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/truncindex" @@ -56,6 +57,12 @@ type Container struct { // is set before using it. 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"` } @@ -70,7 +77,9 @@ type ContainerStore interface { // random one if an empty value is supplied) and optional names, // based on the specified image, using the specified layer as its // 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 // with the specified ID. @@ -117,6 +126,8 @@ func copyContainer(c *Container) *Container { BigDataSizes: copyStringInt64Map(c.BigDataSizes), BigDataDigests: copyStringDigestMap(c.BigDataDigests), Created: c.Created, + UIDMap: copyIDMap(c.UIDMap), + GIDMap: copyIDMap(c.GIDMap), Flags: copyStringInterfaceMap(c.Flags), } } @@ -252,7 +263,7 @@ func (r *containerStore) SetFlag(id string, flag string, value interface{}) erro 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 == "" { id = stringid.GenerateRandomID() _, 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), Created: time.Now().UTC(), Flags: make(map[string]interface{}), + UIDMap: copyIDMap(options.UIDMap), + GIDMap: copyIDMap(options.GIDMap), } r.containers = append(r.containers, container) r.byid[id] = container diff --git a/vendor/github.com/containers/storage/containers_ffjson.go b/vendor/github.com/containers/storage/containers_ffjson.go index a0fb69eb2..aef6becfe 100644 --- a/vendor/github.com/containers/storage/containers_ffjson.go +++ b/vendor/github.com/containers/storage/containers_ffjson.go @@ -1,5 +1,5 @@ // Code generated by ffjson . DO NOT EDIT. -// source: ./containers.go +// source: containers.go package storage @@ -7,6 +7,7 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/containers/storage/pkg/idtools" "github.com/opencontainers/go-digest" fflib "github.com/pquerna/ffjson/fflib/v1" ) @@ -126,6 +127,46 @@ func (j *Container) MarshalJSONBuf(buf fflib.EncodingBuffer) error { } 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 { buf.WriteString(`"flags":`) /* Falling back. type=map[string]interface {} kind=map */ @@ -162,6 +203,10 @@ const ( ffjtContainerCreated + ffjtContainerUIDMap + + ffjtContainerGIDMap + ffjtContainerFlags ) @@ -183,6 +228,10 @@ var ffjKeyContainerBigDataDigests = []byte("big-data-digests") var ffjKeyContainerCreated = []byte("created") +var ffjKeyContainerUIDMap = []byte("uidmap") + +var ffjKeyContainerGIDMap = []byte("gidmap") + var ffjKeyContainerFlags = []byte("flags") // UnmarshalJSON umarshall json - template of ffjson @@ -280,6 +329,14 @@ mainparse: goto mainparse } + case 'g': + + if bytes.Equal(ffjKeyContainerGIDMap, kn) { + currentKey = ffjtContainerGIDMap + state = fflib.FFParse_want_colon + goto mainparse + } + case 'i': if bytes.Equal(ffjKeyContainerID, kn) { @@ -317,6 +374,14 @@ mainparse: goto mainparse } + case 'u': + + if bytes.Equal(ffjKeyContainerUIDMap, kn) { + currentKey = ffjtContainerUIDMap + state = fflib.FFParse_want_colon + goto mainparse + } + } if fflib.EqualFoldRight(ffjKeyContainerFlags, kn) { @@ -325,6 +390,18 @@ 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) { currentKey = ffjtContainerCreated state = fflib.FFParse_want_colon @@ -423,6 +500,12 @@ mainparse: case ffjtContainerCreated: goto handle_Created + case ffjtContainerUIDMap: + goto handle_UIDMap + + case ffjtContainerGIDMap: + goto handle_GIDMap + case ffjtContainerFlags: goto handle_Flags @@ -931,6 +1014,142 @@ handle_Created: state = fflib.FFParse_after_value 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: /* handler: j.Flags type=map[string]interface {} kind=map quoted=false*/ diff --git a/vendor/github.com/containers/storage/drivers/aufs/aufs.go b/vendor/github.com/containers/storage/drivers/aufs/aufs.go index c1cfabee9..bbbe9e4af 100644 --- a/vendor/github.com/containers/storage/drivers/aufs/aufs.go +++ b/vendor/github.com/containers/storage/drivers/aufs/aufs.go @@ -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 } @@ -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") } - if err := a.createDirsFor(id); err != nil { + if err := a.createDirsFor(id, parent); err != nil { return err } // 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. // mnt and diff -func (a *Driver) createDirsFor(id string) error { +func (a *Driver) createDirsFor(id, parent string) error { paths := []string{ "mnt", "diff", } - rootUID, rootGID, err := idtools.GetRootUIDGID(a.uidMaps, a.gidMaps) - if err != nil { - return err - } // Directory permission is 0755. // The path of directories are /mnt/ // and /diff/ 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 } } @@ -463,17 +468,21 @@ func (a *Driver) isParent(id, parent string) bool { // Diff produces an archive of the changes between the specified // 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) { - 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. return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ Compression: archive.Uncompressed, ExcludePatterns: []string{archive.WhiteoutMetaPrefix + "*", "!" + archive.WhiteoutOpaqueDir}, - UIDMaps: a.uidMaps, - GIDMaps: a.gidMaps, + UIDMaps: idMappings.UIDs(), + GIDMaps: idMappings.GIDs(), }) } @@ -492,19 +501,22 @@ func (a *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { 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{ - UIDMaps: a.uidMaps, - GIDMaps: a.gidMaps, + UIDMaps: idMappings.UIDs(), + GIDMaps: idMappings.GIDs(), }) } // DiffSize calculates the changes between the specified id // and its parent and returns the size in bytes of the changes // 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) { - 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. 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 // layer with the specified id and parent, returning the size of the // 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) { - 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. - if err = a.applyDiff(id, diff); err != nil { + if err = a.applyDiff(id, idMappings, diff); err != nil { 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 // 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) { - 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 @@ -689,3 +701,9 @@ func useDirperm() bool { }) 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") +} diff --git a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go index abc856c83..4b9699fdb 100644 --- a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go +++ b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go @@ -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) { diff --git a/vendor/github.com/containers/storage/drivers/chown.go b/vendor/github.com/containers/storage/drivers/chown.go new file mode 100644 index 000000000..c12e73b3b --- /dev/null +++ b/vendor/github.com/containers/storage/drivers/chown.go @@ -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) +} diff --git a/vendor/github.com/containers/storage/drivers/chroot_unix.go b/vendor/github.com/containers/storage/drivers/chroot_unix.go new file mode 100644 index 000000000..c8c4905bf --- /dev/null +++ b/vendor/github.com/containers/storage/drivers/chroot_unix.go @@ -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 +} diff --git a/vendor/github.com/containers/storage/drivers/chroot_windows.go b/vendor/github.com/containers/storage/drivers/chroot_windows.go new file mode 100644 index 000000000..1df031789 --- /dev/null +++ b/vendor/github.com/containers/storage/drivers/chroot_windows.go @@ -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 +} diff --git a/vendor/github.com/containers/storage/drivers/devmapper/driver.go b/vendor/github.com/containers/storage/drivers/devmapper/driver.go index d68fb66cc..a4ec6ebfd 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/driver.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/driver.go @@ -54,7 +54,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap locker: locker.New(), } - return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil + return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil } func (d *Driver) String() string { diff --git a/vendor/github.com/containers/storage/drivers/driver.go b/vendor/github.com/containers/storage/drivers/driver.go index 615d93be5..1b4ad336d 100644 --- a/vendor/github.com/containers/storage/drivers/driver.go +++ b/vendor/github.com/containers/storage/drivers/driver.go @@ -92,25 +92,39 @@ type ProtoDriver interface { type DiffDriver interface { // Diff produces an archive of the changes between the specified // 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 // 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 // layer with the specified id and parent, returning the size of the // new layer in bytes. // 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 // and its parent and returns the size in bytes of the changes // 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. type Driver interface { ProtoDriver DiffDriver + LayerIDMapUpdater } // Capabilities defines a list of capabilities a driver may implement. diff --git a/vendor/github.com/containers/storage/drivers/fsdiff.go b/vendor/github.com/containers/storage/drivers/fsdiff.go index f74239cb9..35869d906 100644 --- a/vendor/github.com/containers/storage/drivers/fsdiff.go +++ b/vendor/github.com/containers/storage/drivers/fsdiff.go @@ -24,29 +24,33 @@ var ( // Notably, the AUFS driver doesn't need to be wrapped like this. type NaiveDiffDriver struct { ProtoDriver - uidMaps []idtools.IDMap - gidMaps []idtools.IDMap + LayerIDMapUpdater } // NewNaiveDiffDriver returns a fully functional driver that wraps the // given ProtoDriver and adds the capability of the following methods which // it may or may not support on its own: -// Diff(id, parent, mountLabel string) (io.ReadCloser, error) -// Changes(id, parent, mountLabel string) ([]archive.Change, error) -// ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size int64, err error) -// DiffSize(id, parent, mountLabel string) (size int64, err error) -func NewNaiveDiffDriver(driver ProtoDriver, uidMaps, gidMaps []idtools.IDMap) Driver { - return &NaiveDiffDriver{ProtoDriver: driver, - uidMaps: uidMaps, - gidMaps: gidMaps} +// Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error) +// Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) +// ApplyDiff(id string, idMappings *idtools.IDMappings, parent, mountLabel string, diff io.Reader) (size int64, err error) +// DiffSize(id string, idMappings *idtools.IDMappings, parent, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) +func NewNaiveDiffDriver(driver ProtoDriver, updater LayerIDMapUpdater) Driver { + return &NaiveDiffDriver{ProtoDriver: driver, LayerIDMapUpdater: updater} } // Diff produces an archive of the changes between the specified // 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() driver := gdw.ProtoDriver + if idMappings == nil { + idMappings = &idtools.IDMappings{} + } + if parentMappings == nil { + parentMappings = &idtools.IDMappings{} + } + layerFs, err := driver.Get(id, mountLabel) if err != nil { return nil, err @@ -59,7 +63,11 @@ func (gdw *NaiveDiffDriver) Diff(id, parent, mountLabel string) (arch io.ReadClo }() 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 { return nil, err } @@ -76,12 +84,12 @@ func (gdw *NaiveDiffDriver) Diff(id, parent, mountLabel string) (arch io.ReadClo } defer driver.Put(parent) - changes, err := archive.ChangesDirs(layerFs, parentFs) + changes, err := archive.ChangesDirs(layerFs, idMappings, parentFs, parentMappings) if err != nil { 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 { 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 // 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 + if idMappings == nil { + idMappings = &idtools.IDMappings{} + } + if parentMappings == nil { + parentMappings = &idtools.IDMappings{} + } + layerFs, err := driver.Get(id, mountLabel) if err != nil { return nil, err @@ -120,15 +135,19 @@ func (gdw *NaiveDiffDriver) Changes(id, parent, mountLabel string) ([]archive.Ch 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 // layer with the specified id and parent, returning the size of the // 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 + if applyMappings == nil { + applyMappings = &idtools.IDMappings{} + } + // Mount the root filesystem so we can apply the diff/layer. layerFs, err := driver.Get(id, mountLabel) if err != nil { @@ -136,8 +155,11 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id, parent, mountLabel string, diff io.Rea } defer driver.Put(id) - options := &archive.TarOptions{UIDMaps: gdw.uidMaps, - GIDMaps: gdw.gidMaps} + options := &archive.TarOptions{} + if applyMappings != nil { + options.UIDMaps = applyMappings.UIDs() + options.GIDMaps = applyMappings.GIDs() + } start := time.Now().UTC() logrus.Debug("Start untar layer") 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 // and its parent and returns the size in bytes of the changes // 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 - 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 { return } diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index de6ee6da5..6b5f6912f 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -169,7 +169,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap options: *opts, } - d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) + d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, d) if backingFs == "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) flags := fmt.Sprintf("lowerdir=%s:%s", lower1Dir, lower2Dir) 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") return supportsDType, nil + } else { + logrus.Debugf("overlay test mount with multiple lowers failed %v", err) } } flags = fmt.Sprintf("lowerdir=%s", lower1Dir) 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") 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) @@ -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 { 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 { return err } @@ -562,8 +576,32 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) { 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), ":") { + if l == "" { + continue + } lower := "" newpath := path.Join(d.home, l) if _, err := os.Stat(newpath); err != nil { @@ -580,15 +618,23 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) { } else { lower = newpath } - if newlowers == "" { - newlowers = lower - } else { - newlowers = newlowers + ":" + lower + absLowers = append(absLowers, lower) + relLowers = append(relLowers, l) + diffN = 1 + _, 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") - lowers = []byte(newlowers) + + // If the lowers list is still empty, use an empty lower so that we can still force an + // 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") @@ -606,7 +652,7 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) { }() 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) mount := unix.Mount 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. if len(mountData) > pageSize { //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) if len(mountData) > pageSize { 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 -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) { - 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) @@ -707,8 +757,8 @@ func (d *Driver) ApplyDiff(id, parent, mountLabel string, diff io.Reader) (size logrus.Debugf("Applying tar in %s", applyDir) // Overlay doesn't need the parent id to apply the diff if err := untar(diff, applyDir, &archive.TarOptions{ - UIDMaps: d.uidMaps, - GIDMaps: d.gidMaps, + UIDMaps: idMappings.UIDs(), + GIDMaps: idMappings.GIDs(), WhiteoutFormat: archive.OverlayWhiteoutFormat, }); err != nil { return 0, err @@ -726,18 +776,22 @@ func (d *Driver) getDiffPath(id string) string { // DiffSize calculates the changes between the specified id // and its parent and returns the size in bytes of the changes // 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) { - return d.naiveDiff.DiffSize(id, parent, mountLabel) + return d.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel) } return directory.Size(d.getDiffPath(id)) } // Diff produces an archive of the changes between the specified // 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) { - 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) @@ -749,8 +803,8 @@ func (d *Driver) Diff(id, parent, mountLabel string) (io.ReadCloser, error) { logrus.Debugf("Tar with options on %s", diffPath) return archive.TarWithOptions(diffPath, &archive.TarOptions{ Compression: archive.Uncompressed, - UIDMaps: d.uidMaps, - GIDMaps: d.gidMaps, + UIDMaps: idMappings.UIDs(), + GIDMaps: idMappings.GIDs(), WhiteoutFormat: archive.OverlayWhiteoutFormat, 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 // 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) { - 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 // layers. @@ -777,3 +831,73 @@ func (d *Driver) Changes(id, parent, mountLabel string) ([]archive.Change, error func (d *Driver) AdditionalImageStores() []string { 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) +} diff --git a/vendor/github.com/containers/storage/drivers/vfs/driver.go b/vendor/github.com/containers/storage/drivers/vfs/driver.go index ae62207d1..cee55f8d1 100644 --- a/vendor/github.com/containers/storage/drivers/vfs/driver.go +++ b/vendor/github.com/containers/storage/drivers/vfs/driver.go @@ -43,7 +43,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap 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. @@ -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 { 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 { return err } diff --git a/vendor/github.com/containers/storage/drivers/windows/windows.go b/vendor/github.com/containers/storage/drivers/windows/windows.go index e9e9f5c65..b750715bf 100644 --- a/vendor/github.com/containers/storage/drivers/windows/windows.go +++ b/vendor/github.com/containers/storage/drivers/windows/windows.go @@ -472,7 +472,7 @@ func (d *Driver) Cleanup() error { // Diff produces an archive of the changes between the specified // layer and its parent layer which may be "". // 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() rID, err := d.resolveID(id) 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 // and its parent layer. If parent is "", then all changes will be ADD changes. // 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() rID, err := d.resolveID(id) 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 // new layer in bytes. // 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() var layerChain []string 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 // and its parent and returns the size in bytes of the changes // 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() rPId, err := d.resolveID(parent) if err != nil { return } - changes, err := d.Changes(id, rPId, mountLabel) + changes, err := d.Changes(id, idMappings, rPId, parentMappings, mountLabel) if err != nil { return } @@ -940,6 +940,17 @@ func (d *Driver) AdditionalImageStores() []string { 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 { size uint64 } diff --git a/vendor/github.com/containers/storage/drivers/zfs/zfs.go b/vendor/github.com/containers/storage/drivers/zfs/zfs.go index 8c8e7d671..886ebc8bd 100644 --- a/vendor/github.com/containers/storage/drivers/zfs/zfs.go +++ b/vendor/github.com/containers/storage/drivers/zfs/zfs.go @@ -119,7 +119,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri gidMaps: gidMaps, 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) { diff --git a/vendor/github.com/containers/storage/errors.go b/vendor/github.com/containers/storage/errors.go index bed6f8cdc..562415c09 100644 --- a/vendor/github.com/containers/storage/errors.go +++ b/vendor/github.com/containers/storage/errors.go @@ -53,4 +53,6 @@ var ( 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 = 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") ) diff --git a/vendor/github.com/containers/storage/images_ffjson.go b/vendor/github.com/containers/storage/images_ffjson.go index f91ee6d4f..f6a8b0650 100644 --- a/vendor/github.com/containers/storage/images_ffjson.go +++ b/vendor/github.com/containers/storage/images_ffjson.go @@ -1,5 +1,5 @@ // Code generated by ffjson . DO NOT EDIT. -// source: ./images.go +// source: images.go package storage diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index 91117a4f2..9a060e684 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -8,12 +8,16 @@ import ( "io/ioutil" "os" "path/filepath" + "reflect" + "sort" "time" drivers "github.com/containers/storage/drivers" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/stringid" + "github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/truncindex" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" @@ -93,6 +97,11 @@ type Layer struct { // Flags is arbitrary data about the layer. 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 { @@ -178,13 +187,13 @@ type LayerStore interface { // underlying drivers can accept a "size" option. At this time, most // underlying drivers do not themselves distinguish between writeable // 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(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(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 // supplied values. @@ -204,6 +213,10 @@ type LayerStore interface { // Unmount unmounts a layer when it is no longer in use. 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 // applies its changes to a specified layer. ApplyDiff(to string, diff io.Reader) (int64, error) @@ -221,6 +234,8 @@ type layerStore struct { bymount map[string]*Layer bycompressedsum map[digest.Digest][]string byuncompressedsum map[digest.Digest][]string + uidMap []idtools.IDMap + gidMap []idtools.IDMap } func copyLayer(l *Layer) *Layer { @@ -239,6 +254,8 @@ func copyLayer(l *Layer) *Layer { UncompressedSize: l.UncompressedSize, CompressionType: l.CompressionType, 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) } -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 { return nil, err } @@ -403,6 +420,8 @@ func newLayerStore(rundir string, layerdir string, driver drivers.Driver) (Layer byid: make(map[string]*Layer), bymount: make(map[string]*Layer), byname: make(map[string]*Layer), + uidMap: copyIDMap(uidMap), + gidMap: copyIDMap(gidMap), } if err := rlstore.Load(); err != nil { return nil, err @@ -489,7 +508,7 @@ func (r *layerStore) Status() ([][2]string, error) { 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() { 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 { return nil, -1, err } - if parent != "" { - if parentLayer, ok := r.lookup(parent); ok { - parent = parentLayer.ID - } - } if id == "" { id = stringid.GenerateRandomID() _, idInUse := r.byid[id] @@ -522,6 +536,15 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o 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{ MountLabel: mountLabel, StorageOpt: options, @@ -531,6 +554,15 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o } else { 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 { layer = &Layer{ ID: id, @@ -539,6 +571,8 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o MountLabel: mountLabel, Created: time.Now().UTC(), Flags: make(map[string]interface{}), + UIDMap: copyIDMap(moreOptions.UIDMap), + GIDMap: copyIDMap(moreOptions.GIDMap), } r.layers = append(r.layers, layer) 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 } -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) { - layer, _, err = r.Put(id, parent, names, mountLabel, options, writeable, flags, nil) +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, moreOptions, writeable, flags, nil) return layer, err } -func (r *layerStore) Create(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool) (layer *Layer, err error) { - return r.CreateWithFlags(id, parent, names, mountLabel, options, writeable, nil) +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, moreOptions, writeable, nil) } func (r *layerStore) Mount(id, mountLabel string) (string, error) { @@ -645,6 +679,67 @@ func (r *layerStore) Unmount(id string) error { 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) { layer.Names = stringSliceWithoutValue(layer.Names, name) } @@ -771,12 +866,11 @@ func (r *layerStore) Wipe() error { 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 fromLayer *Layer toLayer, ok = r.lookup(to) if !ok { - return "", "", nil, ErrLayerUnknown + return "", "", nil, nil, ErrLayerUnknown } to = toLayer.ID 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) { - from, to, toLayer, err := r.findParentAndLayer(from, to) + from, to, fromLayer, toLayer, err := r.findParentAndLayer(from, to) if err != nil { 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 { @@ -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) { var metadata storage.Unpacker - from, to, toLayer, err := r.findParentAndLayer(from, to) + from, to, fromLayer, toLayer, err := r.findParentAndLayer(from, to) if err != nil { return nil, ErrLayerUnknown } @@ -874,7 +975,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, } 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 { return nil, err } @@ -886,7 +987,7 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser, if !os.IsNotExist(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 { 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) { - var toLayer *Layer - from, to, toLayer, err = r.findParentAndLayer(from, to) + var fromLayer, toLayer *Layer + from, to, fromLayer, toLayer, err = r.findParentAndLayer(from, to) if err != nil { 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) { @@ -970,7 +1071,7 @@ func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error if err != nil { 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 { return -1, err } diff --git a/vendor/github.com/containers/storage/layers_ffjson.go b/vendor/github.com/containers/storage/layers_ffjson.go index 608fe1863..125b5d8c9 100644 --- a/vendor/github.com/containers/storage/layers_ffjson.go +++ b/vendor/github.com/containers/storage/layers_ffjson.go @@ -1,5 +1,5 @@ // Code generated by ffjson . DO NOT EDIT. -// source: ./layers.go +// source: layers.go package storage @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" "github.com/opencontainers/go-digest" fflib "github.com/pquerna/ffjson/fflib/v1" ) @@ -323,6 +324,46 @@ func (j *Layer) MarshalJSONBuf(buf fflib.EncodingBuffer) error { } 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.WriteByte('}') return nil @@ -355,6 +396,10 @@ const ( ffjtLayerCompressionType ffjtLayerFlags + + ffjtLayerUIDMap + + ffjtLayerGIDMap ) var ffjKeyLayerID = []byte("id") @@ -381,6 +426,10 @@ var ffjKeyLayerCompressionType = []byte("compression") var ffjKeyLayerFlags = []byte("flags") +var ffjKeyLayerUIDMap = []byte("uidmap") + +var ffjKeyLayerGIDMap = []byte("gidmap") + // UnmarshalJSON umarshall json - template of ffjson func (j *Layer) UnmarshalJSON(input []byte) error { fs := fflib.NewFFLexer(input) @@ -486,6 +535,14 @@ mainparse: goto mainparse } + case 'g': + + if bytes.Equal(ffjKeyLayerGIDMap, kn) { + currentKey = ffjtLayerGIDMap + state = fflib.FFParse_want_colon + goto mainparse + } + case 'i': if bytes.Equal(ffjKeyLayerID, kn) { @@ -523,6 +580,26 @@ 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) { @@ -650,6 +727,12 @@ mainparse: case ffjtLayerFlags: goto handle_Flags + case ffjtLayerUIDMap: + goto handle_UIDMap + + case ffjtLayerGIDMap: + goto handle_GIDMap + case ffjtLayernosuchkey: err = fs.SkipField(tok) if err != nil { @@ -1108,6 +1191,142 @@ handle_Flags: state = fflib.FFParse_after_value 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: return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) wrongtokenerror: diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go index e74cddd70..1002296f2 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive.go @@ -59,18 +59,21 @@ type ( } ) -// Archiver allows the reuse of most utility functions of this package -// with a pluggable Untar function. Also, to facilitate the passing of -// specific id mappings for untar, an archiver can be created with maps -// which will then be passed to Untar operations +// Archiver allows the reuse of most utility functions of this package with a +// pluggable Untar function. To facilitate the passing of specific id mappings +// for untar, an archiver can be created with maps which will then be passed to +// Untar operations. If ChownOpts is set, its values are mapped using +// UntarIDMappings before being used to create files and directories on disk. type Archiver struct { - Untar func(io.Reader, string, *TarOptions) error - IDMappings *idtools.IDMappings + Untar func(io.Reader, string, *TarOptions) error + TarIDMappings *idtools.IDMappings + ChownOpts *idtools.IDPair + UntarIDMappings *idtools.IDMappings } // NewDefaultArchiver returns a new Archiver without any IDMappings 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 @@ -942,7 +945,7 @@ loop: } trBuf.Reset(tr) - if err := remapIDs(idMappings, hdr); err != nil { + if err := remapIDs(nil, idMappings, options.ChownOpts, hdr); err != nil { 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. func (archiver *Archiver) TarUntar(src, dst string) error { 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 { return err } defer archive.Close() - options := &TarOptions{ - UIDMaps: archiver.IDMappings.UIDs(), - GIDMaps: archiver.IDMappings.GIDs(), + untarMappings := archiver.UntarIDMappings + if untarMappings == nil { + untarMappings = &idtools.IDMappings{} + } + options = &TarOptions{ + UIDMaps: untarMappings.UIDs(), + GIDMaps: untarMappings.GIDs(), + ChownOpts: archiver.ChownOpts, } return archiver.Untar(archive, dst, options) } @@ -1042,9 +1059,14 @@ func (archiver *Archiver) UntarPath(src, dst string) error { return err } defer archive.Close() + untarMappings := archiver.UntarIDMappings + if untarMappings == nil { + untarMappings = &idtools.IDMappings{} + } options := &TarOptions{ - UIDMaps: archiver.IDMappings.UIDs(), - GIDMaps: archiver.IDMappings.GIDs(), + UIDMaps: untarMappings.UIDs(), + GIDMaps: untarMappings.GIDs(), + ChownOpts: archiver.ChownOpts, } 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 // the new destination directory with the remapped root UID/GID pair // 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 logrus.Debugf("Creating dest directory: %s", dst) 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.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 } @@ -1143,10 +1168,29 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { return err } -func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error { - ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}) +func remapIDs(readIDMappings, writeIDMappings *idtools.IDMappings, chownOpts *idtools.IDPair, hdr *tar.Header) (err error) { + var uid, gid int + 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 + } + } 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 err + return nil } // cmdStream executes a command, and returns its stdout as a stream. diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go b/vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go new file mode 100644 index 000000000..6f0f4524e --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go @@ -0,0 +1,2230 @@ +// Code generated by ffjson . DO NOT EDIT. +// source: pkg/archive/archive.go + +package archive + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "github.com/containers/storage/pkg/idtools" + fflib "github.com/pquerna/ffjson/fflib/v1" +) + +// MarshalJSON marshal bytes to json - template +func (j *Archiver) MarshalJSON() ([]byte, error) { + var buf fflib.Buffer + if j == nil { + buf.WriteString("null") + return buf.Bytes(), nil + } + err := j.MarshalJSONBuf(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalJSONBuf marshal buff to json - template +func (j *Archiver) MarshalJSONBuf(buf fflib.EncodingBuffer) error { + if j == nil { + buf.WriteString("null") + return nil + } + var err error + var obj []byte + _ = obj + _ = err + buf.WriteString(`{"Untar":`) + /* Falling back. type=func(io.Reader, string, *archive.TarOptions) error kind=func */ + err = buf.Encode(j.Untar) + if err != nil { + return err + } + if j.TarIDMappings != nil { + /* Struct fall back. type=idtools.IDMappings kind=struct */ + buf.WriteString(`,"TarIDMappings":`) + err = buf.Encode(j.TarIDMappings) + if err != nil { + return err + } + } else { + buf.WriteString(`,"TarIDMappings":null`) + } + if j.ChownOpts != nil { + /* Struct fall back. type=idtools.IDPair kind=struct */ + buf.WriteString(`,"ChownOpts":`) + err = buf.Encode(j.ChownOpts) + if err != nil { + return err + } + } else { + buf.WriteString(`,"ChownOpts":null`) + } + if j.UntarIDMappings != nil { + /* Struct fall back. type=idtools.IDMappings kind=struct */ + buf.WriteString(`,"UntarIDMappings":`) + err = buf.Encode(j.UntarIDMappings) + if err != nil { + return err + } + } else { + buf.WriteString(`,"UntarIDMappings":null`) + } + buf.WriteByte('}') + return nil +} + +const ( + ffjtArchiverbase = iota + ffjtArchivernosuchkey + + ffjtArchiverUntar + + ffjtArchiverTarIDMappings + + ffjtArchiverChownOpts + + ffjtArchiverUntarIDMappings +) + +var ffjKeyArchiverUntar = []byte("Untar") + +var ffjKeyArchiverTarIDMappings = []byte("TarIDMappings") + +var ffjKeyArchiverChownOpts = []byte("ChownOpts") + +var ffjKeyArchiverUntarIDMappings = []byte("UntarIDMappings") + +// UnmarshalJSON umarshall json - template of ffjson +func (j *Archiver) UnmarshalJSON(input []byte) error { + fs := fflib.NewFFLexer(input) + return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) +} + +// UnmarshalJSONFFLexer fast json unmarshall - template ffjson +func (j *Archiver) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { + var err error + currentKey := ffjtArchiverbase + _ = currentKey + tok := fflib.FFTok_init + wantedTok := fflib.FFTok_init + +mainparse: + for { + tok = fs.Scan() + // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) + if tok == fflib.FFTok_error { + goto tokerror + } + + switch state { + + case fflib.FFParse_map_start: + if tok != fflib.FFTok_left_bracket { + wantedTok = fflib.FFTok_left_bracket + goto wrongtokenerror + } + state = fflib.FFParse_want_key + continue + + case fflib.FFParse_after_value: + if tok == fflib.FFTok_comma { + state = fflib.FFParse_want_key + } else if tok == fflib.FFTok_right_bracket { + goto done + } else { + wantedTok = fflib.FFTok_comma + goto wrongtokenerror + } + + case fflib.FFParse_want_key: + // json {} ended. goto exit. woo. + if tok == fflib.FFTok_right_bracket { + goto done + } + if tok != fflib.FFTok_string { + wantedTok = fflib.FFTok_string + goto wrongtokenerror + } + + kn := fs.Output.Bytes() + if len(kn) <= 0 { + // "" case. hrm. + currentKey = ffjtArchivernosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } else { + switch kn[0] { + + case 'C': + + if bytes.Equal(ffjKeyArchiverChownOpts, kn) { + currentKey = ffjtArchiverChownOpts + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'T': + + if bytes.Equal(ffjKeyArchiverTarIDMappings, kn) { + currentKey = ffjtArchiverTarIDMappings + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'U': + + if bytes.Equal(ffjKeyArchiverUntar, kn) { + currentKey = ffjtArchiverUntar + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyArchiverUntarIDMappings, kn) { + currentKey = ffjtArchiverUntarIDMappings + state = fflib.FFParse_want_colon + goto mainparse + } + + } + + if fflib.EqualFoldRight(ffjKeyArchiverUntarIDMappings, kn) { + currentKey = ffjtArchiverUntarIDMappings + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyArchiverChownOpts, kn) { + currentKey = ffjtArchiverChownOpts + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyArchiverTarIDMappings, kn) { + currentKey = ffjtArchiverTarIDMappings + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.SimpleLetterEqualFold(ffjKeyArchiverUntar, kn) { + currentKey = ffjtArchiverUntar + state = fflib.FFParse_want_colon + goto mainparse + } + + currentKey = ffjtArchivernosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } + + case fflib.FFParse_want_colon: + if tok != fflib.FFTok_colon { + wantedTok = fflib.FFTok_colon + goto wrongtokenerror + } + state = fflib.FFParse_want_value + continue + case fflib.FFParse_want_value: + + if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null { + switch currentKey { + + case ffjtArchiverUntar: + goto handle_Untar + + case ffjtArchiverTarIDMappings: + goto handle_TarIDMappings + + case ffjtArchiverChownOpts: + goto handle_ChownOpts + + case ffjtArchiverUntarIDMappings: + goto handle_UntarIDMappings + + case ffjtArchivernosuchkey: + err = fs.SkipField(tok) + if err != nil { + return fs.WrapErr(err) + } + state = fflib.FFParse_after_value + goto mainparse + } + } else { + goto wantedvalue + } + } + } + +handle_Untar: + + /* handler: j.Untar type=func(io.Reader, string, *archive.TarOptions) error kind=func quoted=false*/ + + { + /* Falling back. type=func(io.Reader, string, *archive.TarOptions) error kind=func */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.Untar) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_TarIDMappings: + + /* handler: j.TarIDMappings type=idtools.IDMappings kind=struct quoted=false*/ + + { + /* Falling back. type=idtools.IDMappings kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.TarIDMappings) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_ChownOpts: + + /* handler: j.ChownOpts type=idtools.IDPair kind=struct quoted=false*/ + + { + /* Falling back. type=idtools.IDPair kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.ChownOpts) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UntarIDMappings: + + /* handler: j.UntarIDMappings type=idtools.IDMappings kind=struct quoted=false*/ + + { + /* Falling back. type=idtools.IDMappings kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.UntarIDMappings) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +wantedvalue: + return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) +wrongtokenerror: + return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) +tokerror: + if fs.BigError != nil { + return fs.WrapErr(fs.BigError) + } + err = fs.Error.ToError() + if err != nil { + return fs.WrapErr(err) + } + panic("ffjson-generated: unreachable, please report bug.") +done: + + return nil +} + +// MarshalJSON marshal bytes to json - template +func (j *TarOptions) MarshalJSON() ([]byte, error) { + var buf fflib.Buffer + if j == nil { + buf.WriteString("null") + return buf.Bytes(), nil + } + err := j.MarshalJSONBuf(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalJSONBuf marshal buff to json - template +func (j *TarOptions) MarshalJSONBuf(buf fflib.EncodingBuffer) error { + if j == nil { + buf.WriteString("null") + return nil + } + var err error + var obj []byte + _ = obj + _ = err + buf.WriteString(`{"IncludeFiles":`) + if j.IncludeFiles != nil { + buf.WriteString(`[`) + for i, v := range j.IncludeFiles { + if i != 0 { + buf.WriteString(`,`) + } + fflib.WriteJsonString(buf, string(v)) + } + buf.WriteString(`]`) + } else { + buf.WriteString(`null`) + } + buf.WriteString(`,"ExcludePatterns":`) + if j.ExcludePatterns != nil { + buf.WriteString(`[`) + for i, v := range j.ExcludePatterns { + if i != 0 { + buf.WriteString(`,`) + } + fflib.WriteJsonString(buf, string(v)) + } + buf.WriteString(`]`) + } else { + buf.WriteString(`null`) + } + buf.WriteString(`,"Compression":`) + fflib.FormatBits2(buf, uint64(j.Compression), 10, j.Compression < 0) + if j.NoLchown { + buf.WriteString(`,"NoLchown":true`) + } else { + buf.WriteString(`,"NoLchown":false`) + } + buf.WriteString(`,"UIDMaps":`) + if j.UIDMaps != nil { + buf.WriteString(`[`) + for i, v := range j.UIDMaps { + 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.WriteString(`,"GIDMaps":`) + if j.GIDMaps != nil { + buf.WriteString(`[`) + for i, v := range j.GIDMaps { + 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`) + } + if j.ChownOpts != nil { + /* Struct fall back. type=idtools.IDPair kind=struct */ + buf.WriteString(`,"ChownOpts":`) + err = buf.Encode(j.ChownOpts) + if err != nil { + return err + } + } else { + buf.WriteString(`,"ChownOpts":null`) + } + if j.IncludeSourceDir { + buf.WriteString(`,"IncludeSourceDir":true`) + } else { + buf.WriteString(`,"IncludeSourceDir":false`) + } + buf.WriteString(`,"WhiteoutFormat":`) + fflib.FormatBits2(buf, uint64(j.WhiteoutFormat), 10, j.WhiteoutFormat < 0) + buf.WriteString(`,"WhiteoutData":`) + /* Interface types must use runtime reflection. type=interface {} kind=interface */ + err = buf.Encode(j.WhiteoutData) + if err != nil { + return err + } + if j.NoOverwriteDirNonDir { + buf.WriteString(`,"NoOverwriteDirNonDir":true`) + } else { + buf.WriteString(`,"NoOverwriteDirNonDir":false`) + } + if j.RebaseNames == nil { + buf.WriteString(`,"RebaseNames":null`) + } else { + buf.WriteString(`,"RebaseNames":{ `) + for key, value := range j.RebaseNames { + fflib.WriteJsonString(buf, key) + buf.WriteString(`:`) + fflib.WriteJsonString(buf, string(value)) + buf.WriteByte(',') + } + buf.Rewind(1) + buf.WriteByte('}') + } + if j.InUserNS { + buf.WriteString(`,"InUserNS":true`) + } else { + buf.WriteString(`,"InUserNS":false`) + } + buf.WriteByte('}') + return nil +} + +const ( + ffjtTarOptionsbase = iota + ffjtTarOptionsnosuchkey + + ffjtTarOptionsIncludeFiles + + ffjtTarOptionsExcludePatterns + + ffjtTarOptionsCompression + + ffjtTarOptionsNoLchown + + ffjtTarOptionsUIDMaps + + ffjtTarOptionsGIDMaps + + ffjtTarOptionsChownOpts + + ffjtTarOptionsIncludeSourceDir + + ffjtTarOptionsWhiteoutFormat + + ffjtTarOptionsWhiteoutData + + ffjtTarOptionsNoOverwriteDirNonDir + + ffjtTarOptionsRebaseNames + + ffjtTarOptionsInUserNS +) + +var ffjKeyTarOptionsIncludeFiles = []byte("IncludeFiles") + +var ffjKeyTarOptionsExcludePatterns = []byte("ExcludePatterns") + +var ffjKeyTarOptionsCompression = []byte("Compression") + +var ffjKeyTarOptionsNoLchown = []byte("NoLchown") + +var ffjKeyTarOptionsUIDMaps = []byte("UIDMaps") + +var ffjKeyTarOptionsGIDMaps = []byte("GIDMaps") + +var ffjKeyTarOptionsChownOpts = []byte("ChownOpts") + +var ffjKeyTarOptionsIncludeSourceDir = []byte("IncludeSourceDir") + +var ffjKeyTarOptionsWhiteoutFormat = []byte("WhiteoutFormat") + +var ffjKeyTarOptionsWhiteoutData = []byte("WhiteoutData") + +var ffjKeyTarOptionsNoOverwriteDirNonDir = []byte("NoOverwriteDirNonDir") + +var ffjKeyTarOptionsRebaseNames = []byte("RebaseNames") + +var ffjKeyTarOptionsInUserNS = []byte("InUserNS") + +// UnmarshalJSON umarshall json - template of ffjson +func (j *TarOptions) UnmarshalJSON(input []byte) error { + fs := fflib.NewFFLexer(input) + return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) +} + +// UnmarshalJSONFFLexer fast json unmarshall - template ffjson +func (j *TarOptions) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { + var err error + currentKey := ffjtTarOptionsbase + _ = currentKey + tok := fflib.FFTok_init + wantedTok := fflib.FFTok_init + +mainparse: + for { + tok = fs.Scan() + // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) + if tok == fflib.FFTok_error { + goto tokerror + } + + switch state { + + case fflib.FFParse_map_start: + if tok != fflib.FFTok_left_bracket { + wantedTok = fflib.FFTok_left_bracket + goto wrongtokenerror + } + state = fflib.FFParse_want_key + continue + + case fflib.FFParse_after_value: + if tok == fflib.FFTok_comma { + state = fflib.FFParse_want_key + } else if tok == fflib.FFTok_right_bracket { + goto done + } else { + wantedTok = fflib.FFTok_comma + goto wrongtokenerror + } + + case fflib.FFParse_want_key: + // json {} ended. goto exit. woo. + if tok == fflib.FFTok_right_bracket { + goto done + } + if tok != fflib.FFTok_string { + wantedTok = fflib.FFTok_string + goto wrongtokenerror + } + + kn := fs.Output.Bytes() + if len(kn) <= 0 { + // "" case. hrm. + currentKey = ffjtTarOptionsnosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } else { + switch kn[0] { + + case 'C': + + if bytes.Equal(ffjKeyTarOptionsCompression, kn) { + currentKey = ffjtTarOptionsCompression + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyTarOptionsChownOpts, kn) { + currentKey = ffjtTarOptionsChownOpts + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'E': + + if bytes.Equal(ffjKeyTarOptionsExcludePatterns, kn) { + currentKey = ffjtTarOptionsExcludePatterns + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'G': + + if bytes.Equal(ffjKeyTarOptionsGIDMaps, kn) { + currentKey = ffjtTarOptionsGIDMaps + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'I': + + if bytes.Equal(ffjKeyTarOptionsIncludeFiles, kn) { + currentKey = ffjtTarOptionsIncludeFiles + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyTarOptionsIncludeSourceDir, kn) { + currentKey = ffjtTarOptionsIncludeSourceDir + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyTarOptionsInUserNS, kn) { + currentKey = ffjtTarOptionsInUserNS + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'N': + + if bytes.Equal(ffjKeyTarOptionsNoLchown, kn) { + currentKey = ffjtTarOptionsNoLchown + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyTarOptionsNoOverwriteDirNonDir, kn) { + currentKey = ffjtTarOptionsNoOverwriteDirNonDir + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'R': + + if bytes.Equal(ffjKeyTarOptionsRebaseNames, kn) { + currentKey = ffjtTarOptionsRebaseNames + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'U': + + if bytes.Equal(ffjKeyTarOptionsUIDMaps, kn) { + currentKey = ffjtTarOptionsUIDMaps + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'W': + + if bytes.Equal(ffjKeyTarOptionsWhiteoutFormat, kn) { + currentKey = ffjtTarOptionsWhiteoutFormat + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyTarOptionsWhiteoutData, kn) { + currentKey = ffjtTarOptionsWhiteoutData + state = fflib.FFParse_want_colon + goto mainparse + } + + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsInUserNS, kn) { + currentKey = ffjtTarOptionsInUserNS + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsRebaseNames, kn) { + currentKey = ffjtTarOptionsRebaseNames + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.SimpleLetterEqualFold(ffjKeyTarOptionsNoOverwriteDirNonDir, kn) { + currentKey = ffjtTarOptionsNoOverwriteDirNonDir + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.SimpleLetterEqualFold(ffjKeyTarOptionsWhiteoutData, kn) { + currentKey = ffjtTarOptionsWhiteoutData + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.SimpleLetterEqualFold(ffjKeyTarOptionsWhiteoutFormat, kn) { + currentKey = ffjtTarOptionsWhiteoutFormat + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsIncludeSourceDir, kn) { + currentKey = ffjtTarOptionsIncludeSourceDir + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsChownOpts, kn) { + currentKey = ffjtTarOptionsChownOpts + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsGIDMaps, kn) { + currentKey = ffjtTarOptionsGIDMaps + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsUIDMaps, kn) { + currentKey = ffjtTarOptionsUIDMaps + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.SimpleLetterEqualFold(ffjKeyTarOptionsNoLchown, kn) { + currentKey = ffjtTarOptionsNoLchown + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsCompression, kn) { + currentKey = ffjtTarOptionsCompression + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsExcludePatterns, kn) { + currentKey = ffjtTarOptionsExcludePatterns + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyTarOptionsIncludeFiles, kn) { + currentKey = ffjtTarOptionsIncludeFiles + state = fflib.FFParse_want_colon + goto mainparse + } + + currentKey = ffjtTarOptionsnosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } + + case fflib.FFParse_want_colon: + if tok != fflib.FFTok_colon { + wantedTok = fflib.FFTok_colon + goto wrongtokenerror + } + state = fflib.FFParse_want_value + continue + case fflib.FFParse_want_value: + + if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null { + switch currentKey { + + case ffjtTarOptionsIncludeFiles: + goto handle_IncludeFiles + + case ffjtTarOptionsExcludePatterns: + goto handle_ExcludePatterns + + case ffjtTarOptionsCompression: + goto handle_Compression + + case ffjtTarOptionsNoLchown: + goto handle_NoLchown + + case ffjtTarOptionsUIDMaps: + goto handle_UIDMaps + + case ffjtTarOptionsGIDMaps: + goto handle_GIDMaps + + case ffjtTarOptionsChownOpts: + goto handle_ChownOpts + + case ffjtTarOptionsIncludeSourceDir: + goto handle_IncludeSourceDir + + case ffjtTarOptionsWhiteoutFormat: + goto handle_WhiteoutFormat + + case ffjtTarOptionsWhiteoutData: + goto handle_WhiteoutData + + case ffjtTarOptionsNoOverwriteDirNonDir: + goto handle_NoOverwriteDirNonDir + + case ffjtTarOptionsRebaseNames: + goto handle_RebaseNames + + case ffjtTarOptionsInUserNS: + goto handle_InUserNS + + case ffjtTarOptionsnosuchkey: + err = fs.SkipField(tok) + if err != nil { + return fs.WrapErr(err) + } + state = fflib.FFParse_after_value + goto mainparse + } + } else { + goto wantedvalue + } + } + } + +handle_IncludeFiles: + + /* handler: j.IncludeFiles type=[]string 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.IncludeFiles = nil + } else { + + j.IncludeFiles = []string{} + + wantVal := true + + for { + + var tmpJIncludeFiles string + + 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: tmpJIncludeFiles type=string kind=string quoted=false*/ + + { + + { + if tok != fflib.FFTok_string && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) + } + } + + if tok == fflib.FFTok_null { + + } else { + + outBuf := fs.Output.Bytes() + + tmpJIncludeFiles = string(string(outBuf)) + + } + } + + j.IncludeFiles = append(j.IncludeFiles, tmpJIncludeFiles) + + wantVal = false + } + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_ExcludePatterns: + + /* handler: j.ExcludePatterns type=[]string 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.ExcludePatterns = nil + } else { + + j.ExcludePatterns = []string{} + + wantVal := true + + for { + + var tmpJExcludePatterns string + + 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: tmpJExcludePatterns type=string kind=string quoted=false*/ + + { + + { + if tok != fflib.FFTok_string && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) + } + } + + if tok == fflib.FFTok_null { + + } else { + + outBuf := fs.Output.Bytes() + + tmpJExcludePatterns = string(string(outBuf)) + + } + } + + j.ExcludePatterns = append(j.ExcludePatterns, tmpJExcludePatterns) + + wantVal = false + } + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_Compression: + + /* handler: j.Compression type=archive.Compression kind=int quoted=false*/ + + { + if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for Compression", tok)) + } + } + + { + + if tok == fflib.FFTok_null { + + } else { + + tval, err := fflib.ParseInt(fs.Output.Bytes(), 10, 64) + + if err != nil { + return fs.WrapErr(err) + } + + j.Compression = Compression(tval) + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_NoLchown: + + /* handler: j.NoLchown type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.NoLchown = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.NoLchown = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UIDMaps: + + /* handler: j.UIDMaps 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.UIDMaps = nil + } else { + + j.UIDMaps = []idtools.IDMap{} + + wantVal := true + + for { + + var tmpJUIDMaps 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: tmpJUIDMaps 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, &tmpJUIDMaps) + if err != nil { + return fs.WrapErr(err) + } + } + + j.UIDMaps = append(j.UIDMaps, tmpJUIDMaps) + + wantVal = false + } + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_GIDMaps: + + /* handler: j.GIDMaps 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.GIDMaps = nil + } else { + + j.GIDMaps = []idtools.IDMap{} + + wantVal := true + + for { + + var tmpJGIDMaps 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: tmpJGIDMaps 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, &tmpJGIDMaps) + if err != nil { + return fs.WrapErr(err) + } + } + + j.GIDMaps = append(j.GIDMaps, tmpJGIDMaps) + + wantVal = false + } + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_ChownOpts: + + /* handler: j.ChownOpts type=idtools.IDPair kind=struct quoted=false*/ + + { + /* Falling back. type=idtools.IDPair kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.ChownOpts) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_IncludeSourceDir: + + /* handler: j.IncludeSourceDir type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.IncludeSourceDir = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.IncludeSourceDir = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_WhiteoutFormat: + + /* handler: j.WhiteoutFormat type=archive.WhiteoutFormat kind=int quoted=false*/ + + { + if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for WhiteoutFormat", tok)) + } + } + + { + + if tok == fflib.FFTok_null { + + } else { + + tval, err := fflib.ParseInt(fs.Output.Bytes(), 10, 64) + + if err != nil { + return fs.WrapErr(err) + } + + j.WhiteoutFormat = WhiteoutFormat(tval) + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_WhiteoutData: + + /* handler: j.WhiteoutData type=interface {} kind=interface quoted=false*/ + + { + /* Falling back. type=interface {} kind=interface */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.WhiteoutData) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_NoOverwriteDirNonDir: + + /* handler: j.NoOverwriteDirNonDir type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.NoOverwriteDirNonDir = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.NoOverwriteDirNonDir = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_RebaseNames: + + /* handler: j.RebaseNames type=map[string]string kind=map quoted=false*/ + + { + + { + if tok != fflib.FFTok_left_bracket && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for ", tok)) + } + } + + if tok == fflib.FFTok_null { + j.RebaseNames = nil + } else { + + j.RebaseNames = make(map[string]string, 0) + + wantVal := true + + for { + + var k string + + var tmpJRebaseNames string + + tok = fs.Scan() + if tok == fflib.FFTok_error { + goto tokerror + } + if tok == fflib.FFTok_right_bracket { + 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: k type=string kind=string quoted=false*/ + + { + + { + if tok != fflib.FFTok_string && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) + } + } + + if tok == fflib.FFTok_null { + + } else { + + outBuf := fs.Output.Bytes() + + k = string(string(outBuf)) + + } + } + + // Expect ':' after key + tok = fs.Scan() + if tok != fflib.FFTok_colon { + return fs.WrapErr(fmt.Errorf("wanted colon token, but got token: %v", tok)) + } + + tok = fs.Scan() + /* handler: tmpJRebaseNames type=string kind=string quoted=false*/ + + { + + { + if tok != fflib.FFTok_string && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) + } + } + + if tok == fflib.FFTok_null { + + } else { + + outBuf := fs.Output.Bytes() + + tmpJRebaseNames = string(string(outBuf)) + + } + } + + j.RebaseNames[k] = tmpJRebaseNames + + wantVal = false + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_InUserNS: + + /* handler: j.InUserNS type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.InUserNS = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.InUserNS = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +wantedvalue: + return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) +wrongtokenerror: + return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) +tokerror: + if fs.BigError != nil { + return fs.WrapErr(fs.BigError) + } + err = fs.Error.ToError() + if err != nil { + return fs.WrapErr(err) + } + panic("ffjson-generated: unreachable, please report bug.") +done: + + return nil +} + +// MarshalJSON marshal bytes to json - template +func (j *TempArchive) MarshalJSON() ([]byte, error) { + var buf fflib.Buffer + if j == nil { + buf.WriteString("null") + return buf.Bytes(), nil + } + err := j.MarshalJSONBuf(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalJSONBuf marshal buff to json - template +func (j *TempArchive) MarshalJSONBuf(buf fflib.EncodingBuffer) error { + if j == nil { + buf.WriteString("null") + return nil + } + var err error + var obj []byte + _ = obj + _ = err + buf.WriteString(`{"Size":`) + fflib.FormatBits2(buf, uint64(j.Size), 10, j.Size < 0) + buf.WriteByte('}') + return nil +} + +const ( + ffjtTempArchivebase = iota + ffjtTempArchivenosuchkey + + ffjtTempArchiveSize +) + +var ffjKeyTempArchiveSize = []byte("Size") + +// UnmarshalJSON umarshall json - template of ffjson +func (j *TempArchive) UnmarshalJSON(input []byte) error { + fs := fflib.NewFFLexer(input) + return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) +} + +// UnmarshalJSONFFLexer fast json unmarshall - template ffjson +func (j *TempArchive) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { + var err error + currentKey := ffjtTempArchivebase + _ = currentKey + tok := fflib.FFTok_init + wantedTok := fflib.FFTok_init + +mainparse: + for { + tok = fs.Scan() + // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) + if tok == fflib.FFTok_error { + goto tokerror + } + + switch state { + + case fflib.FFParse_map_start: + if tok != fflib.FFTok_left_bracket { + wantedTok = fflib.FFTok_left_bracket + goto wrongtokenerror + } + state = fflib.FFParse_want_key + continue + + case fflib.FFParse_after_value: + if tok == fflib.FFTok_comma { + state = fflib.FFParse_want_key + } else if tok == fflib.FFTok_right_bracket { + goto done + } else { + wantedTok = fflib.FFTok_comma + goto wrongtokenerror + } + + case fflib.FFParse_want_key: + // json {} ended. goto exit. woo. + if tok == fflib.FFTok_right_bracket { + goto done + } + if tok != fflib.FFTok_string { + wantedTok = fflib.FFTok_string + goto wrongtokenerror + } + + kn := fs.Output.Bytes() + if len(kn) <= 0 { + // "" case. hrm. + currentKey = ffjtTempArchivenosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } else { + switch kn[0] { + + case 'S': + + if bytes.Equal(ffjKeyTempArchiveSize, kn) { + currentKey = ffjtTempArchiveSize + state = fflib.FFParse_want_colon + goto mainparse + } + + } + + if fflib.EqualFoldRight(ffjKeyTempArchiveSize, kn) { + currentKey = ffjtTempArchiveSize + state = fflib.FFParse_want_colon + goto mainparse + } + + currentKey = ffjtTempArchivenosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } + + case fflib.FFParse_want_colon: + if tok != fflib.FFTok_colon { + wantedTok = fflib.FFTok_colon + goto wrongtokenerror + } + state = fflib.FFParse_want_value + continue + case fflib.FFParse_want_value: + + if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null { + switch currentKey { + + case ffjtTempArchiveSize: + goto handle_Size + + case ffjtTempArchivenosuchkey: + err = fs.SkipField(tok) + if err != nil { + return fs.WrapErr(err) + } + state = fflib.FFParse_after_value + goto mainparse + } + } else { + goto wantedvalue + } + } + } + +handle_Size: + + /* handler: j.Size type=int64 kind=int64 quoted=false*/ + + { + if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for int64", tok)) + } + } + + { + + if tok == fflib.FFTok_null { + + } else { + + tval, err := fflib.ParseInt(fs.Output.Bytes(), 10, 64) + + if err != nil { + return fs.WrapErr(err) + } + + j.Size = int64(tval) + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +wantedvalue: + return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) +wrongtokenerror: + return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) +tokerror: + if fs.BigError != nil { + return fs.WrapErr(fs.BigError) + } + err = fs.Error.ToError() + if err != nil { + return fs.WrapErr(err) + } + panic("ffjson-generated: unreachable, please report bug.") +done: + + return nil +} + +// MarshalJSON marshal bytes to json - template +func (j *tarAppender) MarshalJSON() ([]byte, error) { + var buf fflib.Buffer + if j == nil { + buf.WriteString("null") + return buf.Bytes(), nil + } + err := j.MarshalJSONBuf(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalJSONBuf marshal buff to json - template +func (j *tarAppender) MarshalJSONBuf(buf fflib.EncodingBuffer) error { + if j == nil { + buf.WriteString("null") + return nil + } + var err error + var obj []byte + _ = obj + _ = err + if j.TarWriter != nil { + /* Struct fall back. type=tar.Writer kind=struct */ + buf.WriteString(`{"TarWriter":`) + err = buf.Encode(j.TarWriter) + if err != nil { + return err + } + } else { + buf.WriteString(`{"TarWriter":null`) + } + if j.Buffer != nil { + /* Struct fall back. type=bufio.Writer kind=struct */ + buf.WriteString(`,"Buffer":`) + err = buf.Encode(j.Buffer) + if err != nil { + return err + } + } else { + buf.WriteString(`,"Buffer":null`) + } + /* Falling back. type=map[uint64]string kind=map */ + buf.WriteString(`,"SeenFiles":`) + err = buf.Encode(j.SeenFiles) + if err != nil { + return err + } + if j.IDMappings != nil { + /* Struct fall back. type=idtools.IDMappings kind=struct */ + buf.WriteString(`,"IDMappings":`) + err = buf.Encode(j.IDMappings) + if err != nil { + return err + } + } else { + buf.WriteString(`,"IDMappings":null`) + } + if j.ChownOpts != nil { + /* Struct fall back. type=idtools.IDPair kind=struct */ + buf.WriteString(`,"ChownOpts":`) + err = buf.Encode(j.ChownOpts) + if err != nil { + return err + } + } else { + buf.WriteString(`,"ChownOpts":null`) + } + buf.WriteString(`,"WhiteoutConverter":`) + /* Interface types must use runtime reflection. type=archive.tarWhiteoutConverter kind=interface */ + err = buf.Encode(j.WhiteoutConverter) + if err != nil { + return err + } + buf.WriteByte('}') + return nil +} + +const ( + ffjttarAppenderbase = iota + ffjttarAppendernosuchkey + + ffjttarAppenderTarWriter + + ffjttarAppenderBuffer + + ffjttarAppenderSeenFiles + + ffjttarAppenderIDMappings + + ffjttarAppenderChownOpts + + ffjttarAppenderWhiteoutConverter +) + +var ffjKeytarAppenderTarWriter = []byte("TarWriter") + +var ffjKeytarAppenderBuffer = []byte("Buffer") + +var ffjKeytarAppenderSeenFiles = []byte("SeenFiles") + +var ffjKeytarAppenderIDMappings = []byte("IDMappings") + +var ffjKeytarAppenderChownOpts = []byte("ChownOpts") + +var ffjKeytarAppenderWhiteoutConverter = []byte("WhiteoutConverter") + +// UnmarshalJSON umarshall json - template of ffjson +func (j *tarAppender) UnmarshalJSON(input []byte) error { + fs := fflib.NewFFLexer(input) + return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) +} + +// UnmarshalJSONFFLexer fast json unmarshall - template ffjson +func (j *tarAppender) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { + var err error + currentKey := ffjttarAppenderbase + _ = currentKey + tok := fflib.FFTok_init + wantedTok := fflib.FFTok_init + +mainparse: + for { + tok = fs.Scan() + // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) + if tok == fflib.FFTok_error { + goto tokerror + } + + switch state { + + case fflib.FFParse_map_start: + if tok != fflib.FFTok_left_bracket { + wantedTok = fflib.FFTok_left_bracket + goto wrongtokenerror + } + state = fflib.FFParse_want_key + continue + + case fflib.FFParse_after_value: + if tok == fflib.FFTok_comma { + state = fflib.FFParse_want_key + } else if tok == fflib.FFTok_right_bracket { + goto done + } else { + wantedTok = fflib.FFTok_comma + goto wrongtokenerror + } + + case fflib.FFParse_want_key: + // json {} ended. goto exit. woo. + if tok == fflib.FFTok_right_bracket { + goto done + } + if tok != fflib.FFTok_string { + wantedTok = fflib.FFTok_string + goto wrongtokenerror + } + + kn := fs.Output.Bytes() + if len(kn) <= 0 { + // "" case. hrm. + currentKey = ffjttarAppendernosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } else { + switch kn[0] { + + case 'B': + + if bytes.Equal(ffjKeytarAppenderBuffer, kn) { + currentKey = ffjttarAppenderBuffer + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'C': + + if bytes.Equal(ffjKeytarAppenderChownOpts, kn) { + currentKey = ffjttarAppenderChownOpts + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'I': + + if bytes.Equal(ffjKeytarAppenderIDMappings, kn) { + currentKey = ffjttarAppenderIDMappings + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'S': + + if bytes.Equal(ffjKeytarAppenderSeenFiles, kn) { + currentKey = ffjttarAppenderSeenFiles + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'T': + + if bytes.Equal(ffjKeytarAppenderTarWriter, kn) { + currentKey = ffjttarAppenderTarWriter + state = fflib.FFParse_want_colon + goto mainparse + } + + case 'W': + + if bytes.Equal(ffjKeytarAppenderWhiteoutConverter, kn) { + currentKey = ffjttarAppenderWhiteoutConverter + state = fflib.FFParse_want_colon + goto mainparse + } + + } + + if fflib.SimpleLetterEqualFold(ffjKeytarAppenderWhiteoutConverter, kn) { + currentKey = ffjttarAppenderWhiteoutConverter + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeytarAppenderChownOpts, kn) { + currentKey = ffjttarAppenderChownOpts + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeytarAppenderIDMappings, kn) { + currentKey = ffjttarAppenderIDMappings + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeytarAppenderSeenFiles, kn) { + currentKey = ffjttarAppenderSeenFiles + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.SimpleLetterEqualFold(ffjKeytarAppenderBuffer, kn) { + currentKey = ffjttarAppenderBuffer + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.SimpleLetterEqualFold(ffjKeytarAppenderTarWriter, kn) { + currentKey = ffjttarAppenderTarWriter + state = fflib.FFParse_want_colon + goto mainparse + } + + currentKey = ffjttarAppendernosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } + + case fflib.FFParse_want_colon: + if tok != fflib.FFTok_colon { + wantedTok = fflib.FFTok_colon + goto wrongtokenerror + } + state = fflib.FFParse_want_value + continue + case fflib.FFParse_want_value: + + if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null { + switch currentKey { + + case ffjttarAppenderTarWriter: + goto handle_TarWriter + + case ffjttarAppenderBuffer: + goto handle_Buffer + + case ffjttarAppenderSeenFiles: + goto handle_SeenFiles + + case ffjttarAppenderIDMappings: + goto handle_IDMappings + + case ffjttarAppenderChownOpts: + goto handle_ChownOpts + + case ffjttarAppenderWhiteoutConverter: + goto handle_WhiteoutConverter + + case ffjttarAppendernosuchkey: + err = fs.SkipField(tok) + if err != nil { + return fs.WrapErr(err) + } + state = fflib.FFParse_after_value + goto mainparse + } + } else { + goto wantedvalue + } + } + } + +handle_TarWriter: + + /* handler: j.TarWriter type=tar.Writer kind=struct quoted=false*/ + + { + /* Falling back. type=tar.Writer kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.TarWriter) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_Buffer: + + /* handler: j.Buffer type=bufio.Writer kind=struct quoted=false*/ + + { + /* Falling back. type=bufio.Writer kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.Buffer) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_SeenFiles: + + /* handler: j.SeenFiles type=map[uint64]string kind=map quoted=false*/ + + { + + { + if tok != fflib.FFTok_left_bracket && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for ", tok)) + } + } + + if tok == fflib.FFTok_null { + j.SeenFiles = nil + } else { + + j.SeenFiles = make(map[uint64]string, 0) + + wantVal := true + + for { + + var k uint64 + + var tmpJSeenFiles string + + tok = fs.Scan() + if tok == fflib.FFTok_error { + goto tokerror + } + if tok == fflib.FFTok_right_bracket { + 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: k type=uint64 kind=uint64 quoted=false*/ + + { + if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for uint64", tok)) + } + } + + { + + if tok == fflib.FFTok_null { + + } else { + + tval, err := fflib.ParseUint(fs.Output.Bytes(), 10, 64) + + if err != nil { + return fs.WrapErr(err) + } + + k = uint64(tval) + + } + } + + // Expect ':' after key + tok = fs.Scan() + if tok != fflib.FFTok_colon { + return fs.WrapErr(fmt.Errorf("wanted colon token, but got token: %v", tok)) + } + + tok = fs.Scan() + /* handler: tmpJSeenFiles type=string kind=string quoted=false*/ + + { + + { + if tok != fflib.FFTok_string && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) + } + } + + if tok == fflib.FFTok_null { + + } else { + + outBuf := fs.Output.Bytes() + + tmpJSeenFiles = string(string(outBuf)) + + } + } + + j.SeenFiles[k] = tmpJSeenFiles + + wantVal = false + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_IDMappings: + + /* handler: j.IDMappings type=idtools.IDMappings kind=struct quoted=false*/ + + { + /* Falling back. type=idtools.IDMappings kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.IDMappings) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_ChownOpts: + + /* handler: j.ChownOpts type=idtools.IDPair kind=struct quoted=false*/ + + { + /* Falling back. type=idtools.IDPair kind=struct */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.ChownOpts) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_WhiteoutConverter: + + /* handler: j.WhiteoutConverter type=archive.tarWhiteoutConverter kind=interface quoted=false*/ + + { + /* Falling back. type=archive.tarWhiteoutConverter kind=interface */ + tbuf, err := fs.CaptureField(tok) + if err != nil { + return fs.WrapErr(err) + } + + err = json.Unmarshal(tbuf, &j.WhiteoutConverter) + if err != nil { + return fs.WrapErr(err) + } + } + + state = fflib.FFParse_after_value + goto mainparse + +wantedvalue: + return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) +wrongtokenerror: + return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) +tokerror: + if fs.BigError != nil { + return fs.WrapErr(fs.BigError) + } + err = fs.Error.ToError() + if err != nil { + return fs.WrapErr(err) + } + panic("ffjson-generated: unreachable, please report bug.") +done: + + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/archive/changes.go b/vendor/github.com/containers/storage/pkg/archive/changes.go index 8ab6b83f5..d3d6c8f74 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes.go @@ -257,6 +257,7 @@ func changes(layers []string, rw string, dc deleteChange, sc skipChange, wc whit // FileInfo describes the information of a file. type FileInfo struct { parent *FileInfo + idMappings *idtools.IDMappings name string stat *system.StatT 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 // breaks down is if some code intentionally hides a change by setting // back mtime - if statDifferent(oldStat, newStat) || + if statDifferent(oldStat, oldInfo, newStat, info) || !bytes.Equal(oldChild.capability, newChild.capability) { change := Change{ Path: newChild.path(), @@ -379,18 +380,19 @@ func (info *FileInfo) Changes(oldInfo *FileInfo) []Change { return changes } -func newRootFileInfo() *FileInfo { +func newRootFileInfo(idMappings *idtools.IDMappings) *FileInfo { // As this runs on the daemon side, file paths are OS specific. root := &FileInfo{ - name: string(os.PathSeparator), - children: make(map[string]*FileInfo), + name: string(os.PathSeparator), + idMappings: idMappings, + children: make(map[string]*FileInfo), } return root } // 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. -func ChangesDirs(newDir, oldDir string) ([]Change, error) { +func ChangesDirs(newDir string, newMappings *idtools.IDMappings, oldDir string, oldMappings *idtools.IDMappings) ([]Change, error) { var ( oldRoot, newRoot *FileInfo ) @@ -402,7 +404,7 @@ func ChangesDirs(newDir, oldDir string) ([]Change, error) { defer os.Remove(emptyDir) oldDir = emptyDir } - oldRoot, newRoot, err := collectFileInfoForChanges(oldDir, newDir) + oldRoot, newRoot, err := collectFileInfoForChanges(oldDir, newDir, oldMappings, newMappings) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/storage/pkg/archive/changes_linux.go b/vendor/github.com/containers/storage/pkg/archive/changes_linux.go index 8a82e857d..b12309361 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes_linux.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes_linux.go @@ -9,6 +9,7 @@ import ( "syscall" "unsafe" + "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/system" "golang.org/x/sys/unix" ) @@ -22,10 +23,12 @@ import ( // directly. Eliminating stat calls in this way can save up to seconds on large // images. type walker struct { - dir1 string - dir2 string - root1 *FileInfo - root2 *FileInfo + dir1 string + dir2 string + root1 *FileInfo + root2 *FileInfo + idmap1 *idtools.IDMappings + idmap2 *idtools.IDMappings } // 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 // to generating a list of changes between the two directories, as it does not // 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{ dir1: dir1, dir2: dir2, - root1: newRootFileInfo(), - root2: newRootFileInfo(), + root1: newRootFileInfo(idmap1), + root2: newRootFileInfo(idmap2), } i1, err := os.Lstat(w.dir1) @@ -69,9 +72,10 @@ func walkchunk(path string, fi os.FileInfo, dir string, root *FileInfo) error { return fmt.Errorf("walkchunk: Unexpectedly no parent for %s", path) } info := &FileInfo{ - name: filepath.Base(path), - children: make(map[string]*FileInfo), - parent: parent, + name: filepath.Base(path), + children: make(map[string]*FileInfo), + parent: parent, + idMappings: root.idMappings, } cpath := filepath.Join(dir, path) stat, err := system.FromStatT(fi.Sys().(*syscall.Stat_t)) diff --git a/vendor/github.com/containers/storage/pkg/archive/changes_other.go b/vendor/github.com/containers/storage/pkg/archive/changes_other.go index e1d1e7a91..bbbd8c9de 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes_other.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes_other.go @@ -9,21 +9,22 @@ import ( "runtime" "strings" + "github.com/containers/storage/pkg/idtools" "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 ( oldRoot, newRoot *FileInfo err1, err2 error errs = make(chan error, 2) ) go func() { - oldRoot, err1 = collectFileInfo(oldDir) + oldRoot, err1 = collectFileInfo(oldDir, oldIDMap) errs <- err1 }() go func() { - newRoot, err2 = collectFileInfo(newDir) + newRoot, err2 = collectFileInfo(newDir, newIDMap) errs <- err2 }() @@ -37,8 +38,8 @@ func collectFileInfoForChanges(oldDir, newDir string) (*FileInfo, *FileInfo, err return oldRoot, newRoot, nil } -func collectFileInfo(sourceDir string) (*FileInfo, error) { - root := newRootFileInfo() +func collectFileInfo(sourceDir string, idMappings *idtools.IDMappings) (*FileInfo, error) { + root := newRootFileInfo(idMappings) err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error { if err != nil { @@ -73,9 +74,10 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) { } info := &FileInfo{ - name: filepath.Base(relPath), - children: make(map[string]*FileInfo), - parent: parent, + name: filepath.Base(relPath), + children: make(map[string]*FileInfo), + parent: parent, + idMappings: idMappings, } s, err := system.Lstat(path) diff --git a/vendor/github.com/containers/storage/pkg/archive/changes_unix.go b/vendor/github.com/containers/storage/pkg/archive/changes_unix.go index d669c01b4..031ec341b 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes_unix.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes_unix.go @@ -6,15 +6,26 @@ import ( "os" "syscall" + "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/system" "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 + 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() || - oldStat.UID() != newStat.UID() || - oldStat.GID() != newStat.GID() || + ownerChanged || oldStat.Rdev() != newStat.Rdev() || // Don't look at size for dirs, its not a good measure of change (oldStat.Mode()&unix.S_IFDIR != unix.S_IFDIR && diff --git a/vendor/github.com/containers/storage/pkg/archive/changes_windows.go b/vendor/github.com/containers/storage/pkg/archive/changes_windows.go index 5ad3d7e38..966400e59 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes_windows.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes_windows.go @@ -6,7 +6,7 @@ import ( "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 if oldStat.Mtim() != newStat.Mtim() || diff --git a/vendor/github.com/containers/storage/pkg/archive/diff.go b/vendor/github.com/containers/storage/pkg/archive/diff.go index f93f4cb17..eb1f2b4c8 100644 --- a/vendor/github.com/containers/storage/pkg/archive/diff.go +++ b/vendor/github.com/containers/storage/pkg/archive/diff.go @@ -192,7 +192,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, srcData = tmpFile } - if err := remapIDs(idMappings, srcHdr); err != nil { + if err := remapIDs(nil, idMappings, options.ChownOpts, srcHdr); err != nil { return 0, err } diff --git a/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go b/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go index 2735f1400..b9fa228e6 100644 --- a/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go +++ b/vendor/github.com/containers/storage/pkg/chrootarchive/archive.go @@ -16,7 +16,18 @@ func NewArchiver(idMappings *idtools.IDMappings) *archive.Archiver { if idMappings == nil { 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, diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index c7de67ec5..407dda872 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strconv" "strings" "sync" "time" @@ -136,9 +137,8 @@ type StoreOptions struct { GraphDriverName string `json:"driver,omitempty"` // GraphDriverOptions are driver-specific options. GraphDriverOptions []string `json:"driver-options,omitempty"` - // UIDMap and GIDMap are used mainly for deciding on the ownership of - // files in layers as they're stored on disk, which is often necessary - // when user namespaces are being used. + // 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"` } @@ -152,6 +152,8 @@ type Store interface { GraphRoot() string GraphDriverName() string GraphOptions() []string + UIDMap() []idtools.IDMap + GIDMap() []idtools.IDMap // GraphDriver obtains and returns a handle to the graph Driver object used // by the Store. @@ -161,7 +163,7 @@ type Store interface { // optionally having the specified ID (one will be assigned if none is // specified), with the specified layer (or no layer) as its parent, // 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, // marking the layer for automatic removal if applying the diff fails @@ -174,7 +176,7 @@ type Store interface { // if reexec.Init { // 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 // (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. 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() ([]Layer, error) @@ -413,6 +420,11 @@ type Store interface { // directory. 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 // name or ID. Lookup(name string) (string, error) @@ -429,6 +441,33 @@ type Store interface { 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. type ImageOptions struct { // 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. 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 { @@ -558,6 +602,14 @@ func (s *store) GraphOptions() []string { 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 { driver, err := s.GraphDriver() if err != nil { @@ -662,7 +714,7 @@ func (s *store) LayerStore() (LayerStore, error) { if err := os.MkdirAll(glpath, 0700); err != nil { return nil, err } - rls, err := newLayerStore(rlpath, glpath, driver) + rls, err := newLayerStore(rlpath, glpath, driver, s.uidMap, s.gidMap) if err != nil { return nil, err } @@ -742,7 +794,8 @@ func (s *store) ContainerStore() (ContainerStore, error) { 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() if err != nil { return nil, -1, err @@ -768,9 +821,27 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w if id == "" { 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 != "" { var ilayer *Layer 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 { ilayer = l parent = ilayer.ID @@ -780,6 +851,7 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w if ilayer == nil { return nil, -1, ErrLayerUnknown } + parentLayer = ilayer containers, err := rcstore.Containers() if err != nil { return nil, -1, err @@ -789,12 +861,33 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w return nil, -1, ErrParentIsContainer } } + 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), + }, } - return rlstore.Put(id, parent, names, mountLabel, nil, writeable, nil, diff) + 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) { - layer, _, err := s.PutLayer(id, parent, names, mountLabel, writeable, nil) +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, options, nil) 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) { + if options == nil { + options = &ContainerOptions{} + } + if options.HostUIDMapping { + options.UIDMap = nil + } + if options.HostGIDMapping { + options.GIDMap = nil + } rlstore, err := s.LayerStore() if err != nil { return nil, err @@ -862,8 +964,10 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat id = stringid.GenerateRandomID() } - imageTopLayer := "" + var imageTopLayer *Layer imageID := "" + uidMap := options.UIDMap + gidMap := options.GIDMap if image != "" { istore, err := s.ImageStore() if err != nil { @@ -888,10 +992,53 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat if cimage == nil { return nil, ErrImageUnknown } - imageTopLayer = cimage.TopLayer imageID = cimage.ID + + lstores, err := s.ROLayerStores() + if err != nil { + return nil, err + } + 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, true) + clayer, err := rlstore.Create(layer, imageTopLayer, nil, "", nil, layerOptions, true) if err != nil { 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 { 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 { rlstore.Delete(layer) } @@ -1944,6 +2099,51 @@ func (s *store) LayerSize(id string) (int64, error) { 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) { var layers []Layer lstore, err := s.LayerStore() @@ -2399,6 +2599,18 @@ type OptionsConfig struct { // OverrideKernelCheck 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. @@ -2448,6 +2660,71 @@ func init() { if 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") != "" { DefaultStoreOptions.GraphDriverName = os.Getenv("STORAGE_DRIVER") } -- cgit v1.2.3-54-g00ecf