diff --git a/go.mod b/go.mod index e3b39555df..e55ca64c7e 100644 --- a/go.mod +++ b/go.mod @@ -13,13 +13,13 @@ require ( github.com/containernetworking/cni v1.1.2 github.com/containernetworking/plugins v1.2.0 github.com/containers/buildah v1.30.1-0.20230504052500-e925b5852e07 - github.com/containers/common v0.53.1-0.20230506101404-3e93a76d461c + github.com/containers/common v0.53.1-0.20230508125333-8d4783167b58 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.25.1-0.20230505072505-dc4a4be9cc1e github.com/containers/libhvee v0.0.5 github.com/containers/ocicrypt v1.1.7 github.com/containers/psgo v1.8.0 - github.com/containers/storage v1.46.1 + github.com/containers/storage v1.46.2-0.20230508110141-51c23d59f8f3 github.com/coreos/go-systemd/v22 v22.5.0 github.com/coreos/stream-metadata-go v0.4.1 github.com/crc-org/vfkit v0.0.5-0.20230427143911-8117c28876bc diff --git a/go.sum b/go.sum index 2fa88a31cb..3de4ca4019 100644 --- a/go.sum +++ b/go.sum @@ -240,8 +240,8 @@ github.com/containernetworking/plugins v1.2.0 h1:SWgg3dQG1yzUo4d9iD8cwSVh1VqI+bP github.com/containernetworking/plugins v1.2.0/go.mod h1:/VjX4uHecW5vVimFa1wkG4s+r/s9qIfPdqlLF4TW8c4= github.com/containers/buildah v1.30.1-0.20230504052500-e925b5852e07 h1:Bs2sNFh/fSYr4J6JJLFqzyn3dp6HhlA6ewFwRYUpeIE= github.com/containers/buildah v1.30.1-0.20230504052500-e925b5852e07/go.mod h1:6A/BK0YJLXL8+AqlbceKJrhUT+NtEgsvAc51F7TAllc= -github.com/containers/common v0.53.1-0.20230506101404-3e93a76d461c h1:NPf//8NAa6xjlj62eBbEBabu8LWVqxPRuweNAFCAYxs= -github.com/containers/common v0.53.1-0.20230506101404-3e93a76d461c/go.mod h1:vAG2WNLK9d4umy56l413SS9xiJVe5m7LwOUSoi1x10k= +github.com/containers/common v0.53.1-0.20230508125333-8d4783167b58 h1:rVhhv/8UNicVdYGQU7ikwA348V0zLlTnWQjj15BCAUI= +github.com/containers/common v0.53.1-0.20230508125333-8d4783167b58/go.mod h1:uJt+BpKWLomkmP12rHxKvVGw2LwSUdpXkd9wGD0ukhI= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.25.1-0.20230505072505-dc4a4be9cc1e h1:9rH8hFLJjmwMkNAdFfXP3O6cAODKujsTn8JurumYz6I= @@ -258,8 +258,8 @@ github.com/containers/ocicrypt v1.1.7/go.mod h1:7CAhjcj2H8AYp5YvEie7oVSK2AhBY8Ns github.com/containers/psgo v1.8.0 h1:2loGekmGAxM9ir5OsXWEfGwFxorMPYnc6gEDsGFQvhY= github.com/containers/psgo v1.8.0/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc= github.com/containers/storage v1.43.0/go.mod h1:uZ147thiIFGdVTjMmIw19knttQnUCl3y9zjreHrg11s= -github.com/containers/storage v1.46.1 h1:GcAe8J0Y6T2CF70fXPojUpnme6zXamuzGCrNujVtIGE= -github.com/containers/storage v1.46.1/go.mod h1:81vNDX4h+nXJ2o0D6Yqy6JGXDYJGVpHZpz0nr09iJuQ= +github.com/containers/storage v1.46.2-0.20230508110141-51c23d59f8f3 h1:xEKpYtKcKezQICn+BGBbGa3HnA46z+tzGv8ZuxtQOTk= +github.com/containers/storage v1.46.2-0.20230508110141-51c23d59f8f3/go.mod h1:sbKXsEKeGkAk9J0PSicUadeA7J+49rmPODDuUVEqAcM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= diff --git a/vendor/github.com/containers/storage/.cirrus.yml b/vendor/github.com/containers/storage/.cirrus.yml index 4e3ba73170..8efa1f17af 100644 --- a/vendor/github.com/containers/storage/.cirrus.yml +++ b/vendor/github.com/containers/storage/.cirrus.yml @@ -17,13 +17,13 @@ env: #### #### Cache-image names to test with (double-quotes around names are critical) ### - FEDORA_NAME: "fedora-37" + FEDORA_NAME: "fedora-38" DEBIAN_NAME: "debian-12" # GCE project where images live IMAGE_PROJECT: "libpod-218412" # VM Image built in containers/automation_images - IMAGE_SUFFIX: "c20230405t152256z-f37f36d12" + IMAGE_SUFFIX: "c20230426t140447z-f38f37d12" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}" @@ -127,7 +127,6 @@ lint_task: fingerprint_script: cat go.sum folder: $GOPATH/pkg/mod build_script: | - echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/backports.list apt-get update apt-get install -y libbtrfs-dev libdevmapper-dev test_script: make TAGS=regex_precompile local-validate && make lint && make clean @@ -168,7 +167,7 @@ vendor_task: cross_task: container: - image: golang:1.17 + image: golang:1.18 build_script: make cross @@ -182,6 +181,6 @@ success_task: - vendor - cross container: - image: golang:1.17 + image: golang:1.18 clone_script: 'mkdir -p "$CIRRUS_WORKING_DIR"' # Source code not needed script: /bin/true diff --git a/vendor/github.com/containers/storage/Makefile b/vendor/github.com/containers/storage/Makefile index 52266da0f9..42b3aceb4f 100644 --- a/vendor/github.com/containers/storage/Makefile +++ b/vendor/github.com/containers/storage/Makefile @@ -66,7 +66,7 @@ docs: install.tools ## build the docs on the host local-test: local-binary local-test-unit local-test-integration ## build the binaries and run the tests local-test-unit test-unit: local-binary ## run the unit tests on the host (requires\nsuperuser privileges) - @$(GO) test $(MOD_VENDOR) $(BUILDFLAGS) $(TESTFLAGS) $(shell $(GO) list ./... | grep -v ^$(PACKAGE)/vendor) + @$(GO) test -count 1 $(MOD_VENDOR) $(BUILDFLAGS) $(TESTFLAGS) $(shell $(GO) list ./... | grep -v ^$(PACKAGE)/vendor) local-test-integration test-integration: local-binary ## run the integration tests on the host (requires\nsuperuser privileges) @cd tests; ./test_runner.bash diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index aa3ed3a5e0..9dccaf2264 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.46.1 +1.47.0-dev diff --git a/vendor/github.com/containers/storage/check.go b/vendor/github.com/containers/storage/check.go new file mode 100644 index 0000000000..81b5c3ab81 --- /dev/null +++ b/vendor/github.com/containers/storage/check.go @@ -0,0 +1,1086 @@ +package storage + +import ( + "archive/tar" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "sort" + "strings" + "sync" + "time" + + drivers "github.com/containers/storage/drivers" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/types" + "github.com/sirupsen/logrus" +) + +var ( + // ErrLayerUnaccounted describes a layer that is present in the lower-level storage driver, + // but which is not known to or managed by the higher-level driver-agnostic logic. + ErrLayerUnaccounted = types.ErrLayerUnaccounted + // ErrLayerUnreferenced describes a layer which is not used by any image or container. + ErrLayerUnreferenced = types.ErrLayerUnreferenced + // ErrLayerIncorrectContentDigest describes a layer for which the contents of one or more + // files which were added in the layer appear to have changed. It may instead look like an + // unnamed "file integrity checksum failed" error. + ErrLayerIncorrectContentDigest = types.ErrLayerIncorrectContentDigest + // ErrLayerIncorrectContentSize describes a layer for which regenerating the diff that was + // used to populate the layer produced a diff of a different size. We check the digest + // first, so it's highly unlikely you'll ever see this error. + ErrLayerIncorrectContentSize = types.ErrLayerIncorrectContentSize + // ErrLayerContentModified describes a layer which contains contents which should not be + // there, or for which ownership/permissions/dates have been changed. + ErrLayerContentModified = types.ErrLayerContentModified + // ErrLayerDataMissing describes a layer which is missing a big data item. + ErrLayerDataMissing = types.ErrLayerDataMissing + // ErrLayerMissing describes a layer which is the missing parent of a layer. + ErrLayerMissing = types.ErrLayerMissing + // ErrImageLayerMissing describes an image which claims to have a layer that we don't know + // about. + ErrImageLayerMissing = types.ErrImageLayerMissing + // ErrImageDataMissing describes an image which is missing a big data item. + ErrImageDataMissing = types.ErrImageDataMissing + // ErrImageDataIncorrectSize describes an image which has a big data item which looks like + // its size has changed, likely because it's been modified somehow. + ErrImageDataIncorrectSize = types.ErrImageDataIncorrectSize + // ErrContainerImageMissing describes a container which claims to be based on an image that + // we don't know about. + ErrContainerImageMissing = types.ErrContainerImageMissing + // ErrContainerDataMissing describes a container which is missing a big data item. + ErrContainerDataMissing = types.ErrContainerDataMissing + // ErrContainerDataIncorrectSize describes a container which has a big data item which looks + // like its size has changed, likely because it's been modified somehow. + ErrContainerDataIncorrectSize = types.ErrContainerDataIncorrectSize +) + +const ( + defaultMaximumUnreferencedLayerAge = 24 * time.Hour +) + +// CheckOptions is the set of options for Check(), specifying which tests to perform. +type CheckOptions struct { + LayerUnreferencedMaximumAge *time.Duration // maximum allowed age of unreferenced layers + LayerDigests bool // check that contents of image layer diffs can still be reconstructed + LayerMountable bool // check that layers are mountable + LayerContents bool // check that contents of image layers match their diffs, with no unexpected changes, requires LayerMountable + LayerData bool // check that associated "big" data items are present and can be read + ImageData bool // check that associated "big" data items are present, can be read, and match the recorded size + ContainerData bool // check that associated "big" data items are present and can be read +} + +// CheckMost returns a CheckOptions with mostly just "quick" checks enabled. +func CheckMost() *CheckOptions { + return &CheckOptions{ + LayerDigests: true, + LayerMountable: true, + LayerContents: false, + LayerData: true, + ImageData: true, + ContainerData: true, + } +} + +// CheckEverything returns a CheckOptions with every check enabled. +func CheckEverything() *CheckOptions { + return &CheckOptions{ + LayerDigests: true, + LayerMountable: true, + LayerContents: true, + LayerData: true, + ImageData: true, + ContainerData: true, + } +} + +// CheckReport is a list of detected problems. +type CheckReport struct { + Layers map[string][]error // damaged read-write layers + ROLayers map[string][]error // damaged read-only layers + layerParentsByLayerID map[string]string + layerOrder map[string]int + Images map[string][]error // damaged read-write images (including those with damaged layers) + ROImages map[string][]error // damaged read-only images (including those with damaged layers) + Containers map[string][]error // damaged containers (including those based on damaged images) +} + +// RepairOptions is the set of options for Repair(). +type RepairOptions struct { + RemoveContainers bool // Remove damaged containers +} + +// RepairEverything returns a RepairOptions with every optional remediation +// enabled. +func RepairEverything() *RepairOptions { + return &RepairOptions{ + RemoveContainers: true, + } +} + +// Check returns a list of problems with what's in the store, as a whole. It can be very expensive +// to call. +func (s *store) Check(options *CheckOptions) (CheckReport, error) { + var ignoreChownErrors bool + for _, o := range s.graphOptions { + if strings.Contains(o, "ignore_chown_errors") { + ignoreChownErrors = true + } + } + + if options == nil { + options = CheckMost() + } + + report := CheckReport{ + Layers: make(map[string][]error), + ROLayers: make(map[string][]error), + layerParentsByLayerID: make(map[string]string), // layers ID -> their parent's ID, if there is one + layerOrder: make(map[string]int), // layers ID -> order for removal, if we needed to remove them all + Images: make(map[string][]error), + ROImages: make(map[string][]error), + Containers: make(map[string][]error), + } + + // This map will track known layer IDs. If we have multiple stores, read-only ones can + // contain copies of layers that are in the read-write store, but we'll only ever be + // mounting or extracting contents from the read-write versions, since we always search it + // first. The boolean will track if the layer is referenced by at least one image or + // container. + referencedLayers := make(map[string]bool) + referencedROLayers := make(map[string]bool) + + // This map caches the headers for items included in layer diffs. + diffHeadersByLayer := make(map[string][]*tar.Header) + var diffHeadersByLayerMutex sync.Mutex + + // Walk the list of layer stores, looking at each layer that we didn't see in a + // previously-visited store. + if _, _, err := readAllLayerStores(s, func(store roLayerStore) (struct{}, bool, error) { + layers, err := store.Layers() + if err != nil { + return struct{}{}, true, err + } + isReadWrite := roLayerStoreIsReallyReadWrite(store) + readWriteDesc := "" + if !isReadWrite { + readWriteDesc = "read-only " + } + // Examine each layer in turn. + for i := range layers { + layer := layers[i] + id := layer.ID + // If we've already seen a layer with this ID, no need to process it again. + if _, checked := referencedLayers[id]; checked { + continue + } + if _, checked := referencedROLayers[id]; checked { + continue + } + // Note the parent of this layer, and add it to the map of known layers so + // that we know that we've visited it, but we haven't confirmed that it's + // used by anything. + report.layerParentsByLayerID[id] = layer.Parent + if isReadWrite { + referencedLayers[id] = false + } else { + referencedROLayers[id] = false + } + logrus.Debugf("checking %slayer %s", readWriteDesc, id) + // Check that all of the big data items are present and can be read. We + // have no digest or size information to compare the contents to (grumble), + // so we can't verify that the contents haven't been changed since they + // were stored. + if options.LayerData { + for _, name := range layer.BigDataNames { + func() { + rc, err := store.BigData(id, name) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + err := fmt.Errorf("%slayer %s: data item %q: %w", readWriteDesc, id, name, ErrLayerDataMissing) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + err = fmt.Errorf("%slayer %s: data item %q: %w", readWriteDesc, id, name, err) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + defer rc.Close() + if _, err = io.Copy(io.Discard, rc); err != nil { + err = fmt.Errorf("%slayer %s: data item %q: %w", readWriteDesc, id, name, err) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + }() + } + } + // Check that the content we get back when extracting the layer's contents + // match the recorded digest and size. A layer for which they're not given + // isn't a part of an image, and is likely the read-write layer for a + // container, and we can't vouch for the integrity of its contents. + // For each layer with known contents, record the headers for the layer's + // diff, which we can use to reconstruct the expected contents for the tree + // we see when the layer is mounted. + if options.LayerDigests && layer.UncompressedDigest != "" { + func() { + expectedDigest := layer.UncompressedDigest + // Double-check that the digest isn't invalid somehow. + if err := layer.UncompressedDigest.Validate(); err != nil { + err := fmt.Errorf("%slayer %s: %w", readWriteDesc, id, err) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + // Extract the diff. + uncompressed := archive.Uncompressed + diffOptions := DiffOptions{ + Compression: &uncompressed, + } + diff, err := store.Diff("", id, &diffOptions) + if err != nil { + err := fmt.Errorf("%slayer %s: %w", readWriteDesc, id, err) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + // Digest and count the length of the diff. + digester := expectedDigest.Algorithm().Digester() + counter := ioutils.NewWriteCounter(digester.Hash()) + reader := io.TeeReader(diff, counter) + var wg sync.WaitGroup + var archiveErr error + wg.Add(1) + go func(layerID string, diffReader io.Reader) { + // Read the diff, one item at a time. + tr := tar.NewReader(diffReader) + hdr, err := tr.Next() + for err == nil { + diffHeadersByLayerMutex.Lock() + diffHeadersByLayer[layerID] = append(diffHeadersByLayer[layerID], hdr) + diffHeadersByLayerMutex.Unlock() + hdr, err = tr.Next() + } + if !errors.Is(err, io.EOF) { + archiveErr = err + } + // consume any trailer after the EOF marker + io.Copy(io.Discard, diffReader) + wg.Done() + }(id, reader) + wg.Wait() + diff.Close() + if archiveErr != nil { + // Reading the diff didn't end as expected + diffHeadersByLayerMutex.Lock() + delete(diffHeadersByLayer, id) + diffHeadersByLayerMutex.Unlock() + archiveErr = fmt.Errorf("%slayer %s: %w", readWriteDesc, id, archiveErr) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], archiveErr) + } else { + report.ROLayers[id] = append(report.ROLayers[id], archiveErr) + } + return + } + if digester.Digest() != layer.UncompressedDigest { + // The diff digest didn't match. + diffHeadersByLayerMutex.Lock() + delete(diffHeadersByLayer, id) + diffHeadersByLayerMutex.Unlock() + err := fmt.Errorf("%slayer %s: %w", readWriteDesc, id, ErrLayerIncorrectContentDigest) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + if layer.UncompressedSize != -1 && counter.Count != layer.UncompressedSize { + // We expected the diff to have a specific size, and + // it didn't match. + diffHeadersByLayerMutex.Lock() + delete(diffHeadersByLayer, id) + diffHeadersByLayerMutex.Unlock() + err := fmt.Errorf("%slayer %s: read %d bytes instead of %d bytes: %w", readWriteDesc, id, counter.Count, layer.UncompressedSize, ErrLayerIncorrectContentSize) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + }() + } + } + // At this point we're out of things that we can be sure will work in read-only + // stores, so skip the rest for any stores that aren't also read-write stores. + if !isReadWrite { + return struct{}{}, false, nil + } + // Content and mount checks are also things that we can only be sure will work in + // read-write stores. + for i := range layers { + layer := layers[i] + id := layer.ID + // Compare to what we see when we mount the layer and walk the tree, and + // flag cases where content is in the layer that shouldn't be there. The + // tar-split implementation of Diff() won't catch this problem by itself. + if options.LayerMountable { + func() { + // Mount the layer. + mountPoint, err := s.graphDriver.Get(id, drivers.MountOpts{MountLabel: layer.MountLabel}) + if err != nil { + err := fmt.Errorf("%slayer %s: %w", readWriteDesc, id, err) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + // Unmount the layer when we're done in here. + defer func() { + if err := s.graphDriver.Put(id); err != nil { + err := fmt.Errorf("%slayer %s: %w", readWriteDesc, id, err) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + }() + // If we're not looking at layer contents, or we didn't + // look at the diff for this layer, we're done here. + if !options.LayerDigests || layer.UncompressedDigest == "" || !options.LayerContents { + return + } + // Build a list of all of the changes in all of the layers + // that make up the tree we're looking at. + diffHeaderSet := [][]*tar.Header{} + // If we don't know _all_ of the changes that produced this + // layer, it's not part of an image, so we're done here. + for layerID := id; layerID != ""; layerID = report.layerParentsByLayerID[layerID] { + diffHeadersByLayerMutex.Lock() + layerChanges, haveChanges := diffHeadersByLayer[layerID] + diffHeadersByLayerMutex.Unlock() + if !haveChanges { + return + } + // The diff headers for this layer go _before_ those of + // layers that inherited some of its contents. + diffHeaderSet = append([][]*tar.Header{layerChanges}, diffHeaderSet...) + } + expectedCheckDirectory := newCheckDirectoryDefaults() + for _, diffHeaders := range diffHeaderSet { + expectedCheckDirectory.headers(diffHeaders) + } + // Scan the directory tree under the mount point. + actualCheckDirectory, err := newCheckDirectoryFromDirectory(mountPoint) + if err != nil { + err := fmt.Errorf("scanning contents of %slayer %s: %w", readWriteDesc, id, err) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + return + } + // Every departure from our expectations is an error. + diffs := compareCheckDirectory(expectedCheckDirectory, actualCheckDirectory, ignoreChownErrors) + for _, diff := range diffs { + err := fmt.Errorf("%slayer %s: %s, %w", readWriteDesc, id, diff, ErrLayerContentModified) + if isReadWrite { + report.Layers[id] = append(report.Layers[id], err) + } else { + report.ROLayers[id] = append(report.ROLayers[id], err) + } + } + }() + } + } + // Check that we don't have any dangling parent layer references. + for id, parent := range report.layerParentsByLayerID { + // If this layer doesn't have a parent, no problem. + if parent == "" { + continue + } + // If we've already seen a layer with this parent ID, skip it. + if _, checked := referencedLayers[parent]; checked { + continue + } + if _, checked := referencedROLayers[parent]; checked { + continue + } + // We haven't seen a layer with the ID that this layer's record + // says is its parent's ID. + err := fmt.Errorf("%slayer %s: %w", readWriteDesc, parent, ErrLayerMissing) + report.Layers[id] = append(report.Layers[id], err) + } + return struct{}{}, false, nil + }); err != nil { + return CheckReport{}, err + } + + // This map will track examined images. If we have multiple stores, read-only ones can + // contain copies of images that are also in the read-write store, or the read-write store + // may contain a duplicate entry that refers to layers in the read-only stores, but when + // trying to export them, we only look at the first copy of the image. + examinedImages := make(map[string]struct{}) + + // Walk the list of image stores, looking at each image that we didn't see in a + // previously-visited store. + if _, _, err := readAllImageStores(s, func(store roImageStore) (struct{}, bool, error) { + images, err := store.Images() + if err != nil { + return struct{}{}, true, err + } + isReadWrite := roImageStoreIsReallyReadWrite(store) + readWriteDesc := "" + if !isReadWrite { + readWriteDesc = "read-only " + } + // Examine each image in turn. + for i := range images { + image := images[i] + id := image.ID + // If we've already seen an image with this ID, skip it. + if _, checked := examinedImages[id]; checked { + continue + } + examinedImages[id] = struct{}{} + logrus.Debugf("checking %simage %s", readWriteDesc, id) + if options.ImageData { + // Check that all of the big data items are present and reading them + // back gives us the right amount of data. Even though we record + // digests that can be used to look them up, we don't know how they + // were calculated (they're only used as lookup keys), so do not try + // to check them. + for _, key := range image.BigDataNames { + func() { + data, err := store.BigData(id, key) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + err = fmt.Errorf("%simage %s: data item %q: %w", readWriteDesc, id, key, ErrImageDataMissing) + if isReadWrite { + report.Images[id] = append(report.Images[id], err) + } else { + report.ROImages[id] = append(report.ROImages[id], err) + } + return + } + err = fmt.Errorf("%simage %s: data item %q: %w", readWriteDesc, id, key, err) + if isReadWrite { + report.Images[id] = append(report.Images[id], err) + } else { + report.ROImages[id] = append(report.ROImages[id], err) + } + return + } + if int64(len(data)) != image.BigDataSizes[key] { + err = fmt.Errorf("%simage %s: data item %q: %w", readWriteDesc, id, key, ErrImageDataIncorrectSize) + if isReadWrite { + report.Images[id] = append(report.Images[id], err) + } else { + report.ROImages[id] = append(report.ROImages[id], err) + } + return + } + }() + } + } + // Walk the layers list for the image. For every layer that the image uses + // that has errors, the layer's errors are also the image's errors. + examinedImageLayers := make(map[string]struct{}) + for _, topLayer := range append([]string{image.TopLayer}, image.MappedTopLayers...) { + if topLayer == "" { + continue + } + if _, checked := examinedImageLayers[topLayer]; checked { + continue + } + examinedImageLayers[topLayer] = struct{}{} + for layer := topLayer; layer != ""; layer = report.layerParentsByLayerID[layer] { + // The referenced layer should have a corresponding entry in + // one map or the other. + _, checked := referencedLayers[layer] + _, checkedRO := referencedROLayers[layer] + if !checked && !checkedRO { + err := fmt.Errorf("layer %s: %w", layer, ErrImageLayerMissing) + err = fmt.Errorf("%simage %s: %w", readWriteDesc, id, err) + if isReadWrite { + report.Images[id] = append(report.Images[id], err) + } else { + report.ROImages[id] = append(report.ROImages[id], err) + } + } else { + // Count this layer as referenced. Whether by the + // image or one of its child layers doesn't matter + // at this point. + if _, ok := referencedLayers[layer]; ok { + referencedLayers[layer] = true + } + if _, ok := referencedROLayers[layer]; ok { + referencedROLayers[layer] = true + } + } + if isReadWrite { + if len(report.Layers[layer]) > 0 { + report.Images[id] = append(report.Images[id], report.Layers[layer]...) + } + if len(report.ROLayers[layer]) > 0 { + report.Images[id] = append(report.Images[id], report.ROLayers[layer]...) + } + } else { + if len(report.Layers[layer]) > 0 { + report.ROImages[id] = append(report.ROImages[id], report.Layers[layer]...) + } + if len(report.ROLayers[layer]) > 0 { + report.ROImages[id] = append(report.ROImages[id], report.ROLayers[layer]...) + } + } + } + } + } + return struct{}{}, false, nil + }); err != nil { + return CheckReport{}, err + } + + // Iterate over each container in turn. + if _, _, err := readContainerStore(s, func() (struct{}, bool, error) { + containers, err := s.containerStore.Containers() + if err != nil { + return struct{}{}, true, err + } + for i := range containers { + container := containers[i] + id := container.ID + logrus.Debugf("checking container %s", id) + if options.ContainerData { + // Check that all of the big data items are present and reading them + // back gives us the right amount of data. + for _, key := range container.BigDataNames { + func() { + data, err := s.containerStore.BigData(id, key) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + err = fmt.Errorf("container %s: data item %q: %w", id, key, ErrContainerDataMissing) + report.Containers[id] = append(report.Containers[id], err) + return + } + err = fmt.Errorf("container %s: data item %q: %w", id, key, err) + report.Containers[id] = append(report.Containers[id], err) + return + } + if int64(len(data)) != container.BigDataSizes[key] { + err = fmt.Errorf("container %s: data item %q: %w", id, key, ErrContainerDataIncorrectSize) + report.Containers[id] = append(report.Containers[id], err) + return + } + }() + } + } + // Look at the container's base image. If the image has errors, the image's errors + // are the container's errors. + if container.ImageID != "" { + if _, checked := examinedImages[container.ImageID]; !checked { + err := fmt.Errorf("image %s: %w", container.ImageID, ErrContainerImageMissing) + report.Containers[id] = append(report.Containers[id], err) + } + if len(report.Images[container.ImageID]) > 0 { + report.Containers[id] = append(report.Containers[id], report.Images[container.ImageID]...) + } + if len(report.ROImages[container.ImageID]) > 0 { + report.Containers[id] = append(report.Containers[id], report.ROImages[container.ImageID]...) + } + } + // Count the container's layer as referenced. + if container.LayerID != "" { + referencedLayers[container.LayerID] = true + } + } + return struct{}{}, false, nil + }); err != nil { + return CheckReport{}, err + } + + // Now go back through all of the layer stores, and flag any layers which don't belong + // to an image or a container, and has been around longer than we can reasonably expect + // such a layer to be present before a corresponding image record is added. + if _, _, err := readAllLayerStores(s, func(store roLayerStore) (struct{}, bool, error) { + if isReadWrite := roLayerStoreIsReallyReadWrite(store); !isReadWrite { + return struct{}{}, false, nil + } + layers, err := store.Layers() + if err != nil { + return struct{}{}, true, err + } + for _, layer := range layers { + maximumAge := defaultMaximumUnreferencedLayerAge + if options.LayerUnreferencedMaximumAge != nil { + maximumAge = *options.LayerUnreferencedMaximumAge + } + if referenced := referencedLayers[layer.ID]; !referenced { + if layer.Created.IsZero() || layer.Created.Add(maximumAge).Before(time.Now()) { + // Either we don't (and never will) know when this layer was + // created, or it was created far enough in the past that we're + // reasonably sure it's not part of an image that's being written + // right now. + err := fmt.Errorf("layer %s: %w", layer.ID, ErrLayerUnreferenced) + report.Layers[layer.ID] = append(report.Layers[layer.ID], err) + } + } + } + return struct{}{}, false, nil + }); err != nil { + return CheckReport{}, err + } + + // If the driver can tell us about which layers it knows about, we should have previously + // examined all of them. Any that we didn't are probably just wasted space. + // Note: if the driver doesn't support enumerating layers, it returns ErrNotSupported. + if err := s.startUsingGraphDriver(); err != nil { + return CheckReport{}, err + } + defer s.stopUsingGraphDriver() + layerList, err := s.graphDriver.ListLayers() + if err != nil && !errors.Is(err, drivers.ErrNotSupported) { + return CheckReport{}, err + } + if !errors.Is(err, drivers.ErrNotSupported) { + for i, id := range layerList { + if _, known := referencedLayers[id]; !known { + err := fmt.Errorf("layer %s: %w", id, ErrLayerUnaccounted) + report.Layers[id] = append(report.Layers[id], err) + } + report.layerOrder[id] = i + 1 + } + } + + return report, nil +} + +func roLayerStoreIsReallyReadWrite(store roLayerStore) bool { + return store.(*layerStore).lockfile.IsReadWrite() +} + +func roImageStoreIsReallyReadWrite(store roImageStore) bool { + return store.(*imageStore).lockfile.IsReadWrite() +} + +// Repair removes items which are themselves damaged, or which depend on items which are damaged. +// Errors are returned if an attempt to delete an item fails. +func (s *store) Repair(report CheckReport, options *RepairOptions) []error { + if options == nil { + options = RepairEverything() + } + var errs []error + // Just delete damaged containers. + if options.RemoveContainers { + for id := range report.Containers { + err := s.DeleteContainer(id) + if err != nil && !errors.Is(err, ErrContainerUnknown) { + err := fmt.Errorf("deleting container %s: %w", id, err) + errs = append(errs, err) + } + } + } + // Now delete damaged images. Note which layers were removed as part of removing those images. + deletedLayers := make(map[string]struct{}) + for id := range report.Images { + layers, err := s.DeleteImage(id, true) + if err != nil { + if !errors.Is(err, ErrImageUnknown) && !errors.Is(err, ErrLayerUnknown) { + err := fmt.Errorf("deleting image %s: %w", id, err) + errs = append(errs, err) + } + } else { + for _, layer := range layers { + logrus.Debugf("deleted layer %s", layer) + deletedLayers[layer] = struct{}{} + } + logrus.Debugf("deleted image %s", id) + } + } + // Build a list of the layers that we need to remove, sorted with parents of layers before + // layers that they are parents of. + layersToDelete := make([]string, 0, len(report.Layers)) + for id := range report.Layers { + layersToDelete = append(layersToDelete, id) + } + depth := func(id string) int { + d := 0 + parent := report.layerParentsByLayerID[id] + for parent != "" { + d++ + parent = report.layerParentsByLayerID[parent] + } + return d + } + isUnaccounted := func(errs []error) bool { + for _, err := range errs { + if errors.Is(err, ErrLayerUnaccounted) { + return true + } + } + return false + } + sort.Slice(layersToDelete, func(i, j int) bool { + // we've not heard of either of them, so remove them in the order the driver suggested + if isUnaccounted(report.Layers[layersToDelete[i]]) && + isUnaccounted(report.Layers[layersToDelete[j]]) && + report.layerOrder[layersToDelete[i]] != 0 && report.layerOrder[layersToDelete[j]] != 0 { + return report.layerOrder[layersToDelete[i]] < report.layerOrder[layersToDelete[j]] + } + // always delete the one we've heard of first + if isUnaccounted(report.Layers[layersToDelete[i]]) && !isUnaccounted(report.Layers[layersToDelete[j]]) { + return false + } + // always delete the one we've heard of first + if !isUnaccounted(report.Layers[layersToDelete[i]]) && isUnaccounted(report.Layers[layersToDelete[j]]) { + return true + } + // we've heard of both of them; the one that's on the end of a longer chain goes first + return depth(layersToDelete[i]) > depth(layersToDelete[j]) // closer-to-a-notional-base layers get removed later + }) + // Now delete the layers that haven't been removed along with images. + for _, id := range layersToDelete { + if _, ok := deletedLayers[id]; ok { + continue + } + for _, reportedErr := range report.Layers[id] { + var err error + // If a layer was unaccounted for, remove it at the storage driver level. + // Otherwise, remove it at the higher level and let the higher level + // logic worry about telling the storage driver to delete the layer. + if errors.Is(reportedErr, ErrLayerUnaccounted) { + if err = s.graphDriver.Remove(id); err != nil { + err = fmt.Errorf("deleting storage layer %s: %v", id, err) + } else { + logrus.Debugf("deleted storage layer %s", id) + } + } else { + var stillMounted bool + if stillMounted, err = s.Unmount(id, true); err == nil && !stillMounted { + logrus.Debugf("unmounted layer %s", id) + } else if err != nil { + logrus.Debugf("unmounting layer %s: %v", id, err) + } else { + logrus.Debugf("layer %s still mounted", id) + } + if err = s.DeleteLayer(id); err != nil { + err = fmt.Errorf("deleting layer %s: %w", id, err) + logrus.Debugf("deleted layer %s", id) + } + } + if err != nil && !errors.Is(err, ErrLayerUnknown) && !errors.Is(err, ErrNotALayer) && !errors.Is(err, os.ErrNotExist) { + errs = append(errs, err) + } + } + } + return errs +} + +// compareFileInfo returns a string summarizing what's different between the two checkFileInfos +func compareFileInfo(a, b checkFileInfo, ignoreChownErrors bool) string { + if a.typeflag == b.typeflag && a.uid != b.uid && a.gid != b.gid && a.size != b.size && + (os.ModeType|os.ModePerm)&a.mode != (os.ModeType|os.ModePerm)&b.mode { + return "" + } + var comparison []string + if a.typeflag != b.typeflag { + comparison = append(comparison, fmt.Sprintf("filetype:%v→%v", a.typeflag, b.typeflag)) + } + if a.uid != b.uid && ignoreChownErrors { + comparison = append(comparison, fmt.Sprintf("uid:%d→%d", a.uid, b.uid)) + } + if a.gid != b.gid && ignoreChownErrors { + comparison = append(comparison, fmt.Sprintf("gid:%d→%d", a.gid, b.gid)) + } + if a.size != b.size { + comparison = append(comparison, fmt.Sprintf("size:%d→%d", a.size, b.size)) + } + if (os.ModeType|os.ModePerm)&a.mode != (os.ModeType|os.ModePerm)&b.mode { + comparison = append(comparison, fmt.Sprintf("mode:%04o→%04o", a.mode, b.mode)) + } + return strings.Join(comparison, ",") +} + +// checkFileInfo is what we care about for files +type checkFileInfo struct { + typeflag byte + uid, gid int + size int64 + mode os.FileMode +} + +// checkDirectory is a node in a filesystem record, possibly the top +type checkDirectory struct { + directory map[string]*checkDirectory // subdirectories + file map[string]checkFileInfo // non-directories + checkFileInfo +} + +// newCheckDirectory creates an empty checkDirectory +func newCheckDirectory(uid, gid int, size int64, mode os.FileMode) *checkDirectory { + return &checkDirectory{ + directory: make(map[string]*checkDirectory), + file: make(map[string]checkFileInfo), + checkFileInfo: checkFileInfo{ + typeflag: tar.TypeDir, + uid: uid, + gid: gid, + size: size, + mode: mode, + }, + } +} + +// newCheckDirectoryDefaults creates an empty checkDirectory with hardwired defaults for the UID +// (0), GID (0), size (0) and permissions (0o555) +func newCheckDirectoryDefaults() *checkDirectory { + return newCheckDirectory(0, 0, 0, 0o555) +} + +// newCheckDirectoryFromDirectory creates a checkDirectory for an on-disk directory tree +func newCheckDirectoryFromDirectory(dir string) (*checkDirectory, error) { + cd := newCheckDirectoryDefaults() + err := filepath.Walk(dir, func(walkpath string, info os.FileInfo, err error) error { + if err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + rel, err := filepath.Rel(dir, walkpath) + if err != nil { + return err + } + hdr, err := tar.FileInfoHeader(info, "") // we don't record link targets, so don't bother looking it up + if err != nil { + return err + } + if hdr.Typeflag == tar.TypeLink || hdr.Typeflag == tar.TypeRegA { + hdr.Typeflag = tar.TypeReg + } + hdr.Name = filepath.ToSlash(rel) + cd.header(hdr) + return nil + }) + if err != nil { + return nil, err + } + return cd, nil +} + +// add adds an item to a checkDirectory +func (c *checkDirectory) add(path string, typeflag byte, uid, gid int, size int64, mode os.FileMode) { + components := strings.Split(path, "/") + if components[len(components)-1] == "" { + components = components[:len(components)-1] + } + if typeflag == tar.TypeLink || typeflag == tar.TypeRegA { + typeflag = tar.TypeReg + } + switch len(components) { + case 0: + return + case 1: + switch typeflag { + case tar.TypeDir: + delete(c.file, components[0]) + // directory entries are mergers, not replacements + if _, present := c.directory[components[0]]; !present { + c.directory[components[0]] = newCheckDirectory(uid, gid, size, mode) + } else { + c.directory[components[0]].checkFileInfo = checkFileInfo{ + typeflag: tar.TypeDir, + uid: uid, + gid: gid, + size: size, + mode: mode, + } + } + default: + // treat these as TypeReg items + delete(c.directory, components[0]) + c.file[components[0]] = checkFileInfo{ + typeflag: typeflag, + uid: uid, + gid: gid, + size: size, + mode: mode, + } + case tar.TypeXGlobalHeader: + // ignore, since even though it looks like a valid pathname, it doesn't end + // up on the filesystem + } + return + } + subdirectory := c.directory[components[0]] + if subdirectory == nil { + subdirectory = newCheckDirectory(uid, gid, size, mode) + c.directory[components[0]] = subdirectory + } + subdirectory.add(strings.Join(components[1:], "/"), typeflag, uid, gid, size, mode) +} + +// remove removes an item from a checkDirectory +func (c *checkDirectory) remove(path string) { + components := strings.Split(path, "/") + if len(components) == 1 { + delete(c.directory, components[0]) + delete(c.file, components[0]) + return + } + subdirectory := c.directory[components[0]] + if subdirectory != nil { + subdirectory.remove(strings.Join(components[1:], "/")) + } +} + +// header updates a checkDirectory using information from the passed-in header +func (c *checkDirectory) header(hdr *tar.Header) { + name := path.Clean(hdr.Name) + dir, base := path.Split(name) + if strings.HasPrefix(base, archive.WhiteoutPrefix) { + if base == archive.WhiteoutOpaqueDir { + c.remove(path.Clean(dir)) + c.add(path.Clean(dir), tar.TypeDir, hdr.Uid, hdr.Gid, hdr.Size, os.FileMode(hdr.Mode)) + } else { + c.remove(path.Join(dir, base[len(archive.WhiteoutPrefix):])) + } + } else { + c.add(name, hdr.Typeflag, hdr.Uid, hdr.Gid, hdr.Size, os.FileMode(hdr.Mode)) + } +} + +// headers updates a checkDirectory using information from the passed-in header slice +func (c *checkDirectory) headers(hdrs []*tar.Header) { + hdrs = append([]*tar.Header{}, hdrs...) + // sort the headers from the diff to ensure that whiteouts appear + // before content when they both appear in the same directory, per + // https://github.com/opencontainers/image-spec/blob/main/layer.md#whiteouts + sort.Slice(hdrs, func(i, j int) bool { + idir, ifile := path.Split(hdrs[i].Name) + jdir, jfile := path.Split(hdrs[j].Name) + if idir != jdir { + return hdrs[i].Name < hdrs[j].Name + } + if ifile == archive.WhiteoutOpaqueDir { + return true + } + if strings.HasPrefix(ifile, archive.WhiteoutPrefix) && !strings.HasPrefix(jfile, archive.WhiteoutPrefix) { + return true + } + return false + }) + for _, hdr := range hdrs { + c.header(hdr) + } +} + +// names provides a sorted list of the path names in the directory tree +func (c *checkDirectory) names() []string { + names := make([]string, 0, len(c.file)+len(c.directory)) + for name := range c.file { + names = append(names, name) + } + for name, subdirectory := range c.directory { + names = append(names, name+"/") + for _, subname := range subdirectory.names() { + names = append(names, name+"/"+subname) + } + } + return names +} + +// compareCheckSubdirectory walks two subdirectory trees and returns a list of differences +func compareCheckSubdirectory(path string, a, b *checkDirectory, ignoreChownErrors bool) []string { + var diff []string + if a == nil { + a = newCheckDirectoryDefaults() + } + if b == nil { + b = newCheckDirectoryDefaults() + } + for aname, adir := range a.directory { + if bdir, present := b.directory[aname]; !present { + // directory was removed + diff = append(diff, "-"+path+"/"+aname+"/") + diff = append(diff, compareCheckSubdirectory(path+"/"+aname, adir, nil, ignoreChownErrors)...) + } else { + // directory is in both trees; descend + if attributes := compareFileInfo(adir.checkFileInfo, bdir.checkFileInfo, ignoreChownErrors); attributes != "" { + diff = append(diff, path+"/"+aname+"("+attributes+")") + } + diff = append(diff, compareCheckSubdirectory(path+"/"+aname, adir, bdir, ignoreChownErrors)...) + } + } + for bname, bdir := range b.directory { + if _, present := a.directory[bname]; !present { + // directory added + diff = append(diff, "+"+path+"/"+bname+"/") + diff = append(diff, compareCheckSubdirectory(path+"/"+bname, nil, bdir, ignoreChownErrors)...) + } + } + for aname, afile := range a.file { + if bfile, present := b.file[aname]; !present { + // non-directory removed or replaced + diff = append(diff, "-"+path+"/"+aname) + } else { + // item is in both trees; compare + if attributes := compareFileInfo(afile, bfile, ignoreChownErrors); attributes != "" { + diff = append(diff, path+"/"+aname+"("+attributes+")") + } + } + } + for bname := range b.file { + filetype, present := a.file[bname] + if !present { + // non-directory added or replaced with something else + diff = append(diff, "+"+path+"/"+bname) + continue + } + if attributes := compareFileInfo(filetype, b.file[bname], ignoreChownErrors); attributes != "" { + // non-directory replaced with non-directory + diff = append(diff, "+"+path+"/"+bname+"("+attributes+")") + } + } + return diff +} + +// compareCheckDirectory walks two directory trees and returns a sorted list of differences +func compareCheckDirectory(a, b *checkDirectory, ignoreChownErrors bool) []string { + diff := compareCheckSubdirectory("", a, b, ignoreChownErrors) + sort.Slice(diff, func(i, j int) bool { + if strings.Compare(diff[i][1:], diff[j][1:]) < 0 { + return true + } + if diff[i][0] == '-' { + return true + } + return false + }) + return diff +} diff --git a/vendor/github.com/containers/storage/drivers/aufs/aufs.go b/vendor/github.com/containers/storage/drivers/aufs/aufs.go index 301ee24d24..4b0133a055 100644 --- a/vendor/github.com/containers/storage/drivers/aufs/aufs.go +++ b/vendor/github.com/containers/storage/drivers/aufs/aufs.go @@ -251,9 +251,21 @@ func (a *Driver) Exists(id string) bool { return true } -// List layers (not including additional image stores) +// ListLayers() returns all of the layers known to the driver. func (a *Driver) ListLayers() ([]string, error) { - return nil, graphdriver.ErrNotSupported + diffsDir := filepath.Join(a.rootPath(), "diff") + entries, err := os.ReadDir(diffsDir) + if err != nil { + return nil, err + } + results := make([]string, 0, len(entries)) + for _, entry := range entries { + if !entry.IsDir() { + continue + } + results = append(results, entry.Name()) + } + return results, nil } // AdditionalImageStores returns additional image stores supported by the driver diff --git a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go index 8452fa1899..e68804416d 100644 --- a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go +++ b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go @@ -70,7 +70,7 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) if err != nil { return nil, err } - if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAs(filepath.Join(home, "subvolumes"), 0700, rootUID, rootGID); err != nil { return nil, err } @@ -119,7 +119,7 @@ func parseOptions(opt []string) (btrfsOptions, bool, error) { case "btrfs.mountopt": return options, userDiskQuota, fmt.Errorf("btrfs driver does not support mount options") default: - return options, userDiskQuota, fmt.Errorf("unknown option %s", key) + return options, userDiskQuota, fmt.Errorf("unknown option %s (%q)", key, option) } } return options, userDiskQuota, nil @@ -479,8 +479,8 @@ func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts // Create the filesystem with given id. func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { - quotas := path.Join(d.home, "quotas") - subvolumes := path.Join(d.home, "subvolumes") + quotas := d.quotasDir() + subvolumes := d.subvolumesDir() rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { return err @@ -560,7 +560,7 @@ func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) e } driver.options.size = uint64(size) default: - return fmt.Errorf("unknown option %s", key) + return fmt.Errorf("unknown option %s (%q)", key, storageOpt) } } @@ -679,9 +679,21 @@ func (d *Driver) Exists(id string) bool { return err == nil } -// List layers (not including additional image stores) +// List all of the layers known to the driver. func (d *Driver) ListLayers() ([]string, error) { - return nil, graphdriver.ErrNotSupported + subvolumesDir := filepath.Join(d.home, "subvolumes") + entries, err := os.ReadDir(subvolumesDir) + if err != nil { + return nil, err + } + results := make([]string, 0, len(entries)) + for _, entry := range entries { + if !entry.IsDir() { + continue + } + results = append(results, entry.Name()) + } + return results, nil } // AdditionalImageStores returns additional image stores supported by the driver diff --git a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go index d2d0effc34..8603a0516a 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go @@ -1001,6 +1001,10 @@ func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error { devices.Lock() defer devices.Unlock() + if devices.filesystem == "" { + devices.filesystem = determineDefaultFS() + } + if err := devices.activateDeviceIfNeeded(baseInfo, false); err != nil { return err } @@ -1707,6 +1711,9 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil { return err } + if err := idtools.MkdirAs(filepath.Join(devices.root, "mnt"), 0700, uid, gid); err != nil && !errors.Is(err, os.ErrExist) { + return err + } prevSetupConfig, err := readLVMConfig(devices.root) if err != nil { @@ -2317,7 +2324,7 @@ func (devices *DeviceSet) Shutdown(home string) error { info.lock.Lock() devices.Lock() if err := devices.deactivateDevice(info); err != nil { - logrus.Debugf("devmapper: Shutdown deactivate base , error: %s", err) + logrus.Debugf("devmapper: Shutdown deactivate base, error: %s", err) } devices.Unlock() info.lock.Unlock() @@ -2326,7 +2333,7 @@ func (devices *DeviceSet) Shutdown(home string) error { devices.Lock() if devices.thinPoolDevice == "" { if err := devices.deactivatePool(); err != nil { - logrus.Debugf("devmapper: Shutdown deactivate pool , error: %s", err) + logrus.Debugf("devmapper: Shutdown deactivate pool, error: %s", err) } } devices.Unlock() @@ -2483,6 +2490,26 @@ func (devices *DeviceSet) List() []string { return ids } +// ListLayers returns a list of device IDs, omitting the ""/"base" device and +// any which have been marked as deleted. +func (devices *DeviceSet) ListLayers() ([]string, error) { + if err := devices.cleanupDeletedDevices(); err != nil { + return nil, err + } + + devices.Lock() + defer devices.Unlock() + + ids := make([]string, 0, len(devices.Devices)) + for k, d := range devices.Devices { + if k == "" || d.Deleted { + continue + } + ids = append(ids, k) + } + return ids, nil +} + func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) { var params string _, sizeInSectors, _, params, err = devicemapper.GetStatus(devName) diff --git a/vendor/github.com/containers/storage/drivers/devmapper/driver.go b/vendor/github.com/containers/storage/drivers/devmapper/driver.go index 8b3ee51df7..f53f0625f1 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/driver.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/driver.go @@ -55,7 +55,6 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), locker: locker.New(), } - return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil } @@ -267,11 +266,6 @@ func (d *Driver) Exists(id string) bool { return d.DeviceSet.HasDevice(id) } -// List layers (not including additional image stores) -func (d *Driver) ListLayers() ([]string, error) { - return nil, graphdriver.ErrNotSupported -} - // AdditionalImageStores returns additional image stores supported by the driver func (d *Driver) AdditionalImageStores() []string { return nil diff --git a/vendor/github.com/containers/storage/drivers/driver.go b/vendor/github.com/containers/storage/drivers/driver.go index 055d99d183..4ac36684dc 100644 --- a/vendor/github.com/containers/storage/drivers/driver.go +++ b/vendor/github.com/containers/storage/drivers/driver.go @@ -111,6 +111,10 @@ type ProtoDriver interface { Exists(id string) bool // Returns a list of layer ids that exist on this driver (does not include // additional storage layers). Not supported by all backends. + // If the driver requires that layers be removed in a particular order, + // usually due to parent-child relationships that it cares about, The + // list should be sorted well enough so that if all layers need to be + // removed, they can be removed in the order in which they're returned. ListLayers() ([]string, error) // Status returns a set of key-value pairs which give low // level diagnostic status about this driver. @@ -322,6 +326,7 @@ func getBuiltinDriver(name, home string, options Options) (Driver, error) { type Options struct { Root string RunRoot string + ImageStore string DriverPriority []string DriverOptions []string UIDMaps []idtools.IDMap diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index b606713f04..64dcadebc3 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -29,7 +29,6 @@ import ( "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/mount" "github.com/containers/storage/pkg/parsers" - "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/unshare" units "github.com/docker/go-units" @@ -47,7 +46,9 @@ var ( ) const ( - defaultPerms = os.FileMode(0555) + defaultPerms = os.FileMode(0555) + selinuxLabelTest = "system_u:object_r:container_file_t:s0" + mountProgramFlagFile = ".has-mount-program" ) // This backend uses the overlay union filesystem for containers @@ -78,9 +79,10 @@ const ( // that mounts do not fail due to length. const ( - linkDir = "l" - lowerFile = "lower" - maxDepth = 500 + linkDir = "l" + stagingDir = "staging" + lowerFile = "lower" + maxDepth = 500 // idLength represents the number of random characters // which can be used to create the unique link identifier @@ -110,6 +112,7 @@ type Driver struct { name string home string runhome string + imageStore string uidMaps []idtools.IDMap gidMaps []idtools.IDMap ctr *graphdriver.RefCounter @@ -124,7 +127,6 @@ type Driver struct { } type additionalLayerStore struct { - // path is the directory where this store is available on the host. path string @@ -175,7 +177,7 @@ func hasVolatileOption(opts []string) bool { } func getMountProgramFlagFile(path string) string { - return filepath.Join(path, ".has-mount-program") + return filepath.Join(path, mountProgramFlagFile) } func checkSupportVolatile(home, runhome string) (bool, error) { @@ -303,6 +305,16 @@ func isNetworkFileSystem(fsMagic graphdriver.FsMagic) bool { // If overlay filesystem is not supported on the host, a wrapped graphdriver.ErrNotSupported is returned as error. // If an overlay filesystem is not supported over an existing filesystem then a wrapped graphdriver.ErrIncompatibleFS is returned. func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) { + // If custom --imagestore is selected never + // ditch the original graphRoot, instead add it as + // additionalImageStore so its images can still be + // read and used. + if options.ImageStore != "" { + graphRootAsAdditionalStore := fmt.Sprintf("AdditionalImageStore=%s", options.ImageStore) + options.DriverOptions = append(options.DriverOptions, graphRootAsAdditionalStore) + // complete base name with driver name included + options.ImageStore = filepath.Join(options.ImageStore, "overlay") + } opts, err := parseOptions(options.DriverOptions) if err != nil { return nil, err @@ -329,6 +341,12 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) return nil, err } + if options.ImageStore != "" { + if err := idtools.MkdirAllAs(path.Join(options.ImageStore, linkDir), 0755, 0, 0); err != nil { + return nil, err + } + } + if err := idtools.MkdirAllAs(runhome, 0700, rootUID, rootGID); err != nil { return nil, err } @@ -420,6 +438,7 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) d := &Driver{ name: "overlay", home: home, + imageStore: options.ImageStore, runhome: runhome, uidMaps: options.UIDMaps, gidMaps: options.GIDMaps, @@ -806,15 +825,22 @@ func (d *Driver) Status() [][2]string { // Metadata returns meta data about the overlay driver such as // LowerDir, UpperDir, WorkDir and MergeDir used to store data. func (d *Driver) Metadata(id string) (map[string]string, error) { - dir := d.dir(id) + dir, imagestore, _ := d.dir2(id) if _, err := os.Stat(dir); err != nil { return nil, err } + workDirBase := dir + if imagestore != "" { + if _, err := os.Stat(dir); err != nil { + return nil, err + } + workDirBase = imagestore + } metadata := map[string]string{ - "WorkDir": path.Join(dir, "work"), - "MergedDir": path.Join(dir, "merged"), - "UpperDir": path.Join(dir, "diff"), + "WorkDir": path.Join(workDirBase, "work"), + "MergedDir": path.Join(workDirBase, "merged"), + "UpperDir": path.Join(workDirBase, "diff"), } lowerDirs, err := d.getLowerDirs(id) @@ -929,7 +955,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr } func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disableQuota bool) (retErr error) { - dir := d.dir(id) + dir, imageStore, _ := d.dir2(id) uidMaps := d.uidMaps gidMaps := d.gidMaps @@ -957,8 +983,19 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable if err := idtools.MkdirAllAndChownNew(path.Dir(dir), 0755, idPair); err != nil { return err } + workDirBase := dir + if imageStore != "" { + workDirBase = imageStore + if err := idtools.MkdirAllAndChownNew(path.Dir(imageStore), 0755, idPair); err != nil { + return err + } + } if parent != "" { - st, err := system.Stat(d.dir(parent)) + parentBase, parentImageStore, _ := d.dir2(parent) + if parentImageStore != "" { + parentBase = parentImageStore + } + st, err := system.Stat(filepath.Join(parentBase, "diff")) if err != nil { return err } @@ -978,6 +1015,11 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable if err := idtools.MkdirAllAndChownNew(dir, 0700, idPair); err != nil { return err } + if imageStore != "" { + if err := idtools.MkdirAllAndChownNew(imageStore, 0700, idPair); err != nil { + return err + } + } defer func() { // Clean up on failure @@ -985,6 +1027,11 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable if err2 := os.RemoveAll(dir); err2 != nil { logrus.Errorf("While recovering from a failure creating a layer, error deleting %#v: %v", dir, err2) } + if imageStore != "" { + if err2 := os.RemoveAll(workDirBase); err2 != nil { + logrus.Errorf("While recovering from a failure creating a layer, error deleting %#v: %v", dir, err2) + } + } } }() @@ -1013,20 +1060,31 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable if d.options.forceMask != nil { perms = *d.options.forceMask } + if parent != "" { - st, err := system.Stat(filepath.Join(d.dir(parent), "diff")) + parentDir, parentImageStore, _ := d.dir2(parent) + base := parentDir + if parentImageStore != "" { + base = parentImageStore + } + st, err := system.Stat(filepath.Join(base, "diff")) if err != nil { return err } perms = os.FileMode(st.Mode()) } - if err := idtools.MkdirAs(path.Join(dir, "diff"), perms, rootUID, rootGID); err != nil { + if err := idtools.MkdirAs(path.Join(workDirBase, "diff"), perms, rootUID, rootGID); err != nil { return err } lid := generateID(idLength) - if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil { + + linkBase := path.Join("..", id, "diff") + if imageStore != "" { + linkBase = path.Join(imageStore, "diff") + } + if err := os.Symlink(linkBase, path.Join(d.home, linkDir, lid)); err != nil { return err } @@ -1035,7 +1093,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable return err } - if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAs(path.Join(workDirBase, "work"), 0700, rootUID, rootGID); err != nil { return err } if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { @@ -1120,22 +1178,26 @@ func (d *Driver) getLower(parent string) (string, error) { } func (d *Driver) dir(id string) string { - p, _ := d.dir2(id) + p, _, _ := d.dir2(id) return p } -func (d *Driver) dir2(id string) (string, bool) { +func (d *Driver) dir2(id string) (string, string, bool) { newpath := path.Join(d.home, id) + imageStore := "" + if d.imageStore != "" { + imageStore = path.Join(d.imageStore, id) + } if _, err := os.Stat(newpath); err != nil { for _, p := range d.AdditionalImageStores() { l := path.Join(p, d.name, id) _, err = os.Stat(l) if err == nil { - return l, true + return l, imageStore, true } } } - return newpath, false + return newpath, imageStore, false } func (d *Driver) getLowerDirs(id string) ([]string, error) { @@ -1342,10 +1404,14 @@ func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) { } func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountOpts) (_ string, retErr error) { - dir, inAdditionalStore := d.dir2(id) + dir, imageStore, inAdditionalStore := d.dir2(id) if _, err := os.Stat(dir); err != nil { return "", err } + workDirBase := dir + if imageStore != "" { + workDirBase = imageStore + } readWrite := !inAdditionalStore if !d.SupportsShifting() || options.DisableShifting { @@ -1478,11 +1544,11 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO absLowers = append(absLowers, path.Join(dir, "empty")) } // user namespace requires this to move a directory from lower to upper. - rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) + rootUID, rootGID, err := idtools.GetRootUIDGID(options.UidMaps, options.GidMaps) if err != nil { return "", err } - diffDir := path.Join(dir, "diff") + diffDir := path.Join(workDirBase, "diff") if err := idtools.MkdirAllAs(diffDir, perms, rootUID, rootGID); err != nil { return "", err } @@ -1505,7 +1571,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO } }() - workdir := path.Join(dir, "work") + workdir := path.Join(workDirBase, "work") if d.options.mountProgram == "" && unshare.IsRootless() { optsList = append(optsList, "userxattr") @@ -1725,20 +1791,23 @@ func (d *Driver) ListLayers() ([]string, error) { if err != nil { return nil, err } - layers := make([]string, 0) for _, entry := range entries { id := entry.Name() - // Does it look like a datadir directory? - if !entry.IsDir() || stringid.ValidateID(id) != nil { + switch id { + case linkDir, stagingDir, quota.BackingFsBlockDeviceLink, mountProgramFlagFile: + // expected, but not a layer. skip it continue + default: + // Does it look like a datadir directory? + if !entry.IsDir() { + continue + } + layers = append(layers, id) } - - layers = append(layers, id) } - - return layers, err + return layers, nil } // isParent returns if the passed in parent is the direct parent of the passed in layer @@ -1795,7 +1864,7 @@ func (g *overlayFileGetter) Close() error { } func (d *Driver) getStagingDir() string { - return filepath.Join(d.home, "staging") + return filepath.Join(d.home, stagingDir) } // DiffGetter returns a FileGetCloser that can read files from the directory that @@ -1922,8 +1991,12 @@ func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts) } func (d *Driver) getDiffPath(id string) (string, error) { - dir := d.dir(id) - return redirectDiffIfAdditionalLayer(path.Join(dir, "diff")) + dir, imagestore, _ := d.dir2(id) + base := dir + if imagestore != "" { + base = imagestore + } + return redirectDiffIfAdditionalLayer(path.Join(base, "diff")) } func (d *Driver) getLowerDiffPaths(id string) ([]string, error) { @@ -2014,8 +2087,12 @@ func (d *Driver) AdditionalImageStores() []string { // 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") + dir, imagestore, _ := d.dir2(id) + base := dir + if imagestore != "" { + base = imagestore + } + diffDir := filepath.Join(base, "diff") rootUID, rootGID := 0, 0 if toHost != nil { diff --git a/vendor/github.com/containers/storage/drivers/quota/projectquota.go b/vendor/github.com/containers/storage/drivers/quota/projectquota.go index f5484dee74..2b051e4103 100644 --- a/vendor/github.com/containers/storage/drivers/quota/projectquota.go +++ b/vendor/github.com/containers/storage/drivers/quota/projectquota.go @@ -67,6 +67,10 @@ import ( const projectIDsAllocatedPerQuotaHome = 10000 +// BackingFsBlockDeviceLink is the name of a file that we place in +// the home directory of a driver that uses this package. +const BackingFsBlockDeviceLink = "backingFsBlockDev" + // Quota limit params - currently we only control blocks hard limit and inodes type Quota struct { Size uint64 @@ -395,9 +399,9 @@ func openDir(path string) (*C.DIR, error) { Cpath := C.CString(path) defer free(Cpath) - dir := C.opendir(Cpath) + dir, errno := C.opendir(Cpath) if dir == nil { - return nil, fmt.Errorf("can't open dir %v", Cpath) + return nil, fmt.Errorf("can't open dir %v: %w", Cpath, errno) } return dir, nil } @@ -421,7 +425,7 @@ func makeBackingFsDev(home string) (string, error) { return "", err } - backingFsBlockDev := path.Join(home, "backingFsBlockDev") + backingFsBlockDev := path.Join(home, BackingFsBlockDeviceLink) backingFsBlockDevTmp := backingFsBlockDev + ".tmp" // Re-create just in case someone copied the home directory over to a new device if err := unix.Mknod(backingFsBlockDevTmp, unix.S_IFBLK|0600, int(stat.Dev)); err != nil { diff --git a/vendor/github.com/containers/storage/drivers/template.go b/vendor/github.com/containers/storage/drivers/template.go index 7b96c082d7..66ab89f7f7 100644 --- a/vendor/github.com/containers/storage/drivers/template.go +++ b/vendor/github.com/containers/storage/drivers/template.go @@ -34,6 +34,7 @@ func NaiveCreateFromTemplate(d TemplateDriver, id, template string, templateIDMa } return err } + defer diff.Close() applyOptions := ApplyDiffOpts{ Diff: diff, diff --git a/vendor/github.com/containers/storage/drivers/vfs/driver.go b/vendor/github.com/containers/storage/drivers/vfs/driver.go index bf0cfe9408..50a4adc0ba 100644 --- a/vendor/github.com/containers/storage/drivers/vfs/driver.go +++ b/vendor/github.com/containers/storage/drivers/vfs/driver.go @@ -14,18 +14,12 @@ import ( "github.com/containers/storage/pkg/directory" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/parsers" - "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/system" "github.com/opencontainers/selinux/go-selinux/label" "github.com/sirupsen/logrus" "github.com/vbatts/tar-split/tar/storage" ) -var ( - // CopyDir defines the copy method to use. - CopyDir = dirCopy -) - const defaultPerms = os.FileMode(0555) func init() { @@ -42,11 +36,10 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) } rootIDs := d.idMappings.RootPair() - if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil { + if err := idtools.MkdirAllAndChown(filepath.Join(home, "dir"), 0700, rootIDs); err != nil { return nil, err } for _, option := range options.DriverOptions { - key, val, err := parsers.ParseKeyValueOpt(option) if err != nil { return nil, err @@ -69,6 +62,12 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) return nil, fmt.Errorf("vfs driver does not support %s options", key) } } + // If --imagestore is provided, lets add writable graphRoot + // to vfs's additional image store, as it is done for + // `overlay` driver. + if options.ImageStore != "" { + d.homes = append(d.homes, options.ImageStore) + } d.updater = graphdriver.NewNaiveLayerIDMapUpdater(d) d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, d.updater) @@ -268,7 +267,7 @@ func (d *Driver) Exists(id string) bool { // List layers (not including additional image stores) func (d *Driver) ListLayers() ([]string, error) { - entries, err := os.ReadDir(d.homes[0]) + entries, err := os.ReadDir(filepath.Join(d.homes[0], "dir")) if err != nil { return nil, err } @@ -278,7 +277,7 @@ func (d *Driver) ListLayers() ([]string, error) { for _, entry := range entries { id := entry.Name() // Does it look like a datadir directory? - if !entry.IsDir() || stringid.ValidateID(id) != nil { + if !entry.IsDir() { continue } @@ -304,7 +303,16 @@ func (d *Driver) SupportsShifting() bool { // 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 { - return d.updater.UpdateLayerIDMap(id, toContainer, toHost, mountLabel) + if err := d.updater.UpdateLayerIDMap(id, toContainer, toHost, mountLabel); err != nil { + return err + } + dir := d.dir(id) + rootIDs, err := toHost.ToHost(idtools.IDPair{UID: 0, GID: 0}) + if err != nil { + return err + } + return os.Chown(dir, rootIDs.UID, rootIDs.GID) + } // Changes produces a list of changes between the specified layer diff --git a/vendor/github.com/containers/storage/drivers/zfs/zfs.go b/vendor/github.com/containers/storage/drivers/zfs/zfs.go index aeef641033..703b660d23 100644 --- a/vendor/github.com/containers/storage/drivers/zfs/zfs.go +++ b/vendor/github.com/containers/storage/drivers/zfs/zfs.go @@ -409,7 +409,6 @@ func (d *Driver) Remove(id string) error { // Get returns the mountpoint for the given id after creating the target directories if necessary. func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) { - mountpoint := d.mountPath(id) if count := d.ctr.Increment(mountpoint); count > 1 { return mountpoint, nil @@ -506,7 +505,9 @@ func (d *Driver) Exists(id string) bool { return d.filesystemsCache[d.zfsPath(id)] } -// List layers (not including additional image stores) +// List layers (not including additional image stores). Our layers aren't all +// dependent on a single well-known dataset, so we can't reliably tell which +// datasets are ours and which ones just look like they could be ours. func (d *Driver) ListLayers() ([]string, error) { return nil, graphdriver.ErrNotSupported } diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index 3f37405b0c..59f17eb06d 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -1922,6 +1922,18 @@ func (r *layerStore) Wipe() error { return err } } + ids, err := r.driver.ListLayers() + if err != nil { + if !errors.Is(err, drivers.ErrNotSupported) { + return err + } + ids = nil + } + for _, id := range ids { + if err := r.driver.Remove(id); err != nil { + return err + } + } return nil } @@ -2198,7 +2210,7 @@ func (r *layerStore) applyDiffWithOptions(to string, layerOptions *LayerOptions, return -1, err } compression := archive.DetectCompression(header[:n]) - defragmented := io.MultiReader(bytes.NewBuffer(header[:n]), diff) + defragmented := io.MultiReader(bytes.NewReader(header[:n]), diff) // Decide if we need to compute digests var compressedDigest, uncompressedDigest digest.Digest // = "" diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go index 6209205b3a..a391a89867 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive.go @@ -1422,7 +1422,7 @@ func IsArchive(header []byte) bool { if compression != Uncompressed { return true } - r := tar.NewReader(bytes.NewBuffer(header)) + r := tar.NewReader(bytes.NewReader(header)) _, err := r.Next() return err == nil } diff --git a/vendor/github.com/containers/storage/pkg/config/config.go b/vendor/github.com/containers/storage/pkg/config/config.go index f6e0cfcfe8..20d72ca899 100644 --- a/vendor/github.com/containers/storage/pkg/config/config.go +++ b/vendor/github.com/containers/storage/pkg/config/config.go @@ -124,6 +124,11 @@ type OptionsConfig struct { // for shared image content AdditionalImageStores []string `toml:"additionalimagestores,omitempty"` + // ImageStore is the location of image store which is separated from the + // container store. Usually this is not recommended unless users wants + // separate store for image and containers. + ImageStore string `toml:"imagestore,omitempty"` + // AdditionalLayerStores is the location of additional read/only // Layer stores. Usually used to access Networked File System // for shared image content diff --git a/vendor/github.com/containers/storage/pkg/system/rm.go b/vendor/github.com/containers/storage/pkg/system/rm.go index 60c7d8bd9b..5917fa251d 100644 --- a/vendor/github.com/containers/storage/pkg/system/rm.go +++ b/vendor/github.com/containers/storage/pkg/system/rm.go @@ -30,6 +30,12 @@ func EnsureRemoveAll(dir string) error { exitOnErr := make(map[string]int) maxRetry := 100 + // Attempt a simple remove all first, this avoids the more expensive + // RecursiveUnmount call if not needed. + if err := os.RemoveAll(dir); err == nil { + return nil + } + // Attempt to unmount anything beneath this dir first if err := mount.RecursiveUnmount(dir); err != nil { logrus.Debugf("RecusiveUnmount on %s failed: %v", dir, err) diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 4c4082084d..a4dd215c2f 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -520,6 +520,13 @@ type Store interface { // references in the json files. These can happen in the case of unclean // shutdowns or regular restarts in transient store mode. GarbageCollect() error + + // Check returns a report of things that look wrong in the store. + Check(options *CheckOptions) (CheckReport, error) + // Repair attempts to remediate problems mentioned in the CheckReport, + // usually by deleting layers and images which are damaged. If the + // right options are set, it will remove containers as well. + Repair(report CheckReport, options *RepairOptions) []error } // AdditionalLayer represents a layer that is contained in the additional layer store @@ -661,6 +668,7 @@ type store struct { usernsLock *lockfile.LockFile graphRoot string graphOptions []string + imageStoreDir string pullOptions map[string]string uidMap []idtools.IDMap gidMap []idtools.IDMap @@ -668,6 +676,7 @@ type store struct { autoNsMinSize uint32 autoNsMaxSize uint32 imageStore rwImageStore + rwImageStores []rwImageStore roImageStores []roImageStore containerStore rwContainerStore digestLockRoot string @@ -755,9 +764,19 @@ func GetStore(options types.StoreOptions) (Store, error) { if err := os.MkdirAll(options.GraphRoot, 0700); err != nil { return nil, err } + if options.ImageStore != "" { + if err := os.MkdirAll(options.ImageStore, 0700); err != nil { + return nil, err + } + } if err := os.MkdirAll(filepath.Join(options.GraphRoot, options.GraphDriverName), 0700); err != nil { return nil, err } + if options.ImageStore != "" { + if err := os.MkdirAll(filepath.Join(options.ImageStore, options.GraphDriverName), 0700); err != nil { + return nil, err + } + } graphLock, err := lockfile.GetLockFile(filepath.Join(options.GraphRoot, "storage.lock")) if err != nil { @@ -785,6 +804,7 @@ func GetStore(options types.StoreOptions) (Store, error) { usernsLock: usernsLock, graphRoot: options.GraphRoot, graphOptions: options.GraphDriverOptions, + imageStoreDir: options.ImageStore, pullOptions: options.PullOptions, uidMap: copyIDMap(options.UIDMap), gidMap: copyIDMap(options.GIDMap), @@ -889,7 +909,11 @@ func (s *store) load() error { } driverPrefix := s.graphDriverName + "-" - gipath := filepath.Join(s.graphRoot, driverPrefix+"images") + imgStoreRoot := s.imageStoreDir + if imgStoreRoot == "" { + imgStoreRoot = s.graphRoot + } + gipath := filepath.Join(imgStoreRoot, driverPrefix+"images") if err := os.MkdirAll(gipath, 0700); err != nil { return err } @@ -917,9 +941,22 @@ func (s *store) load() error { for _, store := range driver.AdditionalImageStores() { gipath := filepath.Join(store, driverPrefix+"images") - ris, err := newROImageStore(gipath) - if err != nil { - return err + var ris roImageStore + if s.imageStoreDir != "" && store == s.graphRoot { + // If --imagestore was set and current store + // is `graphRoot` then mount it as a `rw` additional + // store instead of `readonly` additional store. + imageStore, err := newImageStore(gipath) + if err != nil { + return err + } + s.rwImageStores = append(s.rwImageStores, imageStore) + ris = imageStore + } else { + ris, err = newROImageStore(gipath) + if err != nil { + return err + } } s.roImageStores = append(s.roImageStores, ris) } @@ -989,8 +1026,15 @@ func (s *store) stopUsingGraphDriver() { // Almost all users should use startUsingGraphDriver instead. // The caller must hold s.graphLock. func (s *store) createGraphDriverLocked() (drivers.Driver, error) { + driverRoot := s.imageStoreDir + imageStoreBase := s.graphRoot + if driverRoot == "" { + driverRoot = s.graphRoot + imageStoreBase = "" + } config := drivers.Options{ - Root: s.graphRoot, + Root: driverRoot, + ImageStore: imageStoreBase, RunRoot: s.runRoot, DriverPriority: s.graphDriverPriority, DriverOptions: s.graphOptions, @@ -1020,7 +1064,11 @@ func (s *store) getLayerStoreLocked() (rwLayerStore, error) { if err := os.MkdirAll(rlpath, 0700); err != nil { return nil, err } - glpath := filepath.Join(s.graphRoot, driverPrefix+"layers") + imgStoreRoot := s.imageStoreDir + if imgStoreRoot == "" { + imgStoreRoot = s.graphRoot + } + glpath := filepath.Join(imgStoreRoot, driverPrefix+"layers") if err := os.MkdirAll(glpath, 0700); err != nil { return nil, err } @@ -1081,7 +1129,7 @@ func (s *store) bothLayerStoreKindsLocked() (rwLayerStore, []roLayerStore, error } // bothLayerStoreKinds returns the primary, and additional read-only, layer store objects used by the store. -// It must be called with s.graphLock held. +// It must be called WITHOUT s.graphLock held. func (s *store) bothLayerStoreKinds() (rwLayerStore, []roLayerStore, error) { if err := s.startUsingGraphDriver(); err != nil { return nil, nil, err @@ -1115,51 +1163,54 @@ func (s *store) allLayerStores() ([]roLayerStore, error) { // readAllLayerStores processes allLayerStores() in order: // It locks the store for reading, checks for updates, and calls // -// (done, err) := fn(store) +// (data, done, err) := fn(store) // // until the callback returns done == true, and returns the data from the callback. // -// If reading any layer store fails, it immediately returns (true, err). +// If reading any layer store fails, it immediately returns ({}, true, err). // -// If all layer stores are processed without setting done == true, it returns (false, nil). +// If all layer stores are processed without setting done == true, it returns ({}, false, nil). // // Typical usage: // -// var res T = failureValue -// if done, err := s.readAllLayerStores(store, func(…) { +// if res, done, err := s.readAllLayerStores(store, func(…) { // … // }; done { // return res, err // } -func (s *store) readAllLayerStores(fn func(store roLayerStore) (bool, error)) (bool, error) { +func readAllLayerStores[T any](s *store, fn func(store roLayerStore) (T, bool, error)) (T, bool, error) { + var zeroRes T // A zero value of T + layerStores, err := s.allLayerStores() if err != nil { - return true, err + return zeroRes, true, err } for _, s := range layerStores { store := s if err := store.startReading(); err != nil { - return true, err + return zeroRes, true, err } defer store.stopReading() - if done, err := fn(store); done { - return true, err + if res, done, err := fn(store); done { + return res, true, err } } - return false, nil + return zeroRes, false, nil } // writeToLayerStore is a helper for working with store.getLayerStore(): // It locks the store for writing, checks for updates, and calls fn() // It returns the return value of fn, or its own error initializing the store. -func (s *store) writeToLayerStore(fn func(store rwLayerStore) error) error { +func writeToLayerStore[T any](s *store, fn func(store rwLayerStore) (T, error)) (T, error) { + var zeroRes T // A zero value of T + store, err := s.getLayerStore() if err != nil { - return err + return zeroRes, err } if err := store.startWriting(); err != nil { - return err + return zeroRes, err } defer store.stopWriting() return fn(store) @@ -1174,53 +1225,69 @@ func (s *store) allImageStores() []roImageStore { // readAllImageStores processes allImageStores() in order: // It locks the store for reading, checks for updates, and calls // -// (done, err) := fn(store) +// (data, done, err) := fn(store) // // until the callback returns done == true, and returns the data from the callback. // -// If reading any Image store fails, it immediately returns (true, err). +// If reading any Image store fails, it immediately returns ({}, true, err). // -// If all Image stores are processed without setting done == true, it returns (false, nil). +// If all Image stores are processed without setting done == true, it returns ({}, false, nil). // // Typical usage: // -// var res T = failureValue -// if done, err := s.readAllImageStores(store, func(…) { +// if res, done, err := readAllImageStores(store, func(…) { // … // }; done { // return res, err // } -func (s *store) readAllImageStores(fn func(store roImageStore) (bool, error)) (bool, error) { +func readAllImageStores[T any](s *store, fn func(store roImageStore) (T, bool, error)) (T, bool, error) { + var zeroRes T // A zero value of T + for _, s := range s.allImageStores() { store := s if err := store.startReading(); err != nil { - return true, err + return zeroRes, true, err } defer store.stopReading() - if done, err := fn(store); done { - return true, err + if res, done, err := fn(store); done { + return res, true, err } } - return false, nil + return zeroRes, false, nil } -// writeToImageStore is a convenience helper for working with store.getImageStore(): +// writeToImageStore is a convenience helper for working with store.imageStore: // It locks the store for writing, checks for updates, and calls fn(), which can then access store.imageStore. // It returns the return value of fn, or its own error initializing the store. -func (s *store) writeToImageStore(fn func() error) error { +func writeToImageStore[T any](s *store, fn func() (T, error)) (T, error) { if err := s.imageStore.startWriting(); err != nil { - return err + var zeroRes T // A zero value of T + return zeroRes, err } defer s.imageStore.stopWriting() return fn() } -// writeToContainerStore is a convenience helper for working with store.getContainerStore(): +// readContainerStore is a convenience helper for working with store.containerStore: +// It locks the store for reading, checks for updates, and calls fn(), which can then access store.containerStore. +// If reading the container store fails, it returns ({}, true, err). +// Returns the return value of fn on success. +func readContainerStore[T any](s *store, fn func() (T, bool, error)) (T, bool, error) { + if err := s.containerStore.startReading(); err != nil { + var zeroRes T // A zero value of T + return zeroRes, true, err + } + defer s.containerStore.stopReading() + return fn() +} + +// writeToContainerStore is a convenience helper for working with store.containerStore: // It locks the store for writing, checks for updates, and calls fn(), which can then access store.containerStore. // It returns the return value of fn, or its own error initializing the store. -func (s *store) writeToContainerStore(fn func() error) error { +func writeToContainerStore[T any](s *store, fn func() (T, error)) (T, error) { if err := s.containerStore.startWriting(); err != nil { - return err + var zeroRes T // A zero value of T + return zeroRes, err } defer s.containerStore.stopWriting() return fn() @@ -1384,91 +1451,88 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, i layer = ilayer.ID } - var options ImageOptions - var namesToAddAfterCreating []string - - if err := s.imageStore.startWriting(); err != nil { - return nil, err - } - defer s.imageStore.stopWriting() + return writeToImageStore(s, func() (*Image, error) { + var options ImageOptions + var namesToAddAfterCreating []string - // Check if the ID refers to an image in a read-only store -- we want - // to allow images in read-only stores to have their names changed, so - // if we find one, merge the new values in with what we know about the - // image that's already there. - if id != "" { - for _, is := range s.roImageStores { - store := is - if err := store.startReading(); err != nil { - return nil, err - } - defer store.stopReading() - if i, err := store.Get(id); err == nil { - // set information about this image in "options" - options = ImageOptions{ - Metadata: i.Metadata, - CreationDate: i.Created, - Digest: i.Digest, - Digests: copyDigestSlice(i.Digests), - NamesHistory: copyStringSlice(i.NamesHistory), + // Check if the ID refers to an image in a read-only store -- we want + // to allow images in read-only stores to have their names changed, so + // if we find one, merge the new values in with what we know about the + // image that's already there. + if id != "" { + for _, is := range s.roImageStores { + store := is + if err := store.startReading(); err != nil { + return nil, err } - for _, key := range i.BigDataNames { - data, err := store.BigData(id, key) - if err != nil { - return nil, err + defer store.stopReading() + if i, err := store.Get(id); err == nil { + // set information about this image in "options" + options = ImageOptions{ + Metadata: i.Metadata, + CreationDate: i.Created, + Digest: i.Digest, + Digests: copyDigestSlice(i.Digests), + NamesHistory: copyStringSlice(i.NamesHistory), } - dataDigest, err := store.BigDataDigest(id, key) - if err != nil { - return nil, err + for _, key := range i.BigDataNames { + data, err := store.BigData(id, key) + if err != nil { + return nil, err + } + dataDigest, err := store.BigDataDigest(id, key) + if err != nil { + return nil, err + } + options.BigData = append(options.BigData, ImageBigDataOption{ + Key: key, + Data: data, + Digest: dataDigest, + }) } - options.BigData = append(options.BigData, ImageBigDataOption{ - Key: key, - Data: data, - Digest: dataDigest, - }) + namesToAddAfterCreating = dedupeStrings(append(append([]string{}, i.Names...), names...)) + break } - namesToAddAfterCreating = dedupeStrings(append(append([]string{}, i.Names...), names...)) - break } } - } - // merge any passed-in options into "options" as best we can - if iOptions != nil { - if !iOptions.CreationDate.IsZero() { - options.CreationDate = iOptions.CreationDate - } - if iOptions.Digest != "" { - options.Digest = iOptions.Digest - } - options.Digests = append(options.Digests, copyDigestSlice(iOptions.Digests)...) - if iOptions.Metadata != "" { - options.Metadata = iOptions.Metadata + // merge any passed-in options into "options" as best we can + if iOptions != nil { + if !iOptions.CreationDate.IsZero() { + options.CreationDate = iOptions.CreationDate + } + if iOptions.Digest != "" { + options.Digest = iOptions.Digest + } + options.Digests = append(options.Digests, copyDigestSlice(iOptions.Digests)...) + if iOptions.Metadata != "" { + options.Metadata = iOptions.Metadata + } + options.BigData = append(options.BigData, copyImageBigDataOptionSlice(iOptions.BigData)...) + options.NamesHistory = append(options.NamesHistory, copyStringSlice(iOptions.NamesHistory)...) + if options.Flags == nil { + options.Flags = make(map[string]interface{}) + } + for k, v := range iOptions.Flags { + options.Flags[k] = v + } } - options.BigData = append(options.BigData, copyImageBigDataOptionSlice(iOptions.BigData)...) - options.NamesHistory = append(options.NamesHistory, copyStringSlice(iOptions.NamesHistory)...) - if options.Flags == nil { - options.Flags = make(map[string]interface{}) + + if options.CreationDate.IsZero() { + options.CreationDate = time.Now().UTC() } - for k, v := range iOptions.Flags { - options.Flags[k] = v + if metadata != "" { + options.Metadata = metadata } - } - - if options.CreationDate.IsZero() { - options.CreationDate = time.Now().UTC() - } - if metadata != "" { - options.Metadata = metadata - } - res, err := s.imageStore.create(id, names, layer, options) - if err == nil && len(namesToAddAfterCreating) > 0 { - // set any names we pulled up from an additional image store, now that we won't be - // triggering a duplicate names error - err = s.imageStore.updateNames(res.ID, namesToAddAfterCreating, addNames) - } - return res, err + res, err := s.imageStore.create(id, names, layer, options) + if err == nil && len(namesToAddAfterCreating) > 0 { + // set any names we pulled up from an additional image store, now that we won't be + // triggering a duplicate names error + err = s.imageStore.updateNames(res.ID, namesToAddAfterCreating, addNames) + } + return res, err + }) } // imageTopLayerForMapping does ??? @@ -1745,16 +1809,14 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat options.Volatile = true } - var container *Container - err = s.writeToContainerStore(func() error { + return writeToContainerStore(s, func() (*Container, error) { options.IDMappingOptions = types.IDMappingOptions{ HostUIDMapping: len(options.UIDMap) == 0, HostGIDMapping: len(options.GIDMap) == 0, UIDMap: copyIDMap(options.UIDMap), GIDMap: copyIDMap(options.GIDMap), } - var err error - container, err = s.containerStore.create(id, names, imageID, layer, &options) + container, err := s.containerStore.create(id, names, imageID, layer, &options) if err != nil || container == nil { if err2 := rlstore.Delete(layer); err2 != nil { if err == nil { @@ -1764,9 +1826,8 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat } } } - return err + return container, err }) - return container, err } func (s *store) SetMetadata(id, metadata string) error { @@ -1785,49 +1846,46 @@ func (s *store) SetMetadata(id, metadata string) error { } func (s *store) Metadata(id string) (string, error) { - var res string - - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) (string, bool, error) { if store.Exists(id) { - var err error - res, err = store.Metadata(id) - return true, err + res, err := store.Metadata(id) + return res, true, err } - return false, nil + return "", false, nil }); done { return res, err } - if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(store roImageStore) (string, bool, error) { if store.Exists(id) { - var err error - res, err = store.Metadata(id) - return true, err + res, err := store.Metadata(id) + return res, true, err } - return false, nil + return "", false, nil }); done { return res, err } - if err := s.containerStore.startReading(); err != nil { - return "", err - } - defer s.containerStore.stopReading() - if s.containerStore.Exists(id) { - return s.containerStore.Metadata(id) + if res, done, err := readContainerStore(s, func() (string, bool, error) { + if s.containerStore.Exists(id) { + res, err := s.containerStore.Metadata(id) + return res, true, err + } + return "", false, nil + }); done { + return res, err } + return "", ErrNotAnID } func (s *store) ListImageBigData(id string) ([]string, error) { - var res []string - if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(store roImageStore) ([]string, bool, error) { bigDataNames, err := store.BigDataNames(id) if err == nil { - res = bigDataNames - return true, nil + return bigDataNames, true, nil } - return false, nil + return nil, false, nil }); done { return res, err } @@ -1835,29 +1893,28 @@ func (s *store) ListImageBigData(id string) ([]string, error) { } func (s *store) ImageBigDataSize(id, key string) (int64, error) { - var res int64 = -1 - if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(store roImageStore) (int64, bool, error) { size, err := store.BigDataSize(id, key) if err == nil { - res = size - return true, nil + return size, true, nil } - return false, nil + return -1, false, nil }); done { - return res, err + if err != nil { + return -1, err + } + return res, nil } return -1, ErrSizeUnknown } func (s *store) ImageBigDataDigest(id, key string) (digest.Digest, error) { - var res digest.Digest - if done, err := s.readAllImageStores(func(ristore roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(ristore roImageStore) (digest.Digest, bool, error) { d, err := ristore.BigDataDigest(id, key) if err == nil && d.Validate() == nil { - res = d - return true, nil + return d, true, nil } - return false, nil + return "", false, nil }); done { return res, err } @@ -1866,17 +1923,15 @@ func (s *store) ImageBigDataDigest(id, key string) (digest.Digest, error) { func (s *store) ImageBigData(id, key string) ([]byte, error) { foundImage := false - var res []byte - if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(store roImageStore) ([]byte, bool, error) { data, err := store.BigData(id, key) if err == nil { - res = data - return true, nil + return data, true, nil } if store.Exists(id) { foundImage = true } - return false, nil + return nil, false, nil }); done { return res, err } @@ -1890,17 +1945,15 @@ func (s *store) ImageBigData(id, key string) ([]byte, error) { // named data associated with an layer. func (s *store) ListLayerBigData(id string) ([]string, error) { foundLayer := false - var res []string - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) ([]string, bool, error) { data, err := store.BigDataNames(id) if err == nil { - res = data - return true, nil + return data, true, nil } if store.Exists(id) { foundLayer = true } - return false, nil + return nil, false, nil }); done { return res, err } @@ -1914,17 +1967,15 @@ func (s *store) ListLayerBigData(id string) ([]string, error) { // associated with a layer. func (s *store) LayerBigData(id, key string) (io.ReadCloser, error) { foundLayer := false - var res io.ReadCloser - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) (io.ReadCloser, bool, error) { data, err := store.BigData(id, key) if err == nil { - res = data - return true, nil + return data, true, nil } if store.Exists(id) { foundLayer = true } - return false, nil + return nil, false, nil }); done { return res, err } @@ -1937,15 +1988,17 @@ func (s *store) LayerBigData(id, key string) (io.ReadCloser, error) { // SetLayerBigData stores a (possibly large) chunk of named data // associated with a layer. func (s *store) SetLayerBigData(id, key string, data io.Reader) error { - return s.writeToLayerStore(func(store rwLayerStore) error { - return store.SetBigData(id, key, data) + _, err := writeToLayerStore(s, func(store rwLayerStore) (struct{}, error) { + return struct{}{}, store.SetBigData(id, key, data) }) + return err } func (s *store) SetImageBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error { - return s.writeToImageStore(func() error { - return s.imageStore.SetBigData(id, key, data, digestManifest) + _, err := writeToImageStore(s, func() (struct{}, error) { + return struct{}{}, s.imageStore.SetBigData(id, key, data, digestManifest) }) + return err } func (s *store) ImageSize(id string) (int64, error) { @@ -2066,12 +2119,11 @@ func (s *store) ContainerSize(id string) (int64, error) { return -1, err } - var res int64 = -1 - err = s.writeToContainerStore(func() error { // Yes, s.containerStore.BigDataSize requires a write lock. + return writeToContainerStore(s, func() (int64, error) { // Yes, s.containerStore.BigDataSize requires a write lock. // Read the container record. container, err := s.containerStore.Get(id) if err != nil { - return err + return -1, err } // Read the container's layer's size. @@ -2081,24 +2133,24 @@ func (s *store) ContainerSize(id string) (int64, error) { if layer, err = store.Get(container.LayerID); err == nil { size, err = store.DiffSize("", layer.ID) if err != nil { - return fmt.Errorf("determining size of layer with ID %q: %w", layer.ID, err) + return -1, fmt.Errorf("determining size of layer with ID %q: %w", layer.ID, err) } break } } if layer == nil { - return fmt.Errorf("locating layer with ID %q: %w", container.LayerID, ErrLayerUnknown) + return -1, fmt.Errorf("locating layer with ID %q: %w", container.LayerID, ErrLayerUnknown) } // Count big data items. names, err := s.containerStore.BigDataNames(id) if err != nil { - return fmt.Errorf("reading list of big data items for container %q: %w", container.ID, err) + return -1, fmt.Errorf("reading list of big data items for container %q: %w", container.ID, err) } for _, name := range names { n, err := s.containerStore.BigDataSize(id, name) if err != nil { - return fmt.Errorf("reading size of big data item %q for container %q: %w", name, id, err) + return -1, fmt.Errorf("reading size of big data item %q for container %q: %w", name, id, err) } size += n } @@ -2106,92 +2158,88 @@ func (s *store) ContainerSize(id string) (int64, error) { // Count the size of our container directory and container run directory. n, err := directory.Size(cdir) if err != nil { - return err + return -1, err } size += n n, err = directory.Size(rdir) if err != nil { - return err + return -1, err } size += n - res = size - return nil + return size, nil }) - return res, err } func (s *store) ListContainerBigData(id string) ([]string, error) { - if err := s.containerStore.startReading(); err != nil { - return nil, err - } - defer s.containerStore.stopReading() - - return s.containerStore.BigDataNames(id) + res, _, err := readContainerStore(s, func() ([]string, bool, error) { + res, err := s.containerStore.BigDataNames(id) + return res, true, err + }) + return res, err } func (s *store) ContainerBigDataSize(id, key string) (int64, error) { - var res int64 = -1 - err := s.writeToContainerStore(func() error { // Yes, BigDataSize requires a write lock. - var err error - res, err = s.containerStore.BigDataSize(id, key) - return err + return writeToContainerStore(s, func() (int64, error) { // Yes, BigDataSize requires a write lock. + return s.containerStore.BigDataSize(id, key) }) - return res, err } func (s *store) ContainerBigDataDigest(id, key string) (digest.Digest, error) { - var res digest.Digest - err := s.writeToContainerStore(func() error { // Yes, BigDataDigest requires a write lock. - var err error - res, err = s.containerStore.BigDataDigest(id, key) - return err + return writeToContainerStore(s, func() (digest.Digest, error) { // Yes, BigDataDigest requires a write lock. + return s.containerStore.BigDataDigest(id, key) }) - return res, err } func (s *store) ContainerBigData(id, key string) ([]byte, error) { - if err := s.containerStore.startReading(); err != nil { - return nil, err - } - defer s.containerStore.stopReading() - return s.containerStore.BigData(id, key) + res, _, err := readContainerStore(s, func() ([]byte, bool, error) { + res, err := s.containerStore.BigData(id, key) + return res, true, err + }) + return res, err } func (s *store) SetContainerBigData(id, key string, data []byte) error { - return s.writeToContainerStore(func() error { - return s.containerStore.SetBigData(id, key, data) + _, err := writeToContainerStore(s, func() (struct{}, error) { + return struct{}{}, s.containerStore.SetBigData(id, key, data) }) + return err } func (s *store) Exists(id string) bool { - var res = false - - if done, _ := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + found, _, err := readAllLayerStores(s, func(store roLayerStore) (bool, bool, error) { if store.Exists(id) { - res = true - return true, nil + return true, true, nil } - return false, nil - }); done { - return res + return false, false, nil + }) + if err != nil { + return false + } + if found { + return true } - if done, _ := s.readAllImageStores(func(store roImageStore) (bool, error) { + found, _, err = readAllImageStores(s, func(store roImageStore) (bool, bool, error) { if store.Exists(id) { - res = true - return true, nil + return true, true, nil } - return false, nil - }); done { - return res + return false, false, nil + }) + if err != nil { + return false + } + if found { + return true } - if err := s.containerStore.startReading(); err != nil { + found, _, err = readContainerStore(s, func() (bool, bool, error) { + return s.containerStore.Exists(id), true, nil + }) + if err != nil { return false } - defer s.containerStore.stopReading() - return s.containerStore.Exists(id) + return found } func dedupeStrings(names []string) []string { @@ -2234,14 +2282,12 @@ func (s *store) RemoveNames(id string, names []string) error { func (s *store) updateNames(id string, names []string, op updateNameOperation) error { deduped := dedupeStrings(names) - layerFound := false - if err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + if found, err := writeToLayerStore(s, func(rlstore rwLayerStore) (bool, error) { if !rlstore.Exists(id) { - return nil + return false, nil } - layerFound = true - return rlstore.updateNames(id, deduped, op) - }); err != nil || layerFound { + return true, rlstore.updateNames(id, deduped, op) + }); err != nil || found { return err } @@ -2295,14 +2341,12 @@ func (s *store) updateNames(id string, names []string, op updateNameOperation) e } } - containerFound := false - if err := s.writeToContainerStore(func() error { + if found, err := writeToContainerStore(s, func() (bool, error) { if !s.containerStore.Exists(id) { - return nil + return false, nil } - containerFound = true - return s.containerStore.updateNames(id, deduped, op) - }); err != nil || containerFound { + return true, s.containerStore.updateNames(id, deduped, op) + }); err != nil || found { return err } @@ -2310,67 +2354,63 @@ func (s *store) updateNames(id string, names []string, op updateNameOperation) e } func (s *store) Names(id string) ([]string, error) { - var res []string - - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) ([]string, bool, error) { if l, err := store.Get(id); l != nil && err == nil { - res = l.Names - return true, nil + return l.Names, true, nil } - return false, nil + return nil, false, nil }); done { return res, err } - if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(store roImageStore) ([]string, bool, error) { if i, err := store.Get(id); i != nil && err == nil { - res = i.Names - return true, nil + return i.Names, true, nil } - return false, nil + return nil, false, nil }); done { return res, err } - if err := s.containerStore.startReading(); err != nil { - return nil, err - } - defer s.containerStore.stopReading() - if c, err := s.containerStore.Get(id); c != nil && err == nil { - return c.Names, nil + if res, done, err := readContainerStore(s, func() ([]string, bool, error) { + if c, err := s.containerStore.Get(id); c != nil && err == nil { + return c.Names, true, nil + } + return nil, false, nil + }); done { + return res, err } + return nil, ErrLayerUnknown + } func (s *store) Lookup(name string) (string, error) { - var res string - - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) (string, bool, error) { if l, err := store.Get(name); l != nil && err == nil { - res = l.ID - return true, nil + return l.ID, true, nil } - return false, nil + return "", false, nil }); done { return res, err } - if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(store roImageStore) (string, bool, error) { if i, err := store.Get(name); i != nil && err == nil { - res = i.ID - return true, nil + return i.ID, true, nil } - return false, nil + return "", false, nil }); done { return res, err } - if err := s.containerStore.startReading(); err != nil { - return "", err - } - defer s.containerStore.stopReading() - if c, err := s.containerStore.Get(name); c != nil && err == nil { - return c.ID, nil + if res, done, err := readContainerStore(s, func() (string, bool, error) { + if c, err := s.containerStore.Get(name); c != nil && err == nil { + return c.ID, true, nil + } + return "", false, nil + }); done { + return res, err } return "", ErrLayerUnknown @@ -2430,8 +2470,22 @@ func (s *store) DeleteLayer(id string) error { func (s *store) DeleteImage(id string, commit bool) (layers []string, err error) { layersToRemove := []string{} if err := s.writeToAllStores(func(rlstore rwLayerStore) error { - if s.imageStore.Exists(id) { - image, err := s.imageStore.Get(id) + // Delete image from all available imagestores configured to be used. + imageFound := false + for _, is := range append([]rwImageStore{s.imageStore}, s.rwImageStores...) { + if is != s.imageStore { + // This is an additional writeable image store + // so we must perform lock + if err := is.startWriting(); err != nil { + return err + } + defer is.stopWriting() + } + if !is.Exists(id) { + continue + } + imageFound = true + image, err := is.Get(id) if err != nil { return err } @@ -2447,7 +2501,7 @@ func (s *store) DeleteImage(id string, commit bool) (layers []string, err error) if container, ok := aContainerByImage[id]; ok { return fmt.Errorf("image used by %v: %w", container, ErrImageUsedByContainer) } - images, err := s.imageStore.Images() + images, err := is.Images() if err != nil { return err } @@ -2469,7 +2523,7 @@ func (s *store) DeleteImage(id string, commit bool) (layers []string, err error) } } if commit { - if err = s.imageStore.Delete(id); err != nil { + if err = is.Delete(id); err != nil { return err } } @@ -2514,7 +2568,8 @@ func (s *store) DeleteImage(id string, commit bool) (layers []string, err error) layersToRemoveMap[layer] = struct{}{} layer = parent } - } else { + } + if !imageFound { return ErrNotAnImage } if commit { @@ -2542,63 +2597,45 @@ func (s *store) DeleteContainer(id string) error { return ErrNotAContainer } - errChan := make(chan error) - var wg sync.WaitGroup - + // delete the layer first, separately, so that if we get an + // error while trying to do so, we don't go ahead and delete + // the container record that refers to it, effectively losing + // track of it if rlstore.Exists(container.LayerID) { - wg.Add(1) - go func() { - errChan <- rlstore.Delete(container.LayerID) - wg.Done() - }() - } - wg.Add(1) - go func() { - errChan <- s.containerStore.Delete(id) - wg.Done() - }() + if err := rlstore.Delete(container.LayerID); err != nil { + return err + } + } + + var wg multierror.Group + wg.Go(func() error { return s.containerStore.Delete(id) }) middleDir := s.graphDriverName + "-containers" - gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID) - wg.Add(1) - go func() { - defer wg.Done() + + wg.Go(func() error { + gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID) // attempt a simple rm -rf first - err := os.RemoveAll(gcpath) - if err == nil { - errChan <- nil - return + if err := os.RemoveAll(gcpath); err == nil { + return nil } // and if it fails get to the more complicated cleanup - errChan <- system.EnsureRemoveAll(gcpath) - }() + return system.EnsureRemoveAll(gcpath) + }) - rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID) - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() error { + rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID) // attempt a simple rm -rf first - err := os.RemoveAll(rcpath) - if err == nil { - errChan <- nil - return + if err := os.RemoveAll(rcpath); err == nil { + return nil } // and if it fails get to the more complicated cleanup - errChan <- system.EnsureRemoveAll(rcpath) - }() - - go func() { - wg.Wait() - close(errChan) - }() + return system.EnsureRemoveAll(rcpath) + }) - var errors []error - for err := range errChan { - if err != nil { - errors = append(errors, err) - } + if multierr := wg.Wait(); multierr != nil { + return multierr.ErrorOrNil() } - return multierror.Append(nil, errors...).ErrorOrNil() + return nil }) } @@ -2756,27 +2793,21 @@ func (s *store) Unmount(id string, force bool) (bool, error) { if layerID, err := s.ContainerLayerID(id); err == nil { id = layerID } - var res bool - err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + return writeToLayerStore(s, func(rlstore rwLayerStore) (bool, error) { if rlstore.Exists(id) { - var err error - res, err = rlstore.unmount(id, force, false) - return err + return rlstore.unmount(id, force, false) } - return ErrLayerUnknown + return false, ErrLayerUnknown }) - return res, err } func (s *store) Changes(from, to string) ([]archive.Change, error) { - var res []archive.Change - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) ([]archive.Change, bool, error) { if store.Exists(to) { - var err error - res, err = store.Changes(from, to) - return true, err + res, err := store.Changes(from, to) + return res, true, err } - return false, nil + return nil, false, nil }); done { return res, err } @@ -2784,16 +2815,17 @@ func (s *store) Changes(from, to string) ([]archive.Change, error) { } func (s *store) DiffSize(from, to string) (int64, error) { - var res int64 = -1 - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) (int64, bool, error) { if store.Exists(to) { - var err error - res, err = store.DiffSize(from, to) - return true, err + res, err := store.DiffSize(from, to) + return res, true, err } - return false, nil + return -1, false, nil }); done { - return res, err + if err != nil { + return -1, err + } + return res, nil } return -1, ErrLayerUnknown } @@ -2837,71 +2869,61 @@ func (s *store) Diff(from, to string, options *DiffOptions) (io.ReadCloser, erro } func (s *store) ApplyDiffFromStagingDirectory(to, stagingDirectory string, diffOutput *drivers.DriverWithDifferOutput, options *drivers.ApplyDiffOpts) error { - return s.writeToLayerStore(func(rlstore rwLayerStore) error { + _, err := writeToLayerStore(s, func(rlstore rwLayerStore) (struct{}, error) { if !rlstore.Exists(to) { - return ErrLayerUnknown + return struct{}{}, ErrLayerUnknown } - return rlstore.ApplyDiffFromStagingDirectory(to, stagingDirectory, diffOutput, options) + return struct{}{}, rlstore.ApplyDiffFromStagingDirectory(to, stagingDirectory, diffOutput, options) }) + return err } func (s *store) CleanupStagingDirectory(stagingDirectory string) error { - return s.writeToLayerStore(func(rlstore rwLayerStore) error { - return rlstore.CleanupStagingDirectory(stagingDirectory) + _, err := writeToLayerStore(s, func(rlstore rwLayerStore) (struct{}, error) { + return struct{}{}, rlstore.CleanupStagingDirectory(stagingDirectory) }) + return err } func (s *store) ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffOpts, differ drivers.Differ) (*drivers.DriverWithDifferOutput, error) { - var res *drivers.DriverWithDifferOutput - err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + return writeToLayerStore(s, func(rlstore rwLayerStore) (*drivers.DriverWithDifferOutput, error) { if to != "" && !rlstore.Exists(to) { - return ErrLayerUnknown + return nil, ErrLayerUnknown } - var err error - res, err = rlstore.ApplyDiffWithDiffer(to, options, differ) - return err + return rlstore.ApplyDiffWithDiffer(to, options, differ) }) - return res, err } func (s *store) DifferTarget(id string) (string, error) { - var res string - err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + return writeToLayerStore(s, func(rlstore rwLayerStore) (string, error) { if rlstore.Exists(id) { - var err error - res, err = rlstore.DifferTarget(id) - return err + return rlstore.DifferTarget(id) } - return ErrLayerUnknown + return "", ErrLayerUnknown }) - return res, err } func (s *store) ApplyDiff(to string, diff io.Reader) (int64, error) { - var res int64 = -1 - err := s.writeToLayerStore(func(rlstore rwLayerStore) error { + return writeToLayerStore(s, func(rlstore rwLayerStore) (int64, error) { if rlstore.Exists(to) { - var err error - res, err = rlstore.ApplyDiff(to, diff) - return err + return rlstore.ApplyDiff(to, diff) } - return ErrLayerUnknown + return -1, ErrLayerUnknown }) - return res, err } func (s *store) layersByMappedDigest(m func(roLayerStore, digest.Digest) ([]Layer, error), d digest.Digest) ([]Layer, error) { var layers []Layer - if _, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if _, _, err := readAllLayerStores(s, func(store roLayerStore) (struct{}, bool, error) { storeLayers, err := m(store, d) if err != nil { if !errors.Is(err, ErrLayerUnknown) { - return true, err + return struct{}{}, true, err } - return false, nil + return struct{}{}, false, nil } layers = append(layers, storeLayers...) - return false, nil + return struct{}{}, false, nil }); err != nil { return nil, err } @@ -2926,16 +2948,17 @@ func (s *store) LayersByUncompressedDigest(d digest.Digest) ([]Layer, error) { } func (s *store) LayerSize(id string) (int64, error) { - var res int64 = -1 - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) (int64, bool, error) { if store.Exists(id) { - var err error - res, err = store.Size(id) - return true, err + res, err := store.Size(id) + return res, true, err } - return false, nil + return -1, false, nil }); done { - return res, err + if err != nil { + return -1, err + } + return res, nil } return -1, ErrLayerUnknown } @@ -2980,13 +3003,13 @@ func (s *store) ContainerParentOwners(id string) ([]int, []int, error) { func (s *store) Layers() ([]Layer, error) { var layers []Layer - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if _, done, err := readAllLayerStores(s, func(store roLayerStore) (struct{}, bool, error) { storeLayers, err := store.Layers() if err != nil { - return true, err + return struct{}{}, true, err } layers = append(layers, storeLayers...) - return false, nil + return struct{}{}, false, nil }); done { return nil, err } @@ -2995,13 +3018,13 @@ func (s *store) Layers() ([]Layer, error) { func (s *store) Images() ([]Image, error) { var images []Image - if _, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if _, _, err := readAllImageStores(s, func(store roImageStore) (struct{}, bool, error) { storeImages, err := store.Images() if err != nil { - return true, err + return struct{}{}, true, err } images = append(images, storeImages...) - return false, nil + return struct{}{}, false, nil }); err != nil { return nil, err } @@ -3009,23 +3032,20 @@ func (s *store) Images() ([]Image, error) { } func (s *store) Containers() ([]Container, error) { - if err := s.containerStore.startReading(); err != nil { - return nil, err - } - defer s.containerStore.stopReading() - - return s.containerStore.Containers() + res, _, err := readContainerStore(s, func() ([]Container, bool, error) { + res, err := s.containerStore.Containers() + return res, true, err + }) + return res, err } func (s *store) Layer(id string) (*Layer, error) { - var res *Layer - if done, err := s.readAllLayerStores(func(store roLayerStore) (bool, error) { + if res, done, err := readAllLayerStores(s, func(store roLayerStore) (*Layer, bool, error) { layer, err := store.Get(id) if err == nil { - res = layer - return true, nil + return layer, true, nil } - return false, nil + return nil, false, nil }); done { return res, err } @@ -3119,8 +3139,7 @@ func (al *additionalLayer) Release() { } func (s *store) Image(id string) (*Image, error) { - var res *Image - if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if res, done, err := readAllImageStores(s, func(store roImageStore) (*Image, bool, error) { image, err := store.Get(id) if err == nil { if store != s.imageStore { @@ -3130,13 +3149,12 @@ func (s *store) Image(id string) (*Image, error) { // store, but we have an entry with the same ID in the read-write store, // then the name was removed when we duplicated the image's // record into writable storage, so we should ignore this entry - return false, nil + return nil, false, nil } } - res = image - return true, nil + return image, true, nil } - return false, nil + return nil, false, nil }); done { return res, err } @@ -3150,10 +3168,10 @@ func (s *store) ImagesByTopLayer(id string) ([]*Image, error) { } images := []*Image{} - if _, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if _, _, err := readAllImageStores(s, func(store roImageStore) (struct{}, bool, error) { imageList, err := store.Images() if err != nil { - return true, err + return struct{}{}, true, err } for _, image := range imageList { image := image @@ -3161,7 +3179,7 @@ func (s *store) ImagesByTopLayer(id string) ([]*Image, error) { images = append(images, &image) } } - return false, nil + return struct{}{}, false, nil }); err != nil { return nil, err } @@ -3170,13 +3188,13 @@ func (s *store) ImagesByTopLayer(id string) ([]*Image, error) { func (s *store) ImagesByDigest(d digest.Digest) ([]*Image, error) { images := []*Image{} - if _, err := s.readAllImageStores(func(store roImageStore) (bool, error) { + if _, _, err := readAllImageStores(s, func(store roImageStore) (struct{}, bool, error) { imageList, err := store.ByDigest(d) if err != nil && !errors.Is(err, ErrImageUnknown) { - return true, err + return struct{}{}, true, err } images = append(images, imageList...) - return false, nil + return struct{}{}, false, nil }); err != nil { return nil, err } @@ -3184,20 +3202,18 @@ func (s *store) ImagesByDigest(d digest.Digest) ([]*Image, error) { } func (s *store) Container(id string) (*Container, error) { - if err := s.containerStore.startReading(); err != nil { - return nil, err - } - defer s.containerStore.stopReading() - - return s.containerStore.Get(id) + res, _, err := readContainerStore(s, func() (*Container, bool, error) { + res, err := s.containerStore.Get(id) + return res, true, err + }) + return res, err } func (s *store) ContainerLayerID(id string) (string, error) { - if err := s.containerStore.startReading(); err != nil { - return "", err - } - defer s.containerStore.stopReading() - container, err := s.containerStore.Get(id) + container, _, err := readContainerStore(s, func() (*Container, bool, error) { + res, err := s.containerStore.Get(id) + return res, true, err + }) if err != nil { return "", err } @@ -3209,11 +3225,10 @@ func (s *store) ContainerByLayer(id string) (*Container, error) { if err != nil { return nil, err } - if err := s.containerStore.startReading(); err != nil { - return nil, err - } - defer s.containerStore.stopReading() - containerList, err := s.containerStore.Containers() + containerList, _, err := readContainerStore(s, func() ([]Container, bool, error) { + res, err := s.containerStore.Containers() + return res, true, err + }) if err != nil { return nil, err } @@ -3227,41 +3242,37 @@ func (s *store) ContainerByLayer(id string) (*Container, error) { } func (s *store) ContainerDirectory(id string) (string, error) { - if err := s.containerStore.startReading(); err != nil { - return "", err - } - defer s.containerStore.stopReading() - - id, err := s.containerStore.Lookup(id) - if err != nil { - return "", err - } + res, _, err := readContainerStore(s, func() (string, bool, error) { + id, err := s.containerStore.Lookup(id) + if err != nil { + return "", true, err + } - middleDir := s.graphDriverName + "-containers" - gcpath := filepath.Join(s.GraphRoot(), middleDir, id, "userdata") - if err := os.MkdirAll(gcpath, 0700); err != nil { - return "", err - } - return gcpath, nil + middleDir := s.graphDriverName + "-containers" + gcpath := filepath.Join(s.GraphRoot(), middleDir, id, "userdata") + if err := os.MkdirAll(gcpath, 0700); err != nil { + return "", true, err + } + return gcpath, true, nil + }) + return res, err } func (s *store) ContainerRunDirectory(id string) (string, error) { - if err := s.containerStore.startReading(); err != nil { - return "", err - } - defer s.containerStore.stopReading() - - id, err := s.containerStore.Lookup(id) - if err != nil { - return "", err - } + res, _, err := readContainerStore(s, func() (string, bool, error) { + id, err := s.containerStore.Lookup(id) + if err != nil { + return "", true, err + } - middleDir := s.graphDriverName + "-containers" - rcpath := filepath.Join(s.RunRoot(), middleDir, id, "userdata") - if err := os.MkdirAll(rcpath, 0700); err != nil { - return "", err - } - return rcpath, nil + middleDir := s.graphDriverName + "-containers" + rcpath := filepath.Join(s.RunRoot(), middleDir, id, "userdata") + if err := os.MkdirAll(rcpath, 0700); err != nil { + return "", true, err + } + return rcpath, true, nil + }) + return res, err } func (s *store) SetContainerDirectoryFile(id, file string, data []byte) error { @@ -3541,19 +3552,19 @@ func (s *store) Free() { // Tries to clean up old unreferenced container leftovers. returns the first error // but continues as far as it can func (s *store) GarbageCollect() error { - firstErr := s.writeToContainerStore(func() error { - return s.containerStore.GarbageCollect() + _, firstErr := writeToContainerStore(s, func() (struct{}, error) { + return struct{}{}, s.containerStore.GarbageCollect() }) - moreErr := s.writeToImageStore(func() error { - return s.imageStore.GarbageCollect() + _, moreErr := writeToImageStore(s, func() (struct{}, error) { + return struct{}{}, s.imageStore.GarbageCollect() }) if firstErr == nil { firstErr = moreErr } - moreErr = s.writeToLayerStore(func(rlstore rwLayerStore) error { - return rlstore.GarbageCollect() + _, moreErr = writeToLayerStore(s, func(rlstore rwLayerStore) (struct{}, error) { + return struct{}{}, rlstore.GarbageCollect() }) if firstErr == nil { firstErr = moreErr diff --git a/vendor/github.com/containers/storage/types/errors.go b/vendor/github.com/containers/storage/types/errors.go index dc6ee3e0c9..845b14eed7 100644 --- a/vendor/github.com/containers/storage/types/errors.go +++ b/vendor/github.com/containers/storage/types/errors.go @@ -59,4 +59,41 @@ var ( ErrInvalidMappings = errors.New("invalid mappings specified") // ErrNoAvailableIDs is returned when there are not enough unused IDS within the user namespace. ErrNoAvailableIDs = errors.New("not enough unused IDs in user namespace") + + // ErrLayerUnaccounted describes a layer that is present in the lower-level storage driver, + // but which is not known to or managed by the higher-level driver-agnostic logic. + ErrLayerUnaccounted = errors.New("layer in lower level storage driver not accounted for") + // ErrLayerUnreferenced describes a layer which is not used by any image or container. + ErrLayerUnreferenced = errors.New("layer not referenced by any images or containers") + // ErrLayerIncorrectContentDigest describes a layer for which the contents of one or more + // files which were added in the layer appear to have changed. It may instead look like an + // unnamed "file integrity checksum failed" error. + ErrLayerIncorrectContentDigest = errors.New("layer content incorrect digest") + // ErrLayerIncorrectContentSize describes a layer for which regenerating the diff that was + // used to populate the layer produced a diff of a different size. We check the digest + // first, so it's highly unlikely you'll ever see this error. + ErrLayerIncorrectContentSize = errors.New("layer content incorrect size") + // ErrLayerContentModified describes a layer which contains contents which should not be + // there, or for which ownership/permissions/dates have been changed. + ErrLayerContentModified = errors.New("layer content modified") + // ErrLayerDataMissing describes a layer which is missing a big data item. + ErrLayerDataMissing = errors.New("layer data item is missing") + // ErrLayerMissing describes a layer which is the missing parent of a layer. + ErrLayerMissing = errors.New("layer is missing") + // ErrImageLayerMissing describes an image which claims to have a layer that we don't know + // about. + ErrImageLayerMissing = errors.New("image layer is missing") + // ErrImageDataMissing describes an image which is missing a big data item. + ErrImageDataMissing = errors.New("image data item is missing") + // ErrImageDataIncorrectSize describes an image which has a big data item which looks like + // its size has changed, likely because it's been modified somehow. + ErrImageDataIncorrectSize = errors.New("image data item has incorrect size") + // ErrContainerImageMissing describes a container which claims to be based on an image that + // we don't know about. + ErrContainerImageMissing = errors.New("image missing") + // ErrContainerDataMissing describes a container which is missing a big data item. + ErrContainerDataMissing = errors.New("container data item is missing") + // ErrContainerDataIncorrectSize describes a container which has a big data item which looks + // like its size has changed, likely because it's been modified somehow. + ErrContainerDataIncorrectSize = errors.New("container data item has incorrect size") ) diff --git a/vendor/github.com/containers/storage/types/options.go b/vendor/github.com/containers/storage/types/options.go index 7189a8e6a4..800d0b6377 100644 --- a/vendor/github.com/containers/storage/types/options.go +++ b/vendor/github.com/containers/storage/types/options.go @@ -21,6 +21,7 @@ type TomlConfig struct { Driver string `toml:"driver,omitempty"` DriverPriority []string `toml:"driver_priority,omitempty"` RunRoot string `toml:"runroot,omitempty"` + ImageStore string `toml:"imagestore,omitempty"` GraphRoot string `toml:"graphroot,omitempty"` RootlessStoragePath string `toml:"rootless_storage_path,omitempty"` TransientStore bool `toml:"transient_store,omitempty"` @@ -215,6 +216,10 @@ type StoreOptions struct { // GraphRoot is the filesystem path under which we will store the // contents of layers, images, and containers. GraphRoot string `json:"root,omitempty"` + // Image Store is the location of image store which is seperated from the + // container store. Usually this is not recommended unless users wants + // seperate store for image and containers. + ImageStore string `json:"imagestore,omitempty"` // RootlessStoragePath is the storage path for rootless users // default $HOME/.local/share/containers/storage RootlessStoragePath string `toml:"rootless_storage_path"` @@ -305,7 +310,11 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti } if opts.GraphDriverName == "" { if len(systemOpts.GraphDriverPriority) == 0 { - opts.GraphDriverName = "vfs" + if canUseRootlessOverlay(opts.GraphRoot, opts.RunRoot) { + opts.GraphDriverName = overlayDriver + } else { + opts.GraphDriverName = "vfs" + } } else { opts.GraphDriverPriority = systemOpts.GraphDriverPriority } @@ -405,6 +414,9 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro if config.Storage.GraphRoot != "" { storeOptions.GraphRoot = config.Storage.GraphRoot } + if config.Storage.ImageStore != "" { + storeOptions.ImageStore = config.Storage.ImageStore + } if config.Storage.RootlessStoragePath != "" { storeOptions.RootlessStoragePath = config.Storage.RootlessStoragePath } diff --git a/vendor/github.com/containers/storage/types/options_darwin.go b/vendor/github.com/containers/storage/types/options_darwin.go index eed1a3d94b..3c8ff4e4e8 100644 --- a/vendor/github.com/containers/storage/types/options_darwin.go +++ b/vendor/github.com/containers/storage/types/options_darwin.go @@ -11,3 +11,8 @@ const ( var ( defaultOverrideConfigFile = "/etc/containers/storage.conf" ) + +// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers +func canUseRootlessOverlay(home, runhome string) bool { + return false +} diff --git a/vendor/github.com/containers/storage/types/options_freebsd.go b/vendor/github.com/containers/storage/types/options_freebsd.go index afb7ec6b4a..be2bc2f27d 100644 --- a/vendor/github.com/containers/storage/types/options_freebsd.go +++ b/vendor/github.com/containers/storage/types/options_freebsd.go @@ -12,3 +12,8 @@ const ( var ( defaultOverrideConfigFile = "/usr/local/etc/containers/storage.conf" ) + +// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers +func canUseRootlessOverlay(home, runhome string) bool { + return false +} diff --git a/vendor/github.com/containers/storage/types/options_linux.go b/vendor/github.com/containers/storage/types/options_linux.go index d44aaf76a3..a28e82883c 100644 --- a/vendor/github.com/containers/storage/types/options_linux.go +++ b/vendor/github.com/containers/storage/types/options_linux.go @@ -1,5 +1,13 @@ package types +import ( + "os/exec" + "strconv" + "strings" + + "golang.org/x/sys/unix" +) + const ( // these are default path for run and graph root for rootful users // for rootless path is constructed via getRootlessStorageOpts @@ -12,3 +20,33 @@ const ( var ( defaultOverrideConfigFile = "/etc/containers/storage.conf" ) + +// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers +func canUseRootlessOverlay(home, runhome string) bool { + // we check first for fuse-overlayfs since it is cheaper. + if path, _ := exec.LookPath("fuse-overlayfs"); path != "" { + return true + } + + // We cannot use overlay.SupportsNativeOverlay since canUseRootlessOverlay is called by Podman + // before we enter the user namespace and the driver we pick here is written in the podman database. + // Checking the kernel version is usually not a good idea since the feature could be back-ported, e.g. RHEL + // but this is just an heuristic and on RHEL we always install the storage.conf file. + // native overlay for rootless was added upstream in 5.13 (at least the first version that we support), so check + // that the kernel is >= 5.13. + var uts unix.Utsname + if err := unix.Uname(&uts); err == nil { + parts := strings.Split(string(uts.Release[:]), ".") + major, _ := strconv.Atoi(parts[0]) + if major >= 6 { + return true + } + if major == 5 && len(parts) > 1 { + minor, _ := strconv.Atoi(parts[1]) + if minor >= 13 { + return true + } + } + } + return false +} diff --git a/vendor/github.com/containers/storage/types/options_windows.go b/vendor/github.com/containers/storage/types/options_windows.go index d44aaf76a3..c1bea9fac0 100644 --- a/vendor/github.com/containers/storage/types/options_windows.go +++ b/vendor/github.com/containers/storage/types/options_windows.go @@ -12,3 +12,8 @@ const ( var ( defaultOverrideConfigFile = "/etc/containers/storage.conf" ) + +// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers +func canUseRootlessOverlay(home, runhome string) bool { + return false +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2a894002f9..4f10351b26 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -125,7 +125,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.53.1-0.20230506101404-3e93a76d461c +# github.com/containers/common v0.53.1-0.20230508125333-8d4783167b58 ## explicit; go 1.18 github.com/containers/common/libimage github.com/containers/common/libimage/define @@ -286,7 +286,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.46.1 +# github.com/containers/storage v1.46.2-0.20230508110141-51c23d59f8f3 ## explicit; go 1.18 github.com/containers/storage github.com/containers/storage/drivers