summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md2
-rw-r--r--cmd/podman/cliconfig/config.go1
-rw-r--r--cmd/podman/images.go10
-rw-r--r--cmd/podman/varlink/io.podman.varlink3
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/source/markdown/podman-images.1.md4
-rw-r--r--go.mod2
-rw-r--r--go.sum2
-rw-r--r--libpod/image/image.go15
-rw-r--r--pkg/adapter/runtime_remote.go67
-rw-r--r--pkg/varlinkapi/images.go2
-rw-r--r--test/system/010-images.bats15
-rw-r--r--vendor/github.com/containers/storage/VERSION2
-rw-r--r--vendor/github.com/containers/storage/drivers/copy/copy_linux.go16
-rw-r--r--vendor/github.com/containers/storage/drivers/vfs/copy_linux.go2
-rw-r--r--vendor/github.com/containers/storage/images.go11
-rw-r--r--vendor/github.com/containers/storage/images_ffjson.go108
-rw-r--r--vendor/github.com/containers/storage/pkg/archive/archive.go29
-rw-r--r--vendor/github.com/containers/storage/pkg/archive/changes.go5
-rw-r--r--vendor/github.com/containers/storage/pkg/archive/changes_linux.go22
-rw-r--r--vendor/github.com/containers/storage/pkg/system/xattrs_linux.go41
-rw-r--r--vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go12
-rw-r--r--vendor/github.com/containers/storage/utils.go12
-rw-r--r--vendor/modules.txt2
24 files changed, 346 insertions, 40 deletions
diff --git a/API.md b/API.md
index c288e6b28..a02a8cf6f 100755
--- a/API.md
+++ b/API.md
@@ -1698,6 +1698,8 @@ isParent [bool](https://godoc.org/builtin#bool)
topLayer [string](https://godoc.org/builtin#string)
readOnly [bool](https://godoc.org/builtin#bool)
+
+history [[]string](#[]string)
### <a name="ImageHistory"></a>type ImageHistory
ImageHistory describes the returned structure from ImageHistory.
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 27d94f769..a34afa827 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -64,6 +64,7 @@ type ImagesValues struct {
NoTrunc bool
Quiet bool
Sort string
+ History bool
}
type EventValues struct {
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index 7d498517c..6b16272f4 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -32,6 +32,7 @@ type imagesTemplateParams struct {
CreatedTime time.Time
Size string
ReadOnly bool
+ History string
}
type imagesJSONParams struct {
@@ -42,6 +43,7 @@ type imagesJSONParams struct {
Created time.Time `json:"created"`
Size *uint64 `json:"size"`
ReadOnly bool `json:"readonly"`
+ History []string `json:"history"`
}
type imagesOptions struct {
@@ -53,6 +55,7 @@ type imagesOptions struct {
outputformat string
sort string
all bool
+ history bool
}
// Type declaration and functions for sorting the images output
@@ -124,6 +127,7 @@ func imagesInit(command *cliconfig.ImagesValues) {
flags.BoolVar(&command.NoTrunc, "no-trunc", false, "Do not truncate output")
flags.BoolVarP(&command.Quiet, "quiet", "q", false, "Display only image IDs")
flags.StringVar(&command.Sort, "sort", "created", "Sort by created, id, repository, size, or tag")
+ flags.BoolVarP(&command.History, "history", "", false, "Display the image name history")
}
@@ -171,6 +175,7 @@ func imagesCmd(c *cliconfig.ImagesValues) error {
format: c.Format,
sort: c.Sort,
all: c.All,
+ history: c.History,
}
opts.outputformat = opts.setOutputFormat()
@@ -214,6 +219,9 @@ func (i imagesOptions) setOutputFormat() string {
format += "{{.Digest}}\t"
}
format += "{{.ID}}\t{{.Created}}\t{{.Size}}\t"
+ if i.history {
+ format += "{{if .History}}{{.History}}{{else}}<none>{{end}}\t"
+ }
return format
}
@@ -306,6 +314,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma
Created: units.HumanDuration(time.Since(createdTime)) + " ago",
Size: sizeStr,
ReadOnly: img.IsReadOnly(),
+ History: strings.Join(img.NamesHistory(), ", "),
}
imagesOutput = append(imagesOutput, params)
if opts.quiet { // Show only one image ID when quiet
@@ -336,6 +345,7 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage)
Created: img.Created(),
Size: size,
ReadOnly: img.IsReadOnly(),
+ History: img.NamesHistory(),
}
imagesOutput = append(imagesOutput, params)
}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 4f810dd53..e76b9627e 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -70,7 +70,8 @@ type Image (
labels: [string]string,
isParent: bool,
topLayer: string,
- readOnly: bool
+ readOnly: bool,
+ history: []string
)
# ImageHistory describes the returned structure from ImageHistory.
diff --git a/completions/bash/podman b/completions/bash/podman
index 7b64c2a80..6d145030f 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1563,6 +1563,7 @@ _podman_images() {
--filter
-h
--help
+ --history
--no-trunc
--notruncate
-n
diff --git a/docs/source/markdown/podman-images.1.md b/docs/source/markdown/podman-images.1.md
index 3ac07fc43..21fca1dbd 100644
--- a/docs/source/markdown/podman-images.1.md
+++ b/docs/source/markdown/podman-images.1.md
@@ -52,6 +52,10 @@ Filter output based on conditions provided
Change the default output format. This can be of a supported type like 'json'
or a Go template.
+**--history**
+
+Display the history of image names. If an image gets re-tagged or untagged, then the image name history gets prepended (latest image first). This is especially useful when undoing a tag operation or an image does not contain any name because it has been untagged.
+
**--noheading**, **-n**
Omit the table headings from the listing of images.
diff --git a/go.mod b/go.mod
index 5760f910b..83eebaa9c 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
github.com/containers/conmon v2.0.2+incompatible // indirect
github.com/containers/image/v5 v5.0.0
github.com/containers/psgo v1.3.2
- github.com/containers/storage v1.14.0
+ github.com/containers/storage v1.15.0
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b
diff --git a/go.sum b/go.sum
index 23091d495..142a23c29 100644
--- a/go.sum
+++ b/go.sum
@@ -90,6 +90,8 @@ github.com/containers/storage v1.13.5 h1:/SUzGeOP2HDijpF7Yur21Ch6WTZC1BNeZF917CW
github.com/containers/storage v1.13.5/go.mod h1:HELz8Sn+UVbPaUZMI8RvIG9doD4y4z6Gtg4k7xdd2ZY=
github.com/containers/storage v1.14.0 h1:LbX6WZaDmkXt4DT4xWIg3YXAWd6oA4K9Fi6/KG1xt84=
github.com/containers/storage v1.14.0/go.mod h1:qGPsti/qC1xxX+xcpHfiTMT+8ThVE2Jf83wFHHqkDAY=
+github.com/containers/storage v1.15.0 h1:QNW7jJ94ccGcAbFIOSMHUAsUxvHceb71ecLye9EDrkk=
+github.com/containers/storage v1.15.0/go.mod h1:qGPsti/qC1xxX+xcpHfiTMT+8ThVE2Jf83wFHHqkDAY=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
diff --git a/libpod/image/image.go b/libpod/image/image.go
index fa75be44d..129ccd376 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -335,6 +335,21 @@ func (i *Image) Names() []string {
return i.image.Names
}
+// NamesHistory returns a string array of names previously associated with the
+// image, which may be a mixture of tags and digests
+func (i *Image) NamesHistory() []string {
+ if len(i.image.Names) > 0 && len(i.image.NamesHistory) > 0 &&
+ // We compare the latest (time-referenced) tags for equality and skip
+ // it in the history if they match to not display them twice. We have
+ // to compare like this, because `i.image.Names` (latest last) gets
+ // appended on retag, whereas `i.image.NamesHistory` gets prepended
+ // (latest first)
+ i.image.Names[len(i.image.Names)-1] == i.image.NamesHistory[0] {
+ return i.image.NamesHistory[1:]
+ }
+ return i.image.NamesHistory
+}
+
// RepoTags returns a string array of repotags associated with the image
func (i *Image) RepoTags() ([]string, error) {
var repoTags []string
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index ddd4b5271..f9232897c 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -136,21 +136,22 @@ type ContainerImage struct {
}
type remoteImage struct {
- ID string
- Labels map[string]string
- RepoTags []string
- RepoDigests []string
- Parent string
- Size int64
- Created time.Time
- InputName string
- Names []string
- Digest digest.Digest
- Digests []digest.Digest
- isParent bool
- Runtime *LocalRuntime
- TopLayer string
- ReadOnly bool
+ ID string
+ Labels map[string]string
+ RepoTags []string
+ RepoDigests []string
+ Parent string
+ Size int64
+ Created time.Time
+ InputName string
+ Names []string
+ Digest digest.Digest
+ Digests []digest.Digest
+ isParent bool
+ Runtime *LocalRuntime
+ TopLayer string
+ ReadOnly bool
+ NamesHistory []string
}
// Container ...
@@ -232,21 +233,22 @@ func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRu
digests = append(digests, digest.Digest(d))
}
ri := remoteImage{
- InputName: name,
- ID: i.Id,
- Digest: digest.Digest(i.Digest),
- Digests: digests,
- Labels: i.Labels,
- RepoTags: i.RepoTags,
- RepoDigests: i.RepoTags,
- Parent: i.ParentId,
- Size: i.Size,
- Created: created,
- Names: i.RepoTags,
- isParent: i.IsParent,
- Runtime: runtime,
- TopLayer: i.TopLayer,
- ReadOnly: i.ReadOnly,
+ InputName: name,
+ ID: i.Id,
+ Digest: digest.Digest(i.Digest),
+ Digests: digests,
+ Labels: i.Labels,
+ RepoTags: i.RepoTags,
+ RepoDigests: i.RepoTags,
+ Parent: i.ParentId,
+ Size: i.Size,
+ Created: created,
+ Names: i.RepoTags,
+ isParent: i.IsParent,
+ Runtime: runtime,
+ TopLayer: i.TopLayer,
+ ReadOnly: i.ReadOnly,
+ NamesHistory: i.History,
}
return &ContainerImage{ri}, nil
}
@@ -337,6 +339,11 @@ func (ci *ContainerImage) Names() []string {
return ci.remoteImage.Names
}
+// NamesHistory returns a string array of names previously associated with the image
+func (ci *ContainerImage) NamesHistory() []string {
+ return ci.remoteImage.NamesHistory
+}
+
// Created returns the time the image was created
func (ci *ContainerImage) Created() time.Time {
return ci.remoteImage.Created
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index c27088805..7abffa42a 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -70,6 +70,7 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
Labels: labels,
IsParent: isParent,
ReadOnly: image.IsReadOnly(),
+ History: image.NamesHistory(),
}
imageList = append(imageList, i)
}
@@ -111,6 +112,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error {
Labels: labels,
TopLayer: newImage.TopLayer(),
ReadOnly: newImage.IsReadOnly(),
+ History: newImage.NamesHistory(),
}
return call.ReplyGetImage(il)
}
diff --git a/test/system/010-images.bats b/test/system/010-images.bats
index 380623078..543876509 100644
--- a/test/system/010-images.bats
+++ b/test/system/010-images.bats
@@ -44,4 +44,19 @@ size | [0-9]\\\+
}
+@test "podman images - history output" {
+ run_podman images --format json
+ actual=$(echo $output | jq -r '.[0].history | length')
+ is "$actual" "0"
+
+ run_podman tag $PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_TEST_IMAGE_TAG test-image
+ run_podman images --format json
+ actual=$(echo $output | jq -r '.[1].history | length')
+ is "$actual" "0"
+ actual=$(echo $output | jq -r '.[0].history | length')
+ is "$actual" "1"
+ actual=$(echo $output | jq -r '.[0].history[0]')
+ is "$actual" "$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_TEST_IMAGE_TAG"
+}
+
# vim: filetype=sh
diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION
index 850e74240..141f2e805 100644
--- a/vendor/github.com/containers/storage/VERSION
+++ b/vendor/github.com/containers/storage/VERSION
@@ -1 +1 @@
-1.14.0
+1.15.0
diff --git a/vendor/github.com/containers/storage/drivers/copy/copy_linux.go b/vendor/github.com/containers/storage/drivers/copy/copy_linux.go
index e52545d5b..c00b9e47d 100644
--- a/vendor/github.com/containers/storage/drivers/copy/copy_linux.go
+++ b/vendor/github.com/containers/storage/drivers/copy/copy_linux.go
@@ -16,6 +16,7 @@ import (
"io"
"os"
"path/filepath"
+ "strings"
"syscall"
"time"
@@ -97,7 +98,7 @@ func legacyCopy(srcFile io.Reader, dstFile io.Writer) error {
func copyXattr(srcPath, dstPath, attr string) error {
data, err := system.Lgetxattr(srcPath, attr)
- if err != nil {
+ if err != nil && err != unix.EOPNOTSUPP {
return err
}
if data != nil {
@@ -271,6 +272,19 @@ func doCopyXattrs(srcPath, dstPath string) error {
return err
}
+ xattrs, err := system.Llistxattr(srcPath)
+ if err != nil && err != unix.EOPNOTSUPP {
+ return err
+ }
+
+ for _, key := range xattrs {
+ if strings.HasPrefix(key, "user.") {
+ if err := copyXattr(srcPath, dstPath, key); err != nil {
+ return err
+ }
+ }
+ }
+
// We need to copy this attribute if it appears in an overlay upper layer, as
// this function is used to copy those. It is set by overlay if a directory
// is removed and then re-created and should not inherit anything from the
diff --git a/vendor/github.com/containers/storage/drivers/vfs/copy_linux.go b/vendor/github.com/containers/storage/drivers/vfs/copy_linux.go
index 8137fcf67..bf22a5f6f 100644
--- a/vendor/github.com/containers/storage/drivers/vfs/copy_linux.go
+++ b/vendor/github.com/containers/storage/drivers/vfs/copy_linux.go
@@ -3,5 +3,5 @@ package vfs
import "github.com/containers/storage/drivers/copy"
func dirCopy(srcDir, dstDir string) error {
- return copy.DirCopy(srcDir, dstDir, copy.Content, false)
+ return copy.DirCopy(srcDir, dstDir, copy.Content, true)
}
diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go
index 5d6a2e48d..6373ebb41 100644
--- a/vendor/github.com/containers/storage/images.go
+++ b/vendor/github.com/containers/storage/images.go
@@ -47,6 +47,11 @@ type Image struct {
// or canonical references.
Names []string `json:"names,omitempty"`
+ // NamesHistory is an optional set of Names the image had in the past. The
+ // contained names are free from any duplicates, whereas the newest entry
+ // is the first one.
+ NamesHistory []string `json:"names-history,omitempty"`
+
// TopLayer is the ID of the topmost layer of the image itself, if the
// image contains one or more layers. Multiple images can refer to the
// same top layer.
@@ -155,6 +160,7 @@ func copyImage(i *Image) *Image {
Digest: i.Digest,
Digests: copyDigestSlice(i.Digests),
Names: copyStringSlice(i.Names),
+ NamesHistory: copyStringSlice(i.NamesHistory),
TopLayer: i.TopLayer,
MappedTopLayers: copyStringSlice(i.MappedTopLayers),
Metadata: i.Metadata,
@@ -481,6 +487,10 @@ func (r *imageStore) removeName(image *Image, name string) {
image.Names = stringSliceWithoutValue(image.Names, name)
}
+func (i *Image) addNameToHistory(name string) {
+ i.NamesHistory = dedupeNames(append([]string{name}, i.NamesHistory...))
+}
+
func (r *imageStore) SetNames(id string, names []string) error {
if !r.IsReadWrite() {
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change image name assignments at %q", r.imagespath())
@@ -495,6 +505,7 @@ func (r *imageStore) SetNames(id string, names []string) error {
r.removeName(otherImage, name)
}
r.byname[name] = image
+ image.addNameToHistory(name)
}
image.Names = names
return r.Save()
diff --git a/vendor/github.com/containers/storage/images_ffjson.go b/vendor/github.com/containers/storage/images_ffjson.go
index 539acfe93..0dde97c18 100644
--- a/vendor/github.com/containers/storage/images_ffjson.go
+++ b/vendor/github.com/containers/storage/images_ffjson.go
@@ -59,6 +59,22 @@ func (j *Image) MarshalJSONBuf(buf fflib.EncodingBuffer) error {
}
buf.WriteByte(',')
}
+ if len(j.NamesHistory) != 0 {
+ buf.WriteString(`"names-history":`)
+ if j.NamesHistory != nil {
+ buf.WriteString(`[`)
+ for i, v := range j.NamesHistory {
+ if i != 0 {
+ buf.WriteString(`,`)
+ }
+ fflib.WriteJsonString(buf, string(v))
+ }
+ buf.WriteString(`]`)
+ } else {
+ buf.WriteString(`null`)
+ }
+ buf.WriteByte(',')
+ }
if len(j.TopLayer) != 0 {
buf.WriteString(`"layer":`)
fflib.WriteJsonString(buf, string(j.TopLayer))
@@ -171,6 +187,8 @@ const (
ffjtImageNames
+ ffjtImageNamesHistory
+
ffjtImageTopLayer
ffjtImageMappedTopLayers
@@ -194,6 +212,8 @@ var ffjKeyImageDigest = []byte("digest")
var ffjKeyImageNames = []byte("names")
+var ffjKeyImageNamesHistory = []byte("names-history")
+
var ffjKeyImageTopLayer = []byte("layer")
var ffjKeyImageMappedTopLayers = []byte("mapped-layers")
@@ -348,6 +368,11 @@ mainparse:
currentKey = ffjtImageNames
state = fflib.FFParse_want_colon
goto mainparse
+
+ } else if bytes.Equal(ffjKeyImageNamesHistory, kn) {
+ currentKey = ffjtImageNamesHistory
+ state = fflib.FFParse_want_colon
+ goto mainparse
}
}
@@ -400,6 +425,12 @@ mainparse:
goto mainparse
}
+ if fflib.EqualFoldRight(ffjKeyImageNamesHistory, kn) {
+ currentKey = ffjtImageNamesHistory
+ state = fflib.FFParse_want_colon
+ goto mainparse
+ }
+
if fflib.EqualFoldRight(ffjKeyImageNames, kn) {
currentKey = ffjtImageNames
state = fflib.FFParse_want_colon
@@ -444,6 +475,9 @@ mainparse:
case ffjtImageNames:
goto handle_Names
+ case ffjtImageNamesHistory:
+ goto handle_NamesHistory
+
case ffjtImageTopLayer:
goto handle_TopLayer
@@ -608,6 +642,80 @@ handle_Names:
state = fflib.FFParse_after_value
goto mainparse
+handle_NamesHistory:
+
+ /* handler: j.NamesHistory 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.NamesHistory = nil
+ } else {
+
+ j.NamesHistory = []string{}
+
+ wantVal := true
+
+ for {
+
+ var tmpJNamesHistory 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: tmpJNamesHistory 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()
+
+ tmpJNamesHistory = string(string(outBuf))
+
+ }
+ }
+
+ j.NamesHistory = append(j.NamesHistory, tmpJNamesHistory)
+
+ wantVal = false
+ }
+ }
+ }
+
+ state = fflib.FFParse_after_value
+ goto mainparse
+
handle_TopLayer:
/* handler: j.TopLayer type=string kind=string quoted=false*/
diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go
index ba635ae91..d2752ae7c 100644
--- a/vendor/github.com/containers/storage/pkg/archive/archive.go
+++ b/vendor/github.com/containers/storage/pkg/archive/archive.go
@@ -387,7 +387,10 @@ func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 {
// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
// to a tar header
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
- capability, _ := system.Lgetxattr(path, "security.capability")
+ capability, err := system.Lgetxattr(path, "security.capability")
+ if err != nil && err != system.EOPNOTSUPP {
+ return err
+ }
if capability != nil {
hdr.Xattrs = make(map[string]string)
hdr.Xattrs["security.capability"] = string(capability)
@@ -395,6 +398,27 @@ func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
return nil
}
+// ReadUserXattrToTarHeader reads user.* xattr from filesystem to a tar header
+func ReadUserXattrToTarHeader(path string, hdr *tar.Header) error {
+ xattrs, err := system.Llistxattr(path)
+ if err != nil && err != system.EOPNOTSUPP {
+ return err
+ }
+ for _, key := range xattrs {
+ if strings.HasPrefix(key, "user.") {
+ value, err := system.Lgetxattr(path, key)
+ if err != nil {
+ return err
+ }
+ if hdr.Xattrs == nil {
+ hdr.Xattrs = make(map[string]string)
+ }
+ hdr.Xattrs[key] = string(value)
+ }
+ }
+ return nil
+}
+
type tarWhiteoutConverter interface {
ConvertWrite(*tar.Header, string, os.FileInfo) (*tar.Header, error)
ConvertRead(*tar.Header, string) (bool, error)
@@ -469,6 +493,9 @@ func (ta *tarAppender) addTarFile(path, name string) error {
if err := ReadSecurityXattrToTarHeader(path, hdr); err != nil {
return err
}
+ if err := ReadUserXattrToTarHeader(path, hdr); err != nil {
+ return err
+ }
if ta.CopyPass {
copyPassHeader(hdr)
}
diff --git a/vendor/github.com/containers/storage/pkg/archive/changes.go b/vendor/github.com/containers/storage/pkg/archive/changes.go
index d3d6c8f74..3ce396070 100644
--- a/vendor/github.com/containers/storage/pkg/archive/changes.go
+++ b/vendor/github.com/containers/storage/pkg/archive/changes.go
@@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"sort"
"strings"
"syscall"
@@ -263,6 +264,7 @@ type FileInfo struct {
children map[string]*FileInfo
capability []byte
added bool
+ xattrs map[string]string
}
// LookUp looks up the file information of a file.
@@ -331,7 +333,8 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
// breaks down is if some code intentionally hides a change by setting
// back mtime
if statDifferent(oldStat, oldInfo, newStat, info) ||
- !bytes.Equal(oldChild.capability, newChild.capability) {
+ !bytes.Equal(oldChild.capability, newChild.capability) ||
+ !reflect.DeepEqual(oldChild.xattrs, newChild.xattrs) {
change := Change{
Path: newChild.path(),
Kind: ChangeModify,
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 dc313d1ab..ceec53ada 100644
--- a/vendor/github.com/containers/storage/pkg/archive/changes_linux.go
+++ b/vendor/github.com/containers/storage/pkg/archive/changes_linux.go
@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"sort"
+ "strings"
"syscall"
"unsafe"
@@ -83,7 +84,26 @@ func walkchunk(path string, fi os.FileInfo, dir string, root *FileInfo) error {
return err
}
info.stat = stat
- info.capability, _ = system.Lgetxattr(cpath, "security.capability") // lgetxattr(2): fs access
+ info.capability, err = system.Lgetxattr(cpath, "security.capability") // lgetxattr(2): fs access
+ if err != nil && err != system.EOPNOTSUPP {
+ return err
+ }
+ xattrs, err := system.Llistxattr(cpath)
+ if err != nil && err != system.EOPNOTSUPP {
+ return err
+ }
+ for _, key := range xattrs {
+ if strings.HasPrefix(key, "user.") {
+ value, err := system.Lgetxattr(cpath, key)
+ if err != nil {
+ return err
+ }
+ if info.xattrs == nil {
+ info.xattrs = make(map[string]string)
+ }
+ info.xattrs[key] = string(value)
+ }
+ }
parent.children[info.name] = info
return nil
}
diff --git a/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go b/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go
index 98b111be4..0d6cd95e3 100644
--- a/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go
+++ b/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go
@@ -1,6 +1,16 @@
package system
-import "golang.org/x/sys/unix"
+import (
+ "bytes"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+const (
+ // Operation not supported
+ EOPNOTSUPP syscall.Errno = unix.EOPNOTSUPP
+)
// Lgetxattr retrieves the value of the extended attribute identified by attr
// and associated with the given path in the file system.
@@ -27,3 +37,32 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
func Lsetxattr(path string, attr string, data []byte, flags int) error {
return unix.Lsetxattr(path, attr, data, flags)
}
+
+// Llistxattr lists extended attributes associated with the given path
+// in the file system.
+func Llistxattr(path string) ([]string, error) {
+ var dest []byte
+
+ for {
+ sz, err := unix.Llistxattr(path, dest)
+ if err != nil {
+ return nil, err
+ }
+
+ if sz > len(dest) {
+ dest = make([]byte, sz)
+ } else {
+ dest = dest[:sz]
+ break
+ }
+ }
+
+ var attrs []string
+ for _, token := range bytes.Split(dest, []byte{0}) {
+ if len(token) > 0 {
+ attrs = append(attrs, string(token))
+ }
+ }
+
+ return attrs, nil
+}
diff --git a/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go b/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go
index 0114f2227..b4cf4e6ca 100644
--- a/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go
+++ b/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go
@@ -2,6 +2,13 @@
package system
+import "syscall"
+
+const (
+ // Operation not supported
+ EOPNOTSUPP syscall.Errno = syscall.Errno(0)
+)
+
// Lgetxattr is not supported on platforms other than linux.
func Lgetxattr(path string, attr string) ([]byte, error) {
return nil, ErrNotSupportedPlatform
@@ -11,3 +18,8 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
func Lsetxattr(path string, attr string, data []byte, flags int) error {
return ErrNotSupportedPlatform
}
+
+// Llistxattr is not supported on platforms other than linux.
+func Llistxattr(path string) ([]string, error) {
+ return nil, ErrNotSupportedPlatform
+}
diff --git a/vendor/github.com/containers/storage/utils.go b/vendor/github.com/containers/storage/utils.go
index 54627731a..5aa7c0851 100644
--- a/vendor/github.com/containers/storage/utils.go
+++ b/vendor/github.com/containers/storage/utils.go
@@ -70,6 +70,18 @@ func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap stri
// GetRootlessRuntimeDir returns the runtime directory when running as non root
func GetRootlessRuntimeDir(rootlessUid int) (string, error) {
+ path, err := getRootlessRuntimeDir(rootlessUid)
+ if err != nil {
+ return "", err
+ }
+ path = filepath.Join(path, "containers")
+ if err := os.MkdirAll(path, 0700); err != nil {
+ return "", errors.Wrapf(err, "unable to make rootless runtime dir %s", path)
+ }
+ return path, nil
+}
+
+func getRootlessRuntimeDir(rootlessUid int) (string, error) {
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
if runtimeDir != "" {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 8837e7efb..4fb9b9d44 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -125,7 +125,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
-# github.com/containers/storage v1.14.0
+# github.com/containers/storage v1.15.0
github.com/containers/storage
github.com/containers/storage/drivers
github.com/containers/storage/drivers/aufs