From 63e46cc85cb0a9523e9c48db7a88039e0baeac29 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Tue, 26 Nov 2019 16:08:04 +0100 Subject: Add support for image name history We leverage the containers/storage image history tracking feature to show the previously used image names when running: `podman images --history` Signed-off-by: Sascha Grunert --- vendor/github.com/containers/storage/VERSION | 2 +- .../containers/storage/drivers/copy/copy_linux.go | 16 ++- .../containers/storage/drivers/vfs/copy_linux.go | 2 +- vendor/github.com/containers/storage/images.go | 11 +++ .../github.com/containers/storage/images_ffjson.go | 108 +++++++++++++++++++++ .../containers/storage/pkg/archive/archive.go | 29 +++++- .../containers/storage/pkg/archive/changes.go | 5 +- .../storage/pkg/archive/changes_linux.go | 22 ++++- .../containers/storage/pkg/system/xattrs_linux.go | 41 +++++++- .../storage/pkg/system/xattrs_unsupported.go | 12 +++ vendor/github.com/containers/storage/utils.go | 12 +++ 11 files changed, 253 insertions(+), 7 deletions(-) (limited to 'vendor/github.com') 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 != "" { -- cgit v1.2.3-54-g00ecf