Merge pull request #2155 from containers/renovate/github.com-containers-storage-1.x

fix(deps): update module github.com/containers/storage to v1.51.0
This commit is contained in:
Miloslav Trmač 2023-11-16 18:14:56 +01:00 committed by GitHub
commit 313342bdf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 683 additions and 780 deletions

11
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/containers/common v0.56.1-0.20231101110448-3e5caa093e79
github.com/containers/image/v5 v5.28.0
github.com/containers/ocicrypt v1.1.9
github.com/containers/storage v1.50.2
github.com/containers/storage v1.51.0
github.com/docker/distribution v2.8.3+incompatible
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc5
@ -25,14 +25,13 @@ require (
dario.cat/mergo v1.0.0 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.12.0-rc.0 // indirect
github.com/Microsoft/hcsshim v0.12.0-rc.1 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/containerd/cgroups/v3 v3.0.2 // indirect
github.com/containerd/containerd v1.7.8 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/coreos/go-oidc/v3 v3.6.0 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20230710064741-aa7fe85c7dbd // indirect
@ -81,11 +80,11 @@ require (
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/sys/mountinfo v0.7.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/runc v1.1.9 // indirect
github.com/opencontainers/runc v1.1.10 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect

22
go.sum
View File

@ -8,8 +8,8 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.12.0-rc.0 h1:wX/F5huJxH9APBkhKSEAqaiZsuBvbbDnyBROZAqsSaY=
github.com/Microsoft/hcsshim v0.12.0-rc.0/go.mod h1:rvOnw3YlfoNnEp45wReUngvsXbwRW+AFQ10GVjG1kMU=
github.com/Microsoft/hcsshim v0.12.0-rc.1 h1:Hy+xzYujv7urO5wrgcG58SPMOXNLrj4WCJbySs2XX/A=
github.com/Microsoft/hcsshim v0.12.0-rc.1/go.mod h1:Y1a1S0QlYp1mBpyvGiuEdOfZqnao+0uX5AWHXQ5NhZU=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
@ -28,10 +28,8 @@ github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKk
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
github.com/containerd/containerd v1.7.8 h1:RkwgOW3AVUT3H/dyT0W03Dc8AzlpMG65lX48KftOFSM=
github.com/containerd/containerd v1.7.8/go.mod h1:L/Hn9qylJtUFT7cPeM0Sr3fATj+WjHwRQ0lyrYk3OPY=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/containers/common v0.56.1-0.20231101110448-3e5caa093e79 h1:ECmubgcAF/lT8BHT29ZGu0hBqru8qpNY85HHFATa1wc=
github.com/containers/common v0.56.1-0.20231101110448-3e5caa093e79/go.mod h1:WOhqTBT+2qSHz86CYZ+4LYAkcM9kSQJ7ZobNu0AGJPU=
github.com/containers/image/v5 v5.28.0 h1:H4cWbdI88UA/mDb6SxMo3IxpmS1BSs/Kifvhwt9g048=
@ -40,8 +38,8 @@ github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYgle
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.1.9 h1:2Csfba4jse85Raxk5HIyEk8OwZNjRvfkhEGijOjIdEM=
github.com/containers/ocicrypt v1.1.9/go.mod h1:dTKx1918d8TDkxXvarscpNVY+lyPakPNFN4jwA9GBys=
github.com/containers/storage v1.50.2 h1:Fys4BjFUVNRBEXlO70hFI48VW4EXsgnGisTpk9tTMsE=
github.com/containers/storage v1.50.2/go.mod h1:dpspZsUrcKD8SpTofvKWhwPDHD0MkO4Q7VE+oYdWkiA=
github.com/containers/storage v1.51.0 h1:AowbcpiWXzAjHosKz7MKvPEqpyX+ryZA/ZurytRrFNA=
github.com/containers/storage v1.51.0/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyObeKJzVtkUlfc=
github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o=
github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@ -257,8 +255,8 @@ github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@ -278,8 +276,8 @@ github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/image-tools v1.0.0-rc3 h1:ZR837lBIxq6mmwEqfYrbLMuf75eBSHhccVHy6lsBeM4=
github.com/opencontainers/image-tools v1.0.0-rc3/go.mod h1:A9btVpZLzttF4iFaKNychhPyrhfOjJ1OF5KrA8GcLj4=
github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM=
github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40=
github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M=
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=

View File

@ -12,14 +12,16 @@ import (
"syscall"
"time"
"go.opencensus.io/trace"
"github.com/Microsoft/hcsshim/internal/cow"
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/oc"
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
"github.com/Microsoft/hcsshim/internal/vmcompute"
"go.opencensus.io/trace"
)
// ContainerError is an error encountered in HCS
type Process struct {
handleLock sync.RWMutex
handle vmcompute.HcsProcess
@ -50,35 +52,6 @@ func newProcess(process vmcompute.HcsProcess, processID int, computeSystem *Syst
}
}
type processModifyRequest struct {
Operation string
ConsoleSize *consoleSize `json:",omitempty"`
CloseHandle *closeHandle `json:",omitempty"`
}
type consoleSize struct {
Height uint16
Width uint16
}
type closeHandle struct {
Handle string
}
type processStatus struct {
ProcessID uint32
Exited bool
ExitCode uint32
LastWaitResult int32
}
const stdIn string = "StdIn"
const (
modifyConsoleSize string = "ConsoleSize"
modifyCloseHandle string = "CloseHandle"
)
// Pid returns the process ID of the process within the container.
func (process *Process) Pid() int {
return process.processID
@ -260,14 +233,14 @@ func (process *Process) waitBackground() {
process.handleLock.RLock()
defer process.handleLock.RUnlock()
// Make sure we didnt race with Close() here
// Make sure we didn't race with Close() here
if process.handle != 0 {
propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle)
events := processHcsResult(ctx, resultJSON)
if err != nil {
err = makeProcessError(process, operation, err, events)
} else {
properties := &processStatus{}
properties := &hcsschema.ProcessStatus{}
err = json.Unmarshal([]byte(propertiesJSON), properties)
if err != nil {
err = makeProcessError(process, operation, err, nil)
@ -318,10 +291,9 @@ func (process *Process) ResizeConsole(ctx context.Context, width, height uint16)
if process.handle == 0 {
return makeProcessError(process, operation, ErrAlreadyClosed, nil)
}
modifyRequest := processModifyRequest{
Operation: modifyConsoleSize,
ConsoleSize: &consoleSize{
modifyRequest := hcsschema.ProcessModifyRequest{
Operation: guestrequest.ModifyProcessConsoleSize,
ConsoleSize: &hcsschema.ConsoleSize{
Height: height,
Width: width,
},
@ -421,18 +393,12 @@ func (process *Process) CloseStdin(ctx context.Context) (err error) {
return makeProcessError(process, operation, ErrAlreadyClosed, nil)
}
process.stdioLock.Lock()
defer process.stdioLock.Unlock()
if process.stdin == nil {
return nil
}
//HcsModifyProcess request to close stdin will fail if the process has already exited
if !process.stopped() {
modifyRequest := processModifyRequest{
Operation: modifyCloseHandle,
CloseHandle: &closeHandle{
Handle: stdIn,
modifyRequest := hcsschema.ProcessModifyRequest{
Operation: guestrequest.CloseProcessHandle,
CloseHandle: &hcsschema.CloseHandle{
Handle: guestrequest.STDInHandle,
},
}
@ -448,8 +414,12 @@ func (process *Process) CloseStdin(ctx context.Context) (err error) {
}
}
process.stdin.Close()
process.stdin = nil
process.stdioLock.Lock()
defer process.stdioLock.Unlock()
if process.stdin != nil {
process.stdin.Close()
process.stdin = nil
}
return nil
}

View File

@ -9,6 +9,8 @@
package hcsschema
import "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
type CloseHandle struct {
Handle string `json:"Handle,omitempty"`
Handle guestrequest.STDIOHandle `json:"Handle,omitempty"` // NOTE: Swagger generated as string. Locally updated.
}

View File

@ -9,8 +9,11 @@
package hcsschema
type ConsoleSize struct {
Height int32 `json:"Height,omitempty"`
// NOTE: Swagger generated fields as int32. Locally updated to uint16 to match documentation.
// https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#ConsoleSize
Width int32 `json:"Width,omitempty"`
type ConsoleSize struct {
Height uint16 `json:"Height,omitempty"`
Width uint16 `json:"Width,omitempty"`
}

View File

@ -9,9 +9,11 @@
package hcsschema
// Passed to HcsRpc_ModifyProcess
import "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
// Passed to HcsRpc_ModifyProcess
type ProcessModifyRequest struct {
Operation string `json:"Operation,omitempty"`
Operation guestrequest.ProcessModifyOperation `json:"Operation,omitempty"` // NOTE: Swagger generated as string. Locally updated.
ConsoleSize *ConsoleSize `json:"ConsoleSize,omitempty"`

View File

@ -9,13 +9,16 @@
package hcsschema
// Status of a process running in a container
// NOTE: Swagger generated fields as int32. Locally updated to uint16 to match documentation.
// https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#ConsoleSize
// Status of a process running in a container
type ProcessStatus struct {
ProcessId int32 `json:"ProcessId,omitempty"`
ProcessId uint32 `json:"ProcessId,omitempty"` // NOTE: Swagger generated as int32. Locally updated to match documentation.
Exited bool `json:"Exited,omitempty"`
ExitCode int32 `json:"ExitCode,omitempty"`
ExitCode uint32 `json:"ExitCode,omitempty"` // NOTE: Swagger generated as int32. Locally updated to match documentation.
LastWaitResult int32 `json:"LastWaitResult,omitempty"`
}

View File

@ -304,11 +304,22 @@ func (computeSystem *System) WaitError() error {
return computeSystem.waitError
}
// Wait synchronously waits for the compute system to shutdown or terminate. If
// the compute system has already exited returns the previous error (if any).
// Wait synchronously waits for the compute system to shutdown or terminate.
// If the compute system has already exited returns the previous error (if any).
func (computeSystem *System) Wait() error {
<-computeSystem.WaitChannel()
return computeSystem.WaitError()
return computeSystem.WaitCtx(context.Background())
}
// WaitCtx synchronously waits for the compute system to shutdown or terminate, or the context to be cancelled.
//
// See [System.Wait] for more information.
func (computeSystem *System) WaitCtx(ctx context.Context) error {
select {
case <-computeSystem.WaitChannel():
return computeSystem.WaitError()
case <-ctx.Done():
return ctx.Err()
}
}
// stopped returns true if the compute system stopped.
@ -735,9 +746,17 @@ func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process
}
// Close cleans up any state associated with the compute system but does not terminate or wait for it.
func (computeSystem *System) Close() (err error) {
func (computeSystem *System) Close() error {
return computeSystem.CloseCtx(context.Background())
}
// CloseCtx is similar to [System.Close], but accepts a context.
//
// The context is used for all operations, including waits, so timeouts/cancellations may prevent
// proper system cleanup.
func (computeSystem *System) CloseCtx(ctx context.Context) (err error) {
operation := "hcs::System::Close"
ctx, span := oc.StartSpan(context.Background(), operation)
ctx, span := oc.StartSpan(ctx, operation)
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))

View File

@ -9,13 +9,16 @@ import (
"reflect"
"time"
"github.com/containerd/containerd/log"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
const TimeFormat = log.RFC3339NanoFixed
// TimeFormat is [time.RFC3339Nano] with nanoseconds padded using
// zeros to ensure the formatted time is always the same number of
// characters.
// Based on RFC3339NanoFixed from github.com/containerd/log
const TimeFormat = "2006-01-02T15:04:05.000000000Z07:00"
func FormatTime(t time.Time) string {
return t.Format(TimeFormat)

View File

@ -6,7 +6,6 @@ import (
"time"
"github.com/Microsoft/hcsshim/internal/logfields"
"github.com/containerd/containerd/log"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@ -30,7 +29,7 @@ type Hook struct {
// An empty string disables formatting.
// When disabled, the fall back will the JSON encoding, if enabled.
//
// Default is [github.com/containerd/containerd/log.RFC3339NanoFixed].
// Default is [TimeFormat].
TimeFormat string
// Duration format converts a [time.Duration] fields to an appropriate encoding.
@ -49,7 +48,7 @@ var _ logrus.Hook = &Hook{}
func NewHook() *Hook {
return &Hook{
TimeFormat: log.RFC3339NanoFixed,
TimeFormat: TimeFormat,
DurationFormat: DurationFormatString,
AddSpanContext: true,
}

View File

@ -0,0 +1,12 @@
package log
import (
"github.com/sirupsen/logrus"
)
type NopFormatter struct{}
var _ logrus.Formatter = NopFormatter{}
// Format does nothing and returns a nil slice.
func (NopFormatter) Format(*logrus.Entry) ([]byte, error) { return nil, nil }

View File

@ -5,7 +5,7 @@ package guestrequest
type RequestType string
type ResourceType string
// RequestType const
// RequestType const.
const (
RequestTypeAdd RequestType = "Add"
RequestTypeRemove RequestType = "Remove"
@ -54,3 +54,23 @@ var (
"305891a9-b251-5dfe-91a2-c25d9212275b",
}
)
// constants for v2 schema ProcessModifyRequest
// Operation type for [hcsschema.ProcessModifyRequest].
type ProcessModifyOperation string
const (
ModifyProcessConsoleSize ProcessModifyOperation = "ConsoleSize"
CloseProcessHandle ProcessModifyOperation = "CloseHandle"
)
// Standard IO handle(s) to close for [hcsschema.CloseHandle] in [hcsschema.ProcessModifyRequest].
type STDIOHandle string
const (
STDInHandle STDIOHandle = "StdIn"
STDOutHandle STDIOHandle = "StdOut"
STDErrHandle STDIOHandle = "StdErr"
AllHandles STDIOHandle = "All"
)

View File

@ -7,6 +7,10 @@ package wclayer
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"syscall"
"github.com/Microsoft/go-winio/pkg/guid"
@ -101,3 +105,23 @@ func layerPathsToDescriptors(ctx context.Context, parentLayerPaths []string) ([]
return layers, nil
}
// GetLayerUvmBuild looks for a file named `uvmbuildversion` at `layerPath\uvmbuildversion` and returns the
// build number of the UVM from that file.
func GetLayerUvmBuild(layerPath string) (uint16, error) {
data, err := os.ReadFile(filepath.Join(layerPath, UvmBuildFileName))
if err != nil {
return 0, err
}
ver, err := strconv.ParseUint(string(data), 10, 16)
if err != nil {
return 0, err
}
return uint16(ver), nil
}
// WriteLayerUvmBuildFile writes a file at path `layerPath\uvmbuildversion` that contains the given `build`
// version for future reference.
func WriteLayerUvmBuildFile(layerPath string, build uint16) error {
return os.WriteFile(filepath.Join(layerPath, UvmBuildFileName), []byte(fmt.Sprintf("%d", build)), 0777)
}

View File

@ -32,6 +32,7 @@ func CreateScratchLayer(info DriverInfo, layerId, parentId string, parentLayerPa
func DeactivateLayer(info DriverInfo, id string) error {
return wclayer.DeactivateLayer(context.Background(), layerPath(&info, id))
}
func DestroyLayer(info DriverInfo, id string) error {
return wclayer.DestroyLayer(context.Background(), layerPath(&info, id))
}

View File

@ -1,149 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package log
import (
"context"
"github.com/containerd/log"
)
// G is a shorthand for [GetLogger].
//
// Deprecated: use [log.G].
var G = log.G
// L is an alias for the standard logger.
//
// Deprecated: use [log.L].
var L = log.L
// Fields type to pass to "WithFields".
//
// Deprecated: use [log.Fields].
type Fields = log.Fields
// Entry is a logging entry.
//
// Deprecated: use [log.Entry].
type Entry = log.Entry
// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using
// zeros to ensure the formatted time is always the same number of
// characters.
//
// Deprecated: use [log.RFC3339NanoFixed].
const RFC3339NanoFixed = log.RFC3339NanoFixed
// Level is a logging level.
//
// Deprecated: use [log.Level].
type Level = log.Level
// Supported log levels.
const (
// TraceLevel level.
//
// Deprecated: use [log.TraceLevel].
TraceLevel Level = log.TraceLevel
// DebugLevel level.
//
// Deprecated: use [log.DebugLevel].
DebugLevel Level = log.DebugLevel
// InfoLevel level.
//
// Deprecated: use [log.InfoLevel].
InfoLevel Level = log.InfoLevel
// WarnLevel level.
//
// Deprecated: use [log.WarnLevel].
WarnLevel Level = log.WarnLevel
// ErrorLevel level
//
// Deprecated: use [log.ErrorLevel].
ErrorLevel Level = log.ErrorLevel
// FatalLevel level.
//
// Deprecated: use [log.FatalLevel].
FatalLevel Level = log.FatalLevel
// PanicLevel level.
//
// Deprecated: use [log.PanicLevel].
PanicLevel Level = log.PanicLevel
)
// SetLevel sets log level globally. It returns an error if the given
// level is not supported.
//
// Deprecated: use [log.SetLevel].
func SetLevel(level string) error {
return log.SetLevel(level)
}
// GetLevel returns the current log level.
//
// Deprecated: use [log.GetLevel].
func GetLevel() log.Level {
return log.GetLevel()
}
// OutputFormat specifies a log output format.
//
// Deprecated: use [log.OutputFormat].
type OutputFormat = log.OutputFormat
// Supported log output formats.
const (
// TextFormat represents the text logging format.
//
// Deprecated: use [log.TextFormat].
TextFormat log.OutputFormat = "text"
// JSONFormat represents the JSON logging format.
//
// Deprecated: use [log.JSONFormat].
JSONFormat log.OutputFormat = "json"
)
// SetFormat sets the log output format.
//
// Deprecated: use [log.SetFormat].
func SetFormat(format OutputFormat) error {
return log.SetFormat(format)
}
// WithLogger returns a new context with the provided logger. Use in
// combination with logger.WithField(s) for great effect.
//
// Deprecated: use [log.WithLogger].
func WithLogger(ctx context.Context, logger *log.Entry) context.Context {
return log.WithLogger(ctx, logger)
}
// GetLogger retrieves the current logger from the context. If no logger is
// available, the default logger is returned.
//
// Deprecated: use [log.GetLogger].
func GetLogger(ctx context.Context) *log.Entry {
return log.GetLogger(ctx)
}

View File

@ -1,30 +0,0 @@
linters:
enable:
- exportloopref # Checks for pointers to enclosing loop variables
- gofmt
- goimports
- gosec
- ineffassign
- misspell
- nolintlint
- revive
- staticcheck
- tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
- unconvert
- unused
- vet
- dupword # Checks for duplicate words in the source code
disable:
- errcheck
run:
timeout: 5m
skip-dirs:
- api
- cluster
- design
- docs
- docs/man
- releases
- reports
- test # e2e scripts

View File

@ -1,191 +0,0 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,17 +0,0 @@
# log
A Go package providing a common logging interface across containerd repositories and a way for clients to use and configure logging in containerd packages.
This package is not intended to be used as a standalone logging package outside of the containerd ecosystem and is intended as an interface wrapper around a logging implementation.
In the future this package may be replaced with a common go logging interface.
## Project details
**log** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View File

@ -1,182 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package log provides types and functions related to logging, passing
// loggers through a context, and attaching context to the logger.
//
// # Transitional types
//
// This package contains various types that are aliases for types in [logrus].
// These aliases are intended for transitioning away from hard-coding logrus
// as logging implementation. Consumers of this package are encouraged to use
// the type-aliases from this package instead of directly using their logrus
// equivalent.
//
// The intent is to replace these aliases with locally defined types and
// interfaces once all consumers are no longer directly importing logrus
// types.
//
// IMPORTANT: due to the transitional purpose of this package, it is not
// guaranteed for the full logrus API to be provided in the future. As
// outlined, these aliases are provided as a step to transition away from
// a specific implementation which, as a result, exposes the full logrus API.
// While no decisions have been made on the ultimate design and interface
// provided by this package, we do not expect carrying "less common" features.
package log
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
)
// G is a shorthand for [GetLogger].
//
// We may want to define this locally to a package to get package tagged log
// messages.
var G = GetLogger
// L is an alias for the standard logger.
var L = &Entry{
Logger: logrus.StandardLogger(),
// Default is three fields plus a little extra room.
Data: make(Fields, 6),
}
type loggerKey struct{}
// Fields type to pass to "WithFields".
type Fields = map[string]any
// Entry is a logging entry. It contains all the fields passed with
// [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn,
// Error, Fatal or Panic is called on it. These objects can be reused and
// passed around as much as you wish to avoid field duplication.
//
// Entry is a transitional type, and currently an alias for [logrus.Entry].
type Entry = logrus.Entry
// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using
// zeros to ensure the formatted time is always the same number of
// characters.
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
// Level is a logging level.
type Level = logrus.Level
// Supported log levels.
const (
// TraceLevel level. Designates finer-grained informational events
// than [DebugLevel].
TraceLevel Level = logrus.TraceLevel
// DebugLevel level. Usually only enabled when debugging. Very verbose
// logging.
DebugLevel Level = logrus.DebugLevel
// InfoLevel level. General operational entries about what's going on
// inside the application.
InfoLevel Level = logrus.InfoLevel
// WarnLevel level. Non-critical entries that deserve eyes.
WarnLevel Level = logrus.WarnLevel
// ErrorLevel level. Logs errors that should definitely be noted.
// Commonly used for hooks to send errors to an error tracking service.
ErrorLevel Level = logrus.ErrorLevel
// FatalLevel level. Logs and then calls "logger.Exit(1)". It exits
// even if the logging level is set to Panic.
FatalLevel Level = logrus.FatalLevel
// PanicLevel level. This is the highest level of severity. Logs and
// then calls panic with the message passed to Debug, Info, ...
PanicLevel Level = logrus.PanicLevel
)
// SetLevel sets log level globally. It returns an error if the given
// level is not supported.
//
// level can be one of:
//
// - "trace" ([TraceLevel])
// - "debug" ([DebugLevel])
// - "info" ([InfoLevel])
// - "warn" ([WarnLevel])
// - "error" ([ErrorLevel])
// - "fatal" ([FatalLevel])
// - "panic" ([PanicLevel])
func SetLevel(level string) error {
lvl, err := logrus.ParseLevel(level)
if err != nil {
return err
}
L.Logger.SetLevel(lvl)
return nil
}
// GetLevel returns the current log level.
func GetLevel() Level {
return L.Logger.GetLevel()
}
// OutputFormat specifies a log output format.
type OutputFormat string
// Supported log output formats.
const (
// TextFormat represents the text logging format.
TextFormat OutputFormat = "text"
// JSONFormat represents the JSON logging format.
JSONFormat OutputFormat = "json"
)
// SetFormat sets the log output format ([TextFormat] or [JSONFormat]).
func SetFormat(format OutputFormat) error {
switch format {
case TextFormat:
L.Logger.SetFormatter(&logrus.TextFormatter{
TimestampFormat: RFC3339NanoFixed,
FullTimestamp: true,
})
return nil
case JSONFormat:
L.Logger.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: RFC3339NanoFixed,
})
return nil
default:
return fmt.Errorf("unknown log format: %s", format)
}
}
// WithLogger returns a new context with the provided logger. Use in
// combination with logger.WithField(s) for great effect.
func WithLogger(ctx context.Context, logger *Entry) context.Context {
return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx))
}
// GetLogger retrieves the current logger from the context. If no logger is
// available, the default logger is returned.
func GetLogger(ctx context.Context) *Entry {
if logger := ctx.Value(loggerKey{}); logger != nil {
return logger.(*Entry)
}
return L.WithContext(ctx)
}

View File

@ -436,9 +436,8 @@ func importTar(in io.ReaderAt) (*tarFile, error) {
if err != nil {
if err == io.EOF {
break
} else {
return nil, fmt.Errorf("failed to parse tar file, %w", err)
}
return nil, fmt.Errorf("failed to parse tar file, %w", err)
}
switch cleanEntryName(h.Name) {
case PrefetchLandmark, NoPrefetchLandmark:

View File

@ -17,13 +17,13 @@ env:
####
#### Cache-image names to test with (double-quotes around names are critical)
###
FEDORA_NAME: "fedora-38"
FEDORA_NAME: "fedora-3"
DEBIAN_NAME: "debian-13"
# GCE project where images live
IMAGE_PROJECT: "libpod-218412"
# VM Image built in containers/automation_images
IMAGE_SUFFIX: "c20230816t191118z-f38f37d13"
IMAGE_SUFFIX: "c20231004t194547z-f39f38d13"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}"
@ -113,8 +113,6 @@ debian_testing_task: &debian_testing
TEST_DRIVER: "fuse-overlay-whiteout"
- env:
TEST_DRIVER: "btrfs"
- env:
TEST_DRIVER: "zfs"
lint_task:

View File

@ -1 +1 @@
1.50.2
1.51.0

View File

@ -189,6 +189,9 @@ type DriverWithDifferOutput struct {
BigData map[string][]byte
TarSplit []byte
TOCDigest digest.Digest
// Artifacts is a collection of additional artifacts
// generated by the differ that the storage driver can use.
Artifacts map[string]interface{}
}
type DifferOutputFormat int

View File

@ -275,3 +275,36 @@ func supportsIdmappedLowerLayers(home string) (bool, error) {
}()
return true, nil
}
// supportsDataOnlyLayers checks if the kernel supports mounting a overlay file system
// that uses data-only layers.
func supportsDataOnlyLayers(home string) (bool, error) {
layerDir, err := os.MkdirTemp(home, "compat")
if err != nil {
return false, err
}
defer func() {
_ = os.RemoveAll(layerDir)
}()
mergedDir := filepath.Join(layerDir, "merged")
lowerDir := filepath.Join(layerDir, "lower")
lowerDirDataOnly := filepath.Join(layerDir, "lower-data")
upperDir := filepath.Join(layerDir, "upper")
workDir := filepath.Join(layerDir, "work")
_ = idtools.MkdirAs(mergedDir, 0o700, 0, 0)
_ = idtools.MkdirAs(lowerDir, 0o700, 0, 0)
_ = idtools.MkdirAs(lowerDirDataOnly, 0o700, 0, 0)
_ = idtools.MkdirAs(upperDir, 0o700, 0, 0)
_ = idtools.MkdirAs(workDir, 0o700, 0, 0)
opts := fmt.Sprintf("lowerdir=%s::%s,upperdir=%s,workdir=%s,metacopy=on", lowerDir, lowerDirDataOnly, upperDir, workDir)
flags := uintptr(0)
if err := unix.Mount("overlay", mergedDir, "overlay", flags, opts); err != nil {
return false, err
}
_ = unix.Unmount(mergedDir, unix.MNT_DETACH)
return true, nil
}

View File

@ -11,7 +11,7 @@ func composeFsSupported() bool {
return false
}
func generateComposeFsBlob(toc []byte, composefsDir string) error {
func generateComposeFsBlob(verityDigests map[string]string, toc interface{}, composefsDir string) error {
return fmt.Errorf("composefs is not supported")
}
@ -19,6 +19,6 @@ func mountComposefsBlob(dataDir, mountPoint string) error {
return fmt.Errorf("composefs is not supported")
}
func enableVerityRecursive(path string) error {
return fmt.Errorf("composefs is not supported")
func enableVerityRecursive(path string) (map[string]string, error) {
return nil, fmt.Errorf("composefs is not supported")
}

View File

@ -4,7 +4,6 @@
package overlay
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
@ -16,6 +15,7 @@ import (
"syscall"
"unsafe"
"github.com/containers/storage/pkg/chunked/dump"
"github.com/containers/storage/pkg/loopback"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
@ -29,7 +29,7 @@ var (
func getComposeFsHelper() (string, error) {
composeFsHelperOnce.Do(func() {
composeFsHelperPath, composeFsHelperErr = exec.LookPath("composefs-from-json")
composeFsHelperPath, composeFsHelperErr = exec.LookPath("mkcomposefs")
})
return composeFsHelperPath, composeFsHelperErr
}
@ -53,7 +53,23 @@ func enableVerity(description string, fd int) error {
return nil
}
func enableVerityRecursive(path string) error {
type verityDigest struct {
Fsv unix.FsverityDigest
Buf [64]byte
}
func measureVerity(description string, fd int) (string, error) {
var digest verityDigest
digest.Fsv.Size = 64
_, _, e1 := syscall.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.FS_IOC_MEASURE_VERITY), uintptr(unsafe.Pointer(&digest)))
if e1 != 0 {
return "", fmt.Errorf("failed to measure verity for %q: %w", description, e1)
}
return fmt.Sprintf("%x", digest.Buf[:digest.Fsv.Size]), nil
}
func enableVerityRecursive(root string) (map[string]string, error) {
digests := make(map[string]string)
walkFn := func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
@ -71,29 +87,47 @@ func enableVerityRecursive(path string) error {
if err := enableVerity(path, int(f.Fd())); err != nil {
return err
}
verity, err := measureVerity(path, int(f.Fd()))
if err != nil {
return err
}
relPath, err := filepath.Rel(root, path)
if err != nil {
return err
}
digests[relPath] = verity
return nil
}
return filepath.WalkDir(path, walkFn)
err := filepath.WalkDir(root, walkFn)
return digests, err
}
func getComposefsBlob(dataDir string) string {
return filepath.Join(dataDir, "composefs.blob")
}
func generateComposeFsBlob(toc []byte, composefsDir string) error {
func generateComposeFsBlob(verityDigests map[string]string, toc interface{}, composefsDir string) error {
if err := os.MkdirAll(composefsDir, 0o700); err != nil {
return err
}
dumpReader, err := dump.GenerateDump(toc, verityDigests)
if err != nil {
return err
}
destFile := getComposefsBlob(composefsDir)
writerJson, err := getComposeFsHelper()
if err != nil {
return fmt.Errorf("failed to find composefs-from-json: %w", err)
return fmt.Errorf("failed to find mkcomposefs: %w", err)
}
fd, err := unix.Openat(unix.AT_FDCWD, destFile, unix.O_WRONLY|unix.O_CREAT|unix.O_TRUNC|unix.O_EXCL|unix.O_CLOEXEC, 0o644)
if err != nil {
return fmt.Errorf("failed to open output file: %w", err)
return fmt.Errorf("failed to open output file %q: %w", destFile, err)
}
outFd := os.NewFile(uintptr(fd), "outFd")
@ -109,10 +143,10 @@ func generateComposeFsBlob(toc []byte, composefsDir string) error {
// a scope to close outFd before setting fsverity on the read-only fd.
defer outFd.Close()
cmd := exec.Command(writerJson, "--format=erofs", "--out=/proc/self/fd/3", "/proc/self/fd/0")
cmd := exec.Command(writerJson, "--from-file", "-", "/proc/self/fd/3")
cmd.ExtraFiles = []*os.File{outFd}
cmd.Stderr = os.Stderr
cmd.Stdin = bytes.NewReader(toc)
cmd.Stdin = dumpReader
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to convert json to erofs: %w", err)
}
@ -166,7 +200,7 @@ func hasACL(path string) (bool, error) {
func mountComposefsBlob(dataDir, mountPoint string) error {
blobFile := getComposefsBlob(dataDir)
loop, err := loopback.AttachLoopDevice(blobFile)
loop, err := loopback.AttachLoopDeviceRO(blobFile)
if err != nil {
return err
}

View File

@ -141,14 +141,27 @@ func mountOverlayFromMain() {
// the new value for the list of lowers, because it's shorter.
if lowerv != "" {
lowers := strings.Split(lowerv, ":")
for i := range lowers {
lowerFd, err := unix.Open(lowers[i], unix.O_RDONLY, 0)
var newLowers []string
dataOnly := false
for _, lowerPath := range lowers {
if lowerPath == "" {
dataOnly = true
continue
}
lowerFd, err := unix.Open(lowerPath, unix.O_RDONLY, 0)
if err != nil {
fatal(err)
}
lowers[i] = fmt.Sprintf("%d", lowerFd)
var lower string
if dataOnly {
lower = fmt.Sprintf(":%d", lowerFd)
dataOnly = false
} else {
lower = fmt.Sprintf("%d", lowerFd)
}
newLowers = append(newLowers, lower)
}
lowerv = strings.Join(lowers, ":")
lowerv = strings.Join(newLowers, ":")
}
// Reconstruct the Label field.

View File

@ -82,7 +82,7 @@ const (
lowerFile = "lower"
maxDepth = 500
zstdChunkedManifest = "zstd-chunked-manifest"
tocArtifact = "toc"
// idLength represents the number of random characters
// which can be used to create the unique link identifier
@ -1003,8 +1003,10 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable
}
}
if parent != "" {
parentBase, parentImageStore, _ := d.dir2(parent)
if parentImageStore != "" {
parentBase, parentImageStore, inAdditionalStore := d.dir2(parent)
// If parentBase path is additional image store, select the image contained in parentBase.
// See https://github.com/containers/podman/issues/19748
if parentImageStore != "" && !inAdditionalStore {
parentBase = parentImageStore
}
st, err := system.Stat(filepath.Join(parentBase, "diff"))
@ -1079,12 +1081,13 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable
}
if parent != "" {
parentDir, parentImageStore, _ := d.dir2(parent)
base := parentDir
if parentImageStore != "" {
base = parentImageStore
parentBase, parentImageStore, inAdditionalStore := d.dir2(parent)
// If parentBase path is additional image store, select the image contained in parentBase.
// See https://github.com/containers/podman/issues/19748
if parentImageStore != "" && !inAdditionalStore {
parentBase = parentImageStore
}
st, err := system.Stat(filepath.Join(base, "diff"))
st, err := system.Stat(filepath.Join(parentBase, "diff"))
if err != nil {
return err
}
@ -1447,7 +1450,9 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
needsIDMapping := !disableShifting && len(options.UidMaps) > 0 && len(options.GidMaps) > 0 && d.options.mountProgram == ""
if len(optsList) == 0 {
optsList = strings.Split(d.options.mountOptions, ",")
if d.options.mountOptions != "" {
optsList = strings.Split(d.options.mountOptions, ",")
}
} else {
// If metacopy=on is present in d.options.mountOptions it must be present in the mount
// options otherwise the kernel refuses to follow the metacopy xattr.
@ -1524,15 +1529,8 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
defer cleanupFunc()
}
composefsLayers := filepath.Join(workDirBase, "composefs-layers")
if err := os.MkdirAll(composefsLayers, 0o700); err != nil {
return "", err
}
skipIDMappingLayers := make(map[string]string)
composeFsLayers := []string{}
composefsMounts := []string{}
defer func() {
for _, m := range composefsMounts {
@ -1540,7 +1538,9 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
}
}()
maybeAddComposefsMount := func(lowerID string, i int) (string, error) {
composeFsLayers := []string{}
composeFsLayersDir := filepath.Join(workDirBase, "composefs-layers")
maybeAddComposefsMount := func(lowerID string, i int, readWrite bool) (string, error) {
composefsBlob := d.getComposefsData(lowerID)
_, err = os.Stat(composefsBlob)
if err != nil {
@ -1551,7 +1551,11 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
}
logrus.Debugf("overlay: using composefs blob %s for lower %s", composefsBlob, lowerID)
dest := filepath.Join(composefsLayers, fmt.Sprintf("%d", i))
if readWrite && i == 0 {
return "", fmt.Errorf("cannot mount a composefs layer as writeable")
}
dest := filepath.Join(composeFsLayersDir, fmt.Sprintf("%d", i))
if err := os.MkdirAll(dest, 0o700); err != nil {
return "", err
}
@ -1571,7 +1575,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
diffDir := path.Join(workDirBase, "diff")
if dest, err := maybeAddComposefsMount(id, 0); err != nil {
if dest, err := maybeAddComposefsMount(id, 0, readWrite); err != nil {
return "", err
} else if dest != "" {
diffDir = dest
@ -1623,7 +1627,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
return "", err
}
lowerID := filepath.Base(filepath.Dir(linkContent))
composefsMount, err := maybeAddComposefsMount(lowerID, i+1)
composefsMount, err := maybeAddComposefsMount(lowerID, i+1, readWrite)
if err != nil {
return "", err
}
@ -1655,8 +1659,6 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
optsList = append(optsList, "metacopy=on", "redirect_dir=on")
}
absLowers = append(absLowers, composeFsLayers...)
if len(absLowers) == 0 {
absLowers = append(absLowers, path.Join(dir, "empty"))
}
@ -1750,11 +1752,20 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
absLowers = newAbsDir
}
lowerDirs := strings.Join(absLowers, ":")
if len(composeFsLayers) > 0 {
composeFsLayersLowerDirs := strings.Join(composeFsLayers, "::")
lowerDirs = lowerDirs + "::" + composeFsLayersLowerDirs
}
// absLowers is not valid anymore now as we have added composeFsLayers to it, so prevent
// its usage.
absLowers = nil //nolint:ineffassign
var opts string
if readWrite {
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workdir)
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDirs, diffDir, workdir)
} else {
opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, strings.Join(absLowers, ":"))
opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, lowerDirs)
}
if len(optsList) > 0 {
opts = fmt.Sprintf("%s,%s", opts, strings.Join(optsList, ","))
@ -1798,9 +1809,9 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
if readWrite {
diffDir := path.Join(id, "diff")
workDir := path.Join(id, "work")
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workDir)
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDirs, diffDir, workDir)
} else {
opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, strings.Join(absLowers, ":"))
opts = fmt.Sprintf("lowerdir=%s:%s", diffDir, lowerDirs)
}
if len(optsList) > 0 {
opts = strings.Join(append([]string{opts}, optsList...), ",")
@ -2007,11 +2018,34 @@ func (d *Driver) CleanupStagingDirectory(stagingDirectory string) error {
return os.RemoveAll(stagingDirectory)
}
func (d *Driver) supportsDataOnlyLayers() (bool, error) {
feature := "dataonly-layers"
overlayCacheResult, overlayCacheText, err := cachedFeatureCheck(d.runhome, feature)
if err == nil {
if overlayCacheResult {
logrus.Debugf("Cached value indicated that data-only layers for overlay are supported")
return true, nil
}
logrus.Debugf("Cached value indicated that data-only layers for overlay are not supported")
return false, errors.New(overlayCacheText)
}
supportsDataOnly, err := supportsDataOnlyLayers(d.home)
if err2 := cachedFeatureRecord(d.runhome, feature, supportsDataOnly, ""); err2 != nil {
return false, fmt.Errorf("recording overlay data-only layers support status: %w", err2)
}
return supportsDataOnly, err
}
func (d *Driver) useComposeFs() bool {
if !composeFsSupported() || unshare.IsRootless() {
return false
}
return true
supportsDataOnlyLayers, err := d.supportsDataOnlyLayers()
if err != nil {
logrus.Debugf("Check for data-only layers failed with: %v", err)
return false
}
return supportsDataOnlyLayers
}
// ApplyDiff applies the changes in the new layer using the specified function
@ -2074,11 +2108,12 @@ func (d *Driver) ApplyDiffFromStagingDirectory(id, parent, stagingDirectory stri
if d.useComposeFs() {
// FIXME: move this logic into the differ so we don't have to open
// the file twice.
if err := enableVerityRecursive(stagingDirectory); err != nil && !errors.Is(err, unix.ENOTSUP) && !errors.Is(err, unix.ENOTTY) {
verityDigests, err := enableVerityRecursive(stagingDirectory)
if err != nil && !errors.Is(err, unix.ENOTSUP) && !errors.Is(err, unix.ENOTTY) {
logrus.Warningf("%s", err)
}
toc := diffOutput.BigData[zstdChunkedManifest]
if err := generateComposeFsBlob(toc, d.getComposefsData(id)); err != nil {
toc := diffOutput.Artifacts[tocArtifact]
if err := generateComposeFsBlob(verityDigests, toc, d.getComposefsData(id)); err != nil {
return err
}
}

View File

@ -1245,8 +1245,8 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
if parentLayer != nil {
parent = parentLayer.ID
}
var parentMappings, templateIDMappings, oldMappings *idtools.IDMappings
var (
templateIDMappings *idtools.IDMappings
templateMetadata string
templateCompressedDigest digest.Digest
templateCompressedSize int64
@ -1274,11 +1274,6 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
} else {
templateIDMappings = &idtools.IDMappings{}
}
if parentLayer != nil {
parentMappings = idtools.NewIDMappingsFromMaps(parentLayer.UIDMap, parentLayer.GIDMap)
} else {
parentMappings = &idtools.IDMappings{}
}
if mountLabel != "" {
selinux.ReserveLabel(mountLabel)
}
@ -1353,6 +1348,12 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
IDMappings: idMappings,
}
var parentMappings, oldMappings *idtools.IDMappings
if parentLayer != nil {
parentMappings = idtools.NewIDMappingsFromMaps(parentLayer.UIDMap, parentLayer.GIDMap)
} else {
parentMappings = &idtools.IDMappings{}
}
if moreOptions.TemplateLayer != "" {
if err = r.driver.CreateFromTemplate(id, moreOptions.TemplateLayer, templateIDMappings, parent, parentMappings, &opts, writeable); err != nil {
cleanupFailureContext = fmt.Sprintf("creating a layer from template layer %q", moreOptions.TemplateLayer)
@ -1371,10 +1372,13 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
return nil, -1, fmt.Errorf("creating read-only layer with ID %q: %w", id, err)
}
}
oldMappings = parentMappings
if parentLayer != nil {
oldMappings = parentMappings
}
}
if !reflect.DeepEqual(oldMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(oldMappings.GIDs(), idMappings.GIDs()) {
if oldMappings != nil &&
(!reflect.DeepEqual(oldMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(oldMappings.GIDs(), idMappings.GIDs())) {
if err = r.driver.UpdateLayerIDMap(id, oldMappings, idMappings, mountLabel); err != nil {
cleanupFailureContext = "in UpdateLayerIDMap"
return nil, -1, err

View File

@ -955,14 +955,8 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
if options.ForceMask != nil {
// if ForceMask is in place, make sure lchown is disabled.
doChown = false
uid, gid, mode, err := GetFileOwner(dest)
if err == nil {
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
if err := system.Lsetxattr(dest, idtools.ContainersOverrideXattr, []byte(value), 0); err != nil {
return err
}
}
}
var rootHdr *tar.Header
// Iterate through the files in the archive.
loop:
@ -1007,6 +1001,9 @@ loop:
if err != nil {
return err
}
if rel == "." {
rootHdr = hdr
}
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
}
@ -1080,6 +1077,14 @@ loop:
return err
}
}
if options.ForceMask != nil && rootHdr != nil {
value := fmt.Sprintf("%d:%d:0%o", rootHdr.Uid, rootHdr.Gid, rootHdr.Mode)
if err := system.Lsetxattr(dest, idtools.ContainersOverrideXattr, []byte(value), 0); err != nil {
return err
}
}
return nil
}

View File

@ -26,6 +26,8 @@ import (
const (
cacheKey = "chunked-manifest-cache"
cacheVersion = 1
digestSha256Empty = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
)
type metadata struct {
@ -650,6 +652,9 @@ func unmarshalToc(manifest []byte) (*internal.TOC, error) {
iter.Skip()
}
}
if m.Type == TypeReg && m.Size == 0 && m.Digest == "" {
m.Digest = digestSha256Empty
}
toc.Entries = append(toc.Entries, m)
}
break

View File

@ -0,0 +1,230 @@
package dump
import (
"bufio"
"fmt"
"io"
"strings"
"time"
"unicode"
"github.com/containers/storage/pkg/chunked/internal"
"golang.org/x/sys/unix"
)
const (
ESCAPE_STANDARD = 0
NOESCAPE_SPACE = 1 << iota
ESCAPE_EQUAL
ESCAPE_LONE_DASH
)
func escaped(val string, escape int) string {
noescapeSpace := escape&NOESCAPE_SPACE != 0
escapeEqual := escape&ESCAPE_EQUAL != 0
escapeLoneDash := escape&ESCAPE_LONE_DASH != 0
length := len(val)
if escapeLoneDash && val == "-" {
return fmt.Sprintf("\\x%.2x", val[0])
}
var result string
for i := 0; i < length; i++ {
c := val[i]
hexEscape := false
var special string
switch c {
case '\\':
special = "\\\\"
case '\n':
special = "\\n"
case '\r':
special = "\\r"
case '\t':
special = "\\t"
case '=':
hexEscape = escapeEqual
default:
if noescapeSpace {
hexEscape = !unicode.IsPrint(rune(c))
} else {
hexEscape = !unicode.IsGraphic(rune(c))
}
}
if special != "" {
result += special
} else if hexEscape {
result += fmt.Sprintf("\\x%.2x", c)
} else {
result += string(c)
}
}
return result
}
func escapedOptional(val string, escape int) string {
if val == "" {
return "-"
}
return escaped(val, escape)
}
func getStMode(mode uint32, typ string) (uint32, error) {
switch typ {
case internal.TypeReg, internal.TypeLink:
mode |= unix.S_IFREG
case internal.TypeChar:
mode |= unix.S_IFCHR
case internal.TypeBlock:
mode |= unix.S_IFBLK
case internal.TypeDir:
mode |= unix.S_IFDIR
case internal.TypeFifo:
mode |= unix.S_IFIFO
case internal.TypeSymlink:
mode |= unix.S_IFLNK
default:
return 0, fmt.Errorf("unknown type %s", typ)
}
return mode, nil
}
func dumpNode(out io.Writer, links map[string]int, verityDigests map[string]string, entry *internal.FileMetadata) error {
path := entry.Name
if path == "" {
path = "/"
} else if path[0] != '/' {
path = "/" + path
}
if _, err := fmt.Fprint(out, escaped(path, ESCAPE_STANDARD)); err != nil {
return err
}
nlinks := links[entry.Name] + links[entry.Linkname] + 1
link := ""
if entry.Type == internal.TypeLink {
link = "@"
}
rdev := unix.Mkdev(uint32(entry.Devmajor), uint32(entry.Devminor))
entryTime := entry.ModTime
if entryTime == nil {
t := time.Unix(0, 0)
entryTime = &t
}
mode, err := getStMode(uint32(entry.Mode), entry.Type)
if err != nil {
return err
}
if _, err := fmt.Fprintf(out, " %d %s%o %d %d %d %d %d.%d ", entry.Size,
link, mode,
nlinks, entry.UID, entry.GID, rdev,
entryTime.Unix(), entryTime.Nanosecond()); err != nil {
return err
}
var payload string
if entry.Linkname != "" {
payload = entry.Linkname
if entry.Type == internal.TypeLink && payload[0] != '/' {
payload = "/" + payload
}
} else {
if len(entry.Digest) > 10 {
d := strings.Replace(entry.Digest, "sha256:", "", 1)
payload = d[:2] + "/" + d[2:]
}
}
if _, err := fmt.Fprintf(out, escapedOptional(payload, ESCAPE_LONE_DASH)); err != nil {
return err
}
/* inline content. */
if _, err := fmt.Fprint(out, " -"); err != nil {
return err
}
/* store the digest. */
if _, err := fmt.Fprint(out, " "); err != nil {
return err
}
digest := verityDigests[payload]
if _, err := fmt.Fprintf(out, escapedOptional(digest, ESCAPE_LONE_DASH)); err != nil {
return err
}
for k, v := range entry.Xattrs {
name := escaped(k, ESCAPE_EQUAL)
value := escaped(v, ESCAPE_EQUAL)
if _, err := fmt.Fprintf(out, " %s=%s", name, value); err != nil {
return err
}
}
if _, err := fmt.Fprint(out, "\n"); err != nil {
return err
}
return nil
}
// GenerateDump generates a dump of the TOC in the same format as `composefs-info dump`
func GenerateDump(tocI interface{}, verityDigests map[string]string) (io.Reader, error) {
toc, ok := tocI.(*internal.TOC)
if !ok {
return nil, fmt.Errorf("invalid TOC type")
}
pipeR, pipeW := io.Pipe()
go func() {
closed := false
w := bufio.NewWriter(pipeW)
defer func() {
if !closed {
w.Flush()
pipeW.Close()
}
}()
links := make(map[string]int)
for _, e := range toc.Entries {
if e.Linkname == "" {
continue
}
links[e.Linkname] = links[e.Linkname] + 1
}
if len(toc.Entries) == 0 || (toc.Entries[0].Name != "" && toc.Entries[0].Name != "/") {
root := &internal.FileMetadata{
Name: "/",
Type: internal.TypeDir,
Mode: 0o755,
}
if err := dumpNode(w, links, verityDigests, root); err != nil {
pipeW.CloseWithError(err)
closed = true
return
}
}
for _, e := range toc.Entries {
if e.Type == internal.TypeChunk {
continue
}
if err := dumpNode(w, links, verityDigests, &e); err != nil {
pipeW.CloseWithError(err)
closed = true
return
}
}
}()
return pipeR, nil
}

View File

@ -45,6 +45,7 @@ const (
bigDataKey = "zstd-chunked-manifest"
chunkedData = "zstd-chunked-data"
chunkedLayerDataKey = "zstd-chunked-layer-data"
tocKey = "toc"
fileTypeZstdChunked = iota
fileTypeEstargz
@ -1470,10 +1471,7 @@ func makeEntriesFlat(mergedEntries []internal.FileMetadata) ([]internal.FileMeta
continue
}
if mergedEntries[i].Digest == "" {
if mergedEntries[i].Size != 0 {
return nil, fmt.Errorf("missing digest for %q", mergedEntries[i].Name)
}
continue
return nil, fmt.Errorf("missing digest for %q", mergedEntries[i].Name)
}
digest, err := digest.Parse(mergedEntries[i].Digest)
if err != nil {
@ -1542,6 +1540,13 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff
if err != nil {
return graphdriver.DriverWithDifferOutput{}, err
}
// Generate the manifest
toc, err := unmarshalToc(c.manifest)
if err != nil {
return graphdriver.DriverWithDifferOutput{}, err
}
output := graphdriver.DriverWithDifferOutput{
Differ: c,
TarSplit: c.tarSplit,
@ -1549,6 +1554,9 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff
bigDataKey: c.manifest,
chunkedLayerDataKey: lcdBigData,
},
Artifacts: map[string]interface{}{
tocKey: toc,
},
TOCDigest: c.contentDigest,
}
@ -1563,12 +1571,6 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff
// List of OSTree repositories to use for deduplication
ostreeRepos := strings.Split(c.storeOpts.PullOptions["ostree_repos"], ":")
// Generate the manifest
toc, err := unmarshalToc(c.manifest)
if err != nil {
return output, err
}
whiteoutConverter := archive.GetWhiteoutConverter(options.WhiteoutFormat, options.WhiteoutData)
var missingParts []missingPart

View File

@ -114,6 +114,16 @@ func openNextAvailableLoopback(index int, sparseName string, sparseFile *os.File
// AttachLoopDevice attaches the given sparse file to the next
// available loopback device. It returns an opened *os.File.
func AttachLoopDevice(sparseName string) (loop *os.File, err error) {
return attachLoopDevice(sparseName, false)
}
// AttachLoopDeviceRO attaches the given sparse file opened read-only to
// the next available loopback device. It returns an opened *os.File.
func AttachLoopDeviceRO(sparseName string) (loop *os.File, err error) {
return attachLoopDevice(sparseName, true)
}
func attachLoopDevice(sparseName string, readonly bool) (loop *os.File, err error) {
// Try to retrieve the next available loopback device via syscall.
// If it fails, we discard error and start looping for a
// loopback from index 0.
@ -122,8 +132,14 @@ func AttachLoopDevice(sparseName string) (loop *os.File, err error) {
logrus.Debugf("Error retrieving the next available loopback: %s", err)
}
var sparseFile *os.File
// OpenFile adds O_CLOEXEC
sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0o644)
if readonly {
sparseFile, err = os.OpenFile(sparseName, os.O_RDONLY, 0o644)
} else {
sparseFile, err = os.OpenFile(sparseName, os.O_RDWR, 0o644)
}
if err != nil {
logrus.Errorf("Opening sparse file: %v", err)
return nil, ErrAttachLoopbackDevice

View File

@ -28,7 +28,7 @@ func EnsureRemoveAll(dir string) error {
// track retries
exitOnErr := make(map[string]int)
maxRetry := 100
maxRetry := 1000
// Attempt a simple remove all first, this avoids the more expensive
// RecursiveUnmount call if not needed.
@ -38,7 +38,7 @@ func EnsureRemoveAll(dir string) error {
// Attempt to unmount anything beneath this dir first
if err := mount.RecursiveUnmount(dir); err != nil {
logrus.Debugf("RecusiveUnmount on %s failed: %v", dir, err)
logrus.Debugf("RecursiveUnmount on %s failed: %v", dir, err)
}
for {
@ -94,6 +94,6 @@ func EnsureRemoveAll(dir string) error {
return err
}
exitOnErr[pe.Path]++
time.Sleep(100 * time.Millisecond)
time.Sleep(10 * time.Millisecond)
}
}

View File

@ -25,6 +25,11 @@ func GetRootlessUID() int {
return os.Getuid()
}
// GetRootlessGID returns the GID of the user in the parent userNS
func GetRootlessGID() int {
return os.Getgid()
}
// RootlessEnv returns the environment settings for the rootless containers
func RootlessEnv() []string {
return append(os.Environ(), UsernsEnvName+"=")

View File

@ -441,6 +441,16 @@ func GetRootlessUID() int {
return os.Getuid()
}
// GetRootlessGID returns the GID of the user in the parent userNS
func GetRootlessGID() int {
gidEnv := getenv("_CONTAINERS_ROOTLESS_GID")
if gidEnv != "" {
u, _ := strconv.Atoi(gidEnv)
return u
}
return os.Getgid()
}
// RootlessEnv returns the environment settings for the rootless containers
func RootlessEnv() []string {
return append(os.Environ(), UsernsEnvName+"=done")

View File

@ -25,6 +25,11 @@ func GetRootlessUID() int {
return os.Getuid()
}
// GetRootlessGID returns the GID of the user in the parent userNS
func GetRootlessGID() int {
return os.Getgid()
}
// RootlessEnv returns the environment settings for the rootless containers
func RootlessEnv() []string {
return append(os.Environ(), UsernsEnvName+"=")

View File

@ -27,9 +27,8 @@ runroot = "/run/containers/storage"
# restorecon -R -v /NEWSTORAGEPATH
graphroot = "/var/lib/containers/storage"
# Optional value for image storage location
# If set, it must be different than graphroot.
# Optional alternate location of image store if a location separate from the
# container store is required. If set, it must be different than graphroot.
# imagestore = ""

View File

@ -2666,34 +2666,23 @@ func (s *store) DeleteContainer(id string) error {
}
var wg multierror.Group
wg.Go(func() error { return s.containerStore.Delete(id) })
middleDir := s.graphDriverName + "-containers"
wg.Go(func() error {
gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID)
// attempt a simple rm -rf first
if err := os.RemoveAll(gcpath); err == nil {
return nil
}
// and if it fails get to the more complicated cleanup
return system.EnsureRemoveAll(gcpath)
})
wg.Go(func() error {
rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID)
// attempt a simple rm -rf first
if err := os.RemoveAll(rcpath); err == nil {
return nil
}
// and if it fails get to the more complicated cleanup
return system.EnsureRemoveAll(rcpath)
})
if multierr := wg.Wait(); multierr != nil {
return multierr.ErrorOrNil()
}
return nil
return s.containerStore.Delete(id)
})
}
@ -3418,16 +3407,16 @@ func (s *store) Shutdown(force bool) ([]string, error) {
err = fmt.Errorf("a layer is mounted: %w", ErrLayerUsedByContainer)
}
if err == nil {
err = s.graphDriver.Cleanup()
// We dont retain the lastWrite value, and treat this update as if someone else did the .Cleanup(),
// so that we reload after a .Shutdown() the same way other processes would.
// Shutdown() is basically an error path, so reliability is more important than performance.
if _, err2 := s.graphLock.RecordWrite(); err2 != nil {
if err == nil {
err = err2
} else {
err = fmt.Errorf("(graphLock.RecordWrite failed: %v) %w", err2, err)
}
err = fmt.Errorf("graphLock.RecordWrite failed: %w", err2)
}
// Do the Cleanup() only after we are sure that the change was recorded with RecordWrite(), so that
// the next user picks it.
if err == nil {
err = s.graphDriver.Cleanup()
}
}
return mounted, err

View File

@ -220,9 +220,8 @@ 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.
// Image Store is the alternate location of image store if a location
// separate from the container store is required.
ImageStore string `json:"imagestore,omitempty"`
// RootlessStoragePath is the storage path for rootless users
// default $HOME/.local/share/containers/storage

View File

@ -5,15 +5,19 @@ import (
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"
"sync"
"golang.org/x/sys/unix"
)
// GetMountsFromReader retrieves a list of mounts from the
// reader provided, with an optional filter applied (use nil
// for no filter). This can be useful in tests or benchmarks
// that provide fake mountinfo data, or when a source other
// than /proc/self/mountinfo needs to be read from.
// than /proc/thread-self/mountinfo needs to be read from.
//
// This function is Linux-specific.
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
@ -127,8 +131,40 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
return out, nil
}
func parseMountTable(filter FilterFunc) ([]*Info, error) {
f, err := os.Open("/proc/self/mountinfo")
var (
haveProcThreadSelf bool
haveProcThreadSelfOnce sync.Once
)
func parseMountTable(filter FilterFunc) (_ []*Info, err error) {
haveProcThreadSelfOnce.Do(func() {
_, err := os.Stat("/proc/thread-self/mountinfo")
haveProcThreadSelf = err == nil
})
// We need to lock ourselves to the current OS thread in order to make sure
// that the thread referenced by /proc/thread-self stays alive until we
// finish parsing the file.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var f *os.File
if haveProcThreadSelf {
f, err = os.Open("/proc/thread-self/mountinfo")
} else {
// On pre-3.17 kernels (such as CentOS 7), we don't have
// /proc/thread-self/ so we need to manually construct
// /proc/self/task/<tid>/ as a fallback.
f, err = os.Open("/proc/self/task/" + strconv.Itoa(unix.Gettid()) + "/mountinfo")
if os.IsNotExist(err) {
// If /proc/self/task/... failed, it means that our active pid
// namespace doesn't match the pid namespace of the /proc mount. In
// this case we just have to make do with /proc/self, since there
// is no other way of figuring out our tid in a parent pid
// namespace on pre-3.17 kernels.
f, err = os.Open("/proc/self/mountinfo")
}
}
if err != nil {
return nil, err
}
@ -158,10 +194,10 @@ func PidMountInfo(pid int) ([]*Info, error) {
// A few specific characters in mountinfo path entries (root and mountpoint)
// are escaped using a backslash followed by a character's ascii code in octal.
//
// space -- as \040
// tab (aka \t) -- as \011
// newline (aka \n) -- as \012
// backslash (aka \\) -- as \134
// space -- as \040
// tab (aka \t) -- as \011
// newline (aka \n) -- as \012
// backslash (aka \\) -- as \134
//
// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
func unescape(path string) (string, error) {

15
vendor/modules.txt vendored
View File

@ -14,7 +14,7 @@ github.com/Microsoft/go-winio/internal/socket
github.com/Microsoft/go-winio/internal/stringbuffer
github.com/Microsoft/go-winio/pkg/guid
github.com/Microsoft/go-winio/vhd
# github.com/Microsoft/hcsshim v0.12.0-rc.0
# github.com/Microsoft/hcsshim v0.12.0-rc.1
## explicit; go 1.18
github.com/Microsoft/hcsshim
github.com/Microsoft/hcsshim/computestorage
@ -56,11 +56,7 @@ github.com/containerd/cgroups/v3/cgroup1/stats
# github.com/containerd/containerd v1.7.8
## explicit; go 1.19
github.com/containerd/containerd/errdefs
github.com/containerd/containerd/log
# github.com/containerd/log v0.1.0
## explicit; go 1.20
github.com/containerd/log
# github.com/containerd/stargz-snapshotter/estargz v0.14.3
# github.com/containerd/stargz-snapshotter/estargz v0.15.1
## explicit; go 1.19
github.com/containerd/stargz-snapshotter/estargz
github.com/containerd/stargz-snapshotter/estargz/errorutil
@ -165,7 +161,7 @@ github.com/containers/ocicrypt/keywrap/pkcs7
github.com/containers/ocicrypt/spec
github.com/containers/ocicrypt/utils
github.com/containers/ocicrypt/utils/keyprovider
# github.com/containers/storage v1.50.2
# github.com/containers/storage v1.51.0
## explicit; go 1.19
github.com/containers/storage
github.com/containers/storage/drivers
@ -184,6 +180,7 @@ github.com/containers/storage/pkg/archive
github.com/containers/storage/pkg/chrootarchive
github.com/containers/storage/pkg/chunked
github.com/containers/storage/pkg/chunked/compressor
github.com/containers/storage/pkg/chunked/dump
github.com/containers/storage/pkg/chunked/internal
github.com/containers/storage/pkg/config
github.com/containers/storage/pkg/devicemapper
@ -418,7 +415,7 @@ github.com/mistifyio/go-zfs/v3
# github.com/mitchellh/mapstructure v1.5.0
## explicit; go 1.14
github.com/mitchellh/mapstructure
# github.com/moby/sys/mountinfo v0.6.2
# github.com/moby/sys/mountinfo v0.7.1
## explicit; go 1.16
github.com/moby/sys/mountinfo
# github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
@ -442,7 +439,7 @@ github.com/opencontainers/image-spec/specs-go/v1
# github.com/opencontainers/image-tools v1.0.0-rc3
## explicit
github.com/opencontainers/image-tools/image
# github.com/opencontainers/runc v1.1.9
# github.com/opencontainers/runc v1.1.10
## explicit; go 1.17
github.com/opencontainers/runc/libcontainer/user
# github.com/opencontainers/runtime-spec v1.1.0