Skip to content
This repository was archived by the owner on Jan 15, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions cmd/oci-create-runtime-bundle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ var bundleTypes = []string{
}

type bundleCmd struct {
stdout *log.Logger
stderr *log.Logger
typ string // the type to bundle, can be empty string
ref string
root string
version bool
stdout *log.Logger
stderr *log.Logger
typ string // the type to bundle, can be empty string
ref string
root string
sameOwner bool
version bool
}

func main() {
Expand Down Expand Up @@ -87,10 +88,17 @@ func newBundleCmd(stdout, stderr *log.Logger) *cobra.Command {
It is strongly recommended to keep the default value.`,
)

isUserRoot := os.Getuid() == 0
cmd.Flags().BoolVar(
&v.sameOwner, "same-owner", isUserRoot,
`Preserve the owner and group of the layer entries when unpacking the image (default for superuser, but not for ordinary users).`,
)

cmd.Flags().BoolVar(
&v.version, "version", false,
`Print version information and exit`,
)

return cmd
}

Expand Down Expand Up @@ -123,13 +131,17 @@ func (v *bundleCmd) Run(cmd *cobra.Command, args []string) {
v.typ = typ
}

unpacker := &image.Unpacker{
PreserveOwnership: v.sameOwner,
}

var err error
switch v.typ {
case image.TypeImageLayout:
err = image.CreateRuntimeBundleLayout(args[0], args[1], v.ref, v.root)
err = image.CreateRuntimeBundleLayout(unpacker, args[0], args[1], v.ref, v.root)

case image.TypeImage:
err = image.CreateRuntimeBundle(args[0], args[1], v.ref, v.root)
err = image.CreateRuntimeBundle(unpacker, args[0], args[1], v.ref, v.root)
}

if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions cmd/oci-create-runtime-bundle/oci-create-runtime-bundle.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ runtime-spec-compatible `dest/config.json`.
**--rootfs**
A directory representing the root filesystem of the container in the OCI runtime bundle. It is strongly recommended to keep the default value. (default "rootfs")

**--same-owner**
Preserve the owner and group of the layer entries when unpacking the image (default for superuser, but not for ordinary users).

**--type**
Type of the file to unpack. If unset, oci-create-runtime-bundle will try to auto-detect the type. One of "imageLayout,image"

Expand Down
27 changes: 20 additions & 7 deletions cmd/oci-unpack/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ var unpackTypes = []string{
}

type unpackCmd struct {
stdout *log.Logger
stderr *log.Logger
typ string // the type to unpack, can be empty string
ref string
version bool
stdout *log.Logger
stderr *log.Logger
typ string // the type to unpack, can be empty string
ref string
sameOwner bool
version bool
}

func main() {
Expand Down Expand Up @@ -79,10 +80,18 @@ func newUnpackCmd(stdout, stderr *log.Logger) *cobra.Command {
&v.ref, "ref", "v1.0",
`The ref pointing to the manifest to be unpacked. This must be present in the "refs" subdirectory of the image.`,
)

isUserRoot := os.Getuid() == 0
cmd.Flags().BoolVar(
&v.sameOwner, "same-owner", isUserRoot,
`Preserve the owner and group of the layer entries when unpacking the image (default for superuser, but not for ordinary users).`,
)

cmd.Flags().BoolVar(
&v.version, "version", false,
`Print version information and exit`,
)

return cmd
}

Expand Down Expand Up @@ -110,13 +119,17 @@ func (v *unpackCmd) Run(cmd *cobra.Command, args []string) {
v.typ = typ
}

unpacker := &image.Unpacker{
PreserveOwnership: v.sameOwner,
}

var err error
switch v.typ {
case image.TypeImageLayout:
err = image.UnpackLayout(args[0], args[1], v.ref)
err = image.UnpackLayout(unpacker, args[0], args[1], v.ref)

case image.TypeImage:
err = image.Unpack(args[0], args[1], v.ref)
err = image.Unpack(unpacker, args[0], args[1], v.ref)
}

if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions cmd/oci-unpack/oci-unpack.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ oci-unpack \- Unpack an image or image source layout
**--ref**
The ref pointing to the manifest to be unpacked. This must be present in the "refs" subdirectory of the image. (default "v1.0")

**--same-owner**
Preserve the owner and group of the layer entries when unpacking the image (default for superuser, but not for ordinary users).

**--type**
Type of the file to unpack. If unset, oci-unpack will try to auto-detect the type. One of "imageLayout,image"

Expand Down
24 changes: 12 additions & 12 deletions image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,24 +97,24 @@ func validate(w walker, refs []string, out *log.Logger) error {
// UnpackLayout walks through the file tree given by src and, using the layers
// specified in the manifest pointed to by the given ref, unpacks all layers in
// the given destination directory or returns an error if the unpacking failed.
func UnpackLayout(src, dest, ref string) error {
return unpack(newPathWalker(src), dest, ref)
func UnpackLayout(u *Unpacker, src, dest, ref string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (u *Unpacker) UnpackLayout(src, …) error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel tempted but UnpackLayout (through the helper unpack) seems to be doing more than the Unpacker. Namely, UnpackLayout finds the descriptor, validates it, finds the manifest, validates it, and then proceeds to the actuall process of unpacking the manifest.

I feel that the current boundary between Unpacker and this logic is healthy (Unpacker only knows about a manifest and a walker in the image layout and deals with the process of unpacking the layout). Of course these boundaries usually get shifted as projects move forward but I am comfortable with how things are split now.

Do you have strong feelings about this @wking?

return unpack(u, newPathWalker(src), dest, ref)
}

// Unpack walks through the given .tar file and, using the layers specified in
// the manifest pointed to by the given ref, unpacks all layers in the given
// destination directory or returns an error if the unpacking failed.
func Unpack(tarFile, dest, ref string) error {
func Unpack(u *Unpacker, tarFile, dest, ref string) error {
f, err := os.Open(tarFile)
if err != nil {
return errors.Wrap(err, "unable to open file")
}
defer f.Close()

return unpack(newTarWalker(tarFile, f), dest, ref)
return unpack(u, newTarWalker(tarFile, f), dest, ref)
}

func unpack(w walker, dest, refName string) error {
func unpack(u *Unpacker, w walker, dest, refName string) error {
ref, err := findDescriptor(w, refName)
if err != nil {
return err
Expand All @@ -133,30 +133,30 @@ func unpack(w walker, dest, refName string) error {
return err
}

return m.unpack(w, dest)
return u.unpack(m, w, dest)
}

// CreateRuntimeBundleLayout walks through the file tree given by src and
// creates an OCI runtime bundle in the given destination dest
// or returns an error if the unpacking failed.
func CreateRuntimeBundleLayout(src, dest, ref, root string) error {
return createRuntimeBundle(newPathWalker(src), dest, ref, root)
func CreateRuntimeBundleLayout(u *Unpacker, src, dest, ref, root string) error {
return createRuntimeBundle(u, newPathWalker(src), dest, ref, root)
}

// CreateRuntimeBundle walks through the given .tar file and
// creates an OCI runtime bundle in the given destination dest
// or returns an error if the unpacking failed.
func CreateRuntimeBundle(tarFile, dest, ref, root string) error {
func CreateRuntimeBundle(u *Unpacker, tarFile, dest, ref, root string) error {
f, err := os.Open(tarFile)
if err != nil {
return errors.Wrap(err, "unable to open file")
}
defer f.Close()

return createRuntimeBundle(newTarWalker(tarFile, f), dest, ref, root)
return createRuntimeBundle(u, newTarWalker(tarFile, f), dest, ref, root)
}

func createRuntimeBundle(w walker, dest, refName, rootfs string) error {
func createRuntimeBundle(u *Unpacker, w walker, dest, refName, rootfs string) error {
ref, err := findDescriptor(w, refName)
if err != nil {
return err
Expand All @@ -180,7 +180,7 @@ func createRuntimeBundle(w walker, dest, refName, rootfs string) error {
return err
}

err = m.unpack(w, filepath.Join(dest, rootfs))
err = u.unpack(m, w, filepath.Join(dest, rootfs))
if err != nil {
return err
}
Expand Down
Loading