diff --git a/CHANGELOG.md b/CHANGELOG.md index 171afe052..2087ab3ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add the `cgroup` namespace to the default configuration generated by `umoci unpack` to make sure that our configuration plays nicely with `runc` when on cgroupv2 systems. +- umoci has been migrated away from `github.com/pkg/errors` to Go stdlib error + wrapping. ### Fixed ### - In 0.4.7, a performance regression was introduced as part of the diff --git a/api.go b/api.go index bade081f1..ff2a51d82 100644 --- a/api.go +++ b/api.go @@ -18,9 +18,10 @@ package umoci import ( + "fmt" + "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" - "github.com/pkg/errors" ) // OpenLayout opens an existing OCI image layout, and fails if it does not @@ -29,7 +30,7 @@ func OpenLayout(imagePath string) (casext.Engine, error) { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return casext.Engine{}, errors.Wrap(err, "open CAS") + return casext.Engine{}, fmt.Errorf("open CAS: %w", err) } return casext.NewEngine(engine), nil diff --git a/cmd/umoci/config.go b/cmd/umoci/config.go index 501596662..2df8ccc31 100644 --- a/cmd/umoci/config.go +++ b/cmd/umoci/config.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package main import ( "context" + "errors" + "fmt" "strings" "time" @@ -28,7 +30,6 @@ import ( "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" igen "github.com/opencontainers/umoci/oci/config/generate" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -52,13 +53,13 @@ image.`, // Verify the metadata. Before: func(ctx *cli.Context) error { if ctx.NArg() != 0 { - return errors.Errorf("invalid number of positional arguments: expected none") + return errors.New("invalid number of positional arguments: expected none") } if _, ok := ctx.App.Metadata["--image-path"]; !ok { - return errors.Errorf("missing mandatory argument: --image") + return errors.New("missing mandatory argument: --image") } if _, ok := ctx.App.Metadata["--image-tag"]; !ok { - return errors.Errorf("missing mandatory argument: --image") + return errors.New("missing mandatory argument: --image") } return nil }, @@ -126,12 +127,12 @@ func fromImage(image ispec.Image) (ispec.ImageConfig, mutate.Meta) { func parseKV(input string) (string, string, error) { parts := strings.SplitN(input, "=", 2) if len(parts) != 2 { - return "", "", errors.Errorf("must contain '=': %s", input) + return "", "", fmt.Errorf("must contain '=': %s", input) } name, value := parts[0], parts[1] if name == "" { - return "", "", errors.Errorf("must have non-empty name: %s", input) + return "", "", fmt.Errorf("must have non-empty name: %s", input) } return name, value, nil } @@ -149,46 +150,46 @@ func config(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(fromDescriptorPaths) == 0 { - return errors.Errorf("tag not found: %s", fromName) + return fmt.Errorf("tag not found: %s", fromName) } if len(fromDescriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", fromName) + return fmt.Errorf("tag is ambiguous: %s", fromName) } mutator, err := mutate.New(engine, fromDescriptorPaths[0]) if err != nil { - return errors.Wrap(err, "create mutator for manifest") + return fmt.Errorf("create mutator for manifest: %w", err) } config, err := mutator.Config(context.Background()) if err != nil { - return errors.Wrap(err, "get base config") + return fmt.Errorf("get base config: %w", err) } imageMeta, err := mutator.Meta(context.Background()) if err != nil { - return errors.Wrap(err, "get base metadata") + return fmt.Errorf("get base metadata: %w", err) } annotations, err := mutator.Annotations(context.Background()) if err != nil { - return errors.Wrap(err, "get base annotations") + return fmt.Errorf("get base annotations: %w", err) } g, err := igen.NewFromImage(toImage(config.Config, imageMeta)) if err != nil { - return errors.Wrap(err, "create new generator") + return fmt.Errorf("create new generator: %w", err) } if ctx.IsSet("clear") { @@ -206,13 +207,13 @@ func config(ctx *cli.Context) error { g.ClearConfigVolumes() case "rootfs.diffids": //g.ClearRootfsDiffIDs() - return errors.Errorf("--clear=rootfs.diffids is not safe") + return errors.New("--clear=rootfs.diffids is not safe") case "config.cmd": g.ClearConfigCmd() case "config.entrypoint": g.ClearConfigEntrypoint() default: - return errors.Errorf("unknown key to --clear: %s", key) + return fmt.Errorf("unknown key to --clear: %s", key) } } } @@ -221,7 +222,7 @@ func config(ctx *cli.Context) error { // How do we handle other formats? created, err := time.Parse(igen.ISO8601, ctx.String("created")) if err != nil { - return errors.Wrap(err, "parse --created") + return fmt.Errorf("parse --created: %w", err) } g.SetCreated(created) } @@ -252,7 +253,7 @@ func config(ctx *cli.Context) error { for _, env := range ctx.StringSlice("config.env") { name, value, err := parseKV(env) if err != nil { - return errors.Wrap(err, "config.env") + return fmt.Errorf("config.env: %w", err) } g.AddConfigEnv(name, value) } @@ -274,7 +275,7 @@ func config(ctx *cli.Context) error { for _, label := range ctx.StringSlice("config.label") { name, value, err := parseKV(label) if err != nil { - return errors.Wrap(err, "config.label") + return fmt.Errorf("config.label: %w", err) } g.AddConfigLabel(name, value) } @@ -309,7 +310,7 @@ func config(ctx *cli.Context) error { if ctx.IsSet("history.created") { created, err := time.Parse(igen.ISO8601, ctx.String("history.created")) if err != nil { - return errors.Wrap(err, "parsing --history.created") + return fmt.Errorf("parsing --history.created: %w", err) } history.Created = &created } @@ -320,18 +321,18 @@ func config(ctx *cli.Context) error { newConfig, newMeta := fromImage(g.Image()) if err := mutator.Set(context.Background(), newConfig, newMeta, annotations, history); err != nil { - return errors.Wrap(err, "set modified configuration") + return fmt.Errorf("set modified configuration: %w", err) } newDescriptorPath, err := mutator.Commit(context.Background()) if err != nil { - return errors.Wrap(err, "commit mutated image") + return fmt.Errorf("commit mutated image: %w", err) } log.Infof("new image manifest created: %s->%s", newDescriptorPath.Root().Digest, newDescriptorPath.Descriptor().Digest) if err := engineExt.UpdateReference(context.Background(), tagName, newDescriptorPath.Root()); err != nil { - return errors.Wrap(err, "add new tag") + return fmt.Errorf("add new tag: %w", err) } log.Infof("created new tag for image manifest: %s", tagName) diff --git a/cmd/umoci/gc.go b/cmd/umoci/gc.go index d023fdad4..708642329 100644 --- a/cmd/umoci/gc.go +++ b/cmd/umoci/gc.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,11 @@ package main import ( "context" + "errors" + "fmt" "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -42,10 +43,10 @@ root set of references. All other blobs will be removed.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 0 { - return errors.Errorf("invalid number of positional arguments: expected none") + return errors.New("invalid number of positional arguments: expected none") } if _, ok := ctx.App.Metadata["--image-path"]; !ok { - return errors.Errorf("missing mandatory argument: --layout") + return errors.New("missing mandatory argument: --layout") } return nil }, @@ -59,11 +60,14 @@ func gc(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() // Run the GC. - return errors.Wrap(engineExt.GC(context.Background()), "gc") + if err := engineExt.GC(context.Background()); err != nil { + return fmt.Errorf("gc: %w", err) + } + return nil } diff --git a/cmd/umoci/init.go b/cmd/umoci/init.go index f15c11502..24060615a 100644 --- a/cmd/umoci/init.go +++ b/cmd/umoci/init.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,12 @@ package main import ( + "errors" "fmt" "os" "github.com/apex/log" "github.com/opencontainers/umoci/oci/cas/dir" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -43,7 +43,7 @@ commands.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 0 { - return errors.Errorf("invalid number of positional arguments: expected none") + return errors.New("invalid number of positional arguments: expected none") } return nil }, @@ -54,15 +54,15 @@ commands.`, func initLayout(ctx *cli.Context) error { imagePath := ctx.App.Metadata["--image-path"].(string) - if _, err := os.Stat(imagePath); !os.IsNotExist(err) { + if _, err := os.Stat(imagePath); !errors.Is(err, os.ErrNotExist) { if err == nil { err = fmt.Errorf("path already exists: %s", imagePath) } - return errors.Wrap(err, "image layout creation") + return fmt.Errorf("image layout creation: %w", err) } if err := dir.Create(imagePath); err != nil { - return errors.Wrap(err, "image layout creation") + return fmt.Errorf("image layout creation: %w", err) } log.Infof("created new OCI image: %s", imagePath) diff --git a/cmd/umoci/insert.go b/cmd/umoci/insert.go index b3107fb9f..66f2731da 100644 --- a/cmd/umoci/insert.go +++ b/cmd/umoci/insert.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * Copyright (C) 2018 Cisco Systems * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ package main import ( "context" + "fmt" "time" "github.com/apex/log" @@ -30,7 +31,6 @@ import ( "github.com/opencontainers/umoci/oci/casext" igen "github.com/opencontainers/umoci/oci/config/generate" "github.com/opencontainers/umoci/oci/layer" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -87,11 +87,11 @@ Some examples: numArgs = 1 } if ctx.NArg() != numArgs { - return errors.Errorf("invalid number of positional arguments: expected %d", numArgs) + return fmt.Errorf("invalid number of positional arguments: expected %d", numArgs) } for idx, args := range ctx.Args() { if args == "" { - return errors.Errorf("invalid positional argument %d: arguments cannot be empty", idx) + return fmt.Errorf("invalid positional argument %d: arguments cannot be empty", idx) } } @@ -124,27 +124,27 @@ func insert(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() descriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(descriptorPaths) == 0 { - return errors.Errorf("tag not found: %s", fromName) + return fmt.Errorf("tag not found: %s", fromName) } if len(descriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", fromName) + return fmt.Errorf("tag is ambiguous: %s", fromName) } // Create the mutator. mutator, err := mutate.New(engine, descriptorPaths[0]) if err != nil { - return errors.Wrap(err, "create mutator for base image") + return fmt.Errorf("create mutator for base image: %w", err) } var meta umoci.Meta @@ -179,7 +179,7 @@ func insert(ctx *cli.Context) error { if ctx.IsSet("history.created") { created, err := time.Parse(igen.ISO8601, ctx.String("history.created")) if err != nil { - return errors.Wrap(err, "parsing --history.created") + return fmt.Errorf("parsing --history.created: %w", err) } history.Created = &created } @@ -191,18 +191,18 @@ func insert(ctx *cli.Context) error { // TODO: We should add a flag to allow for a new layer to be made // non-distributable. if _, err := mutator.Add(context.Background(), ispec.MediaTypeImageLayer, reader, history, mutate.GzipCompressor, nil); err != nil { - return errors.Wrap(err, "add diff layer") + return fmt.Errorf("add diff layer: %w", err) } newDescriptorPath, err := mutator.Commit(context.Background()) if err != nil { - return errors.Wrap(err, "commit mutated image") + return fmt.Errorf("commit mutated image: %w", err) } log.Infof("new image manifest created: %s->%s", newDescriptorPath.Root().Digest, newDescriptorPath.Descriptor().Digest) if err := engineExt.UpdateReference(context.Background(), tagName, newDescriptorPath.Root()); err != nil { - return errors.Wrap(err, "add new tag") + return fmt.Errorf("add new tag: %w", err) } log.Infof("updated tag for image manifest: %s", tagName) return nil diff --git a/cmd/umoci/main.go b/cmd/umoci/main.go index e3b614657..7586b2102 100644 --- a/cmd/umoci/main.go +++ b/cmd/umoci/main.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +18,14 @@ package main import ( + "errors" + "fmt" "os" "runtime/pprof" "github.com/apex/log" logcli "github.com/apex/log/handlers/cli" "github.com/opencontainers/umoci" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -77,22 +78,22 @@ func Main(args []string) error { } if err := ctx.GlobalSet("log", "info"); err != nil { // Should _never_ be reached. - return errors.Wrap(err, "[internal error] failure auto-setting --log=info") + return fmt.Errorf("[internal error] failure auto-setting --log=info: %w", err) } } level, err := log.ParseLevel(ctx.GlobalString("log")) if err != nil { - return errors.Wrap(err, "parsing log level") + return fmt.Errorf("parsing log level: %w", err) } log.SetLevel(level) if path := ctx.GlobalString("cpu-profile"); path != "" { fh, err := os.Create(path) if err != nil { - return errors.Wrap(err, "opening cpu-profile path") + return fmt.Errorf("opening cpu-profile path: %w", err) } if err := pprof.StartCPUProfile(fh); err != nil { - return errors.Wrap(err, "start cpu-profile") + return fmt.Errorf("start cpu-profile: %w", err) } } return nil @@ -129,10 +130,10 @@ func Main(args []string) error { oldBefore := cmd.Before cmd.Before = func(ctx *cli.Context) error { if _, ok := ctx.App.Metadata["--image-path"]; !ok { - return errors.Errorf("missing mandatory argument: --image") + return errors.New("missing mandatory argument: --image") } if _, ok := ctx.App.Metadata["--image-tag"]; !ok { - return errors.Errorf("missing mandatory argument: --image") + return errors.New("missing mandatory argument: --image") } if oldBefore != nil { return oldBefore(ctx) @@ -144,7 +145,7 @@ func Main(args []string) error { oldBefore := cmd.Before cmd.Before = func(ctx *cli.Context) error { if _, ok := ctx.App.Metadata["--image-path"]; !ok { - return errors.Errorf("missing mandatory argument: --layout") + return errors.New("missing mandatory argument: --layout") } if oldBefore != nil { return oldBefore(ctx) @@ -160,7 +161,7 @@ func Main(args []string) error { // If an error is a permission based error, give a hint to the user // that --rootless might help. We probably should only be doing this if // we're an unprivileged user. - if os.IsPermission(errors.Cause(err)) { + if errors.Is(err, os.ErrPermission) { log.Warn("umoci encountered a permission error: maybe --rootless will help?") } log.Debugf("%+v", err) diff --git a/cmd/umoci/new.go b/cmd/umoci/new.go index ece193544..89ea773a4 100644 --- a/cmd/umoci/new.go +++ b/cmd/umoci/new.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,12 @@ package main import ( + "errors" + "fmt" + "github.com/opencontainers/umoci" "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -43,7 +45,7 @@ needing a base image to start from.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 0 { - return errors.Errorf("invalid number of positional arguments: expected none") + return errors.New("invalid number of positional arguments: expected none") } return nil }, @@ -58,7 +60,7 @@ func newImage(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() diff --git a/cmd/umoci/raw-add-layer.go b/cmd/umoci/raw-add-layer.go index 945e7f59a..1183c9131 100644 --- a/cmd/umoci/raw-add-layer.go +++ b/cmd/umoci/raw-add-layer.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package main import ( "context" + "errors" + "fmt" "os" "time" @@ -29,7 +31,6 @@ import ( "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" igen "github.com/opencontainers/umoci/oci/config/generate" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -56,10 +57,10 @@ only supports uncompressed archives.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 1 { - return errors.Errorf("invalid number of positional arguments: expected ") + return errors.New("invalid number of positional arguments: expected ") } if ctx.Args().First() == "" { - return errors.Errorf(" path cannot be empty") + return errors.New(" path cannot be empty") } ctx.App.Metadata["newlayer"] = ctx.Args().First() return nil @@ -83,45 +84,45 @@ func rawAddLayer(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(fromDescriptorPaths) == 0 { - return errors.Errorf("tag not found: %s", fromName) + return fmt.Errorf("tag not found: %s", fromName) } if len(fromDescriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", fromName) + return fmt.Errorf("tag is ambiguous: %s", fromName) } meta.From = fromDescriptorPaths[0] // Create the mutator. mutator, err := mutate.New(engine, meta.From) if err != nil { - return errors.Wrap(err, "create mutator for base image") + return fmt.Errorf("create mutator for base image: %w", err) } newLayer, err := os.Open(newLayerPath) if err != nil { - return errors.Wrap(err, "open new layer archive") + return fmt.Errorf("open new layer archive: %w", err) } if fi, err := newLayer.Stat(); err != nil { - return errors.Wrap(err, "stat new layer archive") + return fmt.Errorf("stat new layer archive: %w", err) } else if fi.IsDir() { - return errors.Errorf("new layer archive is a directory") + return errors.New("new layer archive is a directory") } // TODO: Verify that the layer is actually uncompressed. defer newLayer.Close() imageMeta, err := mutator.Meta(context.Background()) if err != nil { - return errors.Wrap(err, "get image metadata") + return fmt.Errorf("get image metadata: %w", err) } var history *ispec.History @@ -144,7 +145,7 @@ func rawAddLayer(ctx *cli.Context) error { if ctx.IsSet("history.created") { created, err := time.Parse(igen.ISO8601, ctx.String("history.created")) if err != nil { - return errors.Wrap(err, "parsing --history.created") + return fmt.Errorf("parsing --history.created: %w", err) } history.Created = &created } @@ -156,18 +157,18 @@ func rawAddLayer(ctx *cli.Context) error { // TODO: We should add a flag to allow for a new layer to be made // non-distributable. if _, err := mutator.Add(context.Background(), ispec.MediaTypeImageLayer, newLayer, history, mutate.GzipCompressor, nil); err != nil { - return errors.Wrap(err, "add diff layer") + return fmt.Errorf("add diff layer: %w", err) } newDescriptorPath, err := mutator.Commit(context.Background()) if err != nil { - return errors.Wrap(err, "commit mutated image") + return fmt.Errorf("commit mutated image: %w", err) } log.Infof("new image manifest created: %s->%s", newDescriptorPath.Root().Digest, newDescriptorPath.Descriptor().Digest) if err := engineExt.UpdateReference(context.Background(), tagName, newDescriptorPath.Root()); err != nil { - return errors.Wrap(err, "add new tag") + return fmt.Errorf("add new tag: %w", err) } log.Infof("created new tag for image manifest: %s", tagName) diff --git a/cmd/umoci/raw-runtime-config.go b/cmd/umoci/raw-runtime-config.go index 1a0802391..2be4c0602 100644 --- a/cmd/umoci/raw-runtime-config.go +++ b/cmd/umoci/raw-runtime-config.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package main import ( "context" + "errors" "fmt" "os" @@ -28,7 +29,6 @@ import ( "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" "github.com/opencontainers/umoci/oci/layer" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -61,10 +61,10 @@ Note that the results of this may not agree with umoci-unpack(1) because the Before: func(ctx *cli.Context) error { if ctx.NArg() != 1 { - return errors.Errorf("invalid number of positional arguments: expected ") + return errors.New("invalid number of positional arguments: expected ") } if ctx.Args().First() == "" { - return errors.Errorf("config.json path cannot be empty") + return errors.New("config.json path cannot be empty") } ctx.App.Metadata["config"] = ctx.Args().First() return nil @@ -88,52 +88,52 @@ func rawConfig(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(fromDescriptorPaths) == 0 { - return errors.Errorf("tag not found: %s", fromName) + return fmt.Errorf("tag not found: %s", fromName) } if len(fromDescriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", fromName) + return fmt.Errorf("tag is ambiguous: %s", fromName) } meta.From = fromDescriptorPaths[0] manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor()) if err != nil { - return errors.Wrap(err, "get manifest") + return fmt.Errorf("get manifest: %w", err) } defer manifestBlob.Close() if manifestBlob.Descriptor.MediaType != ispec.MediaTypeImageManifest { - return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType), "invalid --image tag") + return fmt.Errorf("invalid --image tag: descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType) } // Get the manifest. manifest, ok := manifestBlob.Data.(ispec.Manifest) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) + return fmt.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) } // Generate the configuration. configFile, err := os.Create(configPath) if err != nil { - return errors.Wrap(err, "opening config path") + return fmt.Errorf("opening config path: %w", err) } defer configFile.Close() // Write out the generated config. log.Info("generating config.json") if err := layer.UnpackRuntimeJSON(context.Background(), engineExt, configFile, ctx.String("rootfs"), manifest, &meta.MapOptions); err != nil { - return errors.Wrap(err, "generate config") + return fmt.Errorf("generate config: %w", err) } return nil } diff --git a/cmd/umoci/raw-unpack.go b/cmd/umoci/raw-unpack.go index 902772eff..87e168d8e 100644 --- a/cmd/umoci/raw-unpack.go +++ b/cmd/umoci/raw-unpack.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package main import ( "context" + "errors" "fmt" "github.com/apex/log" @@ -27,7 +28,6 @@ import ( "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" "github.com/opencontainers/umoci/oci/layer" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -54,10 +54,10 @@ is the destination to unpack the image to.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 1 { - return errors.Errorf("invalid number of positional arguments: expected ") + return errors.New("invalid number of positional arguments: expected ") } if ctx.Args().First() == "" { - return errors.Errorf("rootfs path cannot be empty") + return errors.New("rootfs path cannot be empty") } ctx.App.Metadata["rootfs"] = ctx.Args().First() return nil @@ -86,32 +86,32 @@ func rawUnpack(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(fromDescriptorPaths) == 0 { - return errors.Errorf("tag is not found: %s", fromName) + return fmt.Errorf("tag is not found: %s", fromName) } if len(fromDescriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", fromName) + return fmt.Errorf("tag is ambiguous: %s", fromName) } meta.From = fromDescriptorPaths[0] manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor()) if err != nil { - return errors.Wrap(err, "get manifest") + return fmt.Errorf("get manifest: %w", err) } defer manifestBlob.Close() if manifestBlob.Descriptor.MediaType != ispec.MediaTypeImageManifest { - return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType), "invalid --image tag") + return fmt.Errorf("invalid --image tag: descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType) } log.WithFields(log.Fields{ @@ -124,12 +124,12 @@ func rawUnpack(ctx *cli.Context) error { manifest, ok := manifestBlob.Data.(ispec.Manifest) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) + return fmt.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) } log.Warnf("unpacking rootfs ...") if err := layer.UnpackRootfs(context.Background(), engineExt, rootfsPath, manifest, &unpackOptions); err != nil { - return errors.Wrap(err, "create rootfs") + return fmt.Errorf("create rootfs: %w", err) } log.Warnf("... done") diff --git a/cmd/umoci/repack.go b/cmd/umoci/repack.go index df0b2352d..1c5b33fb7 100644 --- a/cmd/umoci/repack.go +++ b/cmd/umoci/repack.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package main import ( "context" + "errors" "fmt" "time" @@ -30,7 +31,6 @@ import ( "github.com/opencontainers/umoci/oci/casext" igen "github.com/opencontainers/umoci/oci/config/generate" "github.com/opencontainers/umoci/pkg/mtreefilter" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -80,10 +80,10 @@ manifest and configuration information uses the new diff atop the old manifest.` Before: func(ctx *cli.Context) error { if ctx.NArg() != 1 { - return errors.Errorf("invalid number of positional arguments: expected ") + return errors.New("invalid number of positional arguments: expected ") } if ctx.Args().First() == "" { - return errors.Errorf("bundle path cannot be empty") + return errors.New("bundle path cannot be empty") } ctx.App.Metadata["bundle"] = ctx.Args().First() return nil @@ -98,7 +98,7 @@ func repack(ctx *cli.Context) error { // Read the metadata first. meta, err := umoci.ReadBundleMeta(bundlePath) if err != nil { - return errors.Wrap(err, "read umoci.json metadata") + return fmt.Errorf("read umoci.json metadata: %w", err) } log.WithFields(log.Fields{ @@ -108,13 +108,13 @@ func repack(ctx *cli.Context) error { }).Debugf("umoci: loaded Meta metadata") if meta.From.Descriptor().MediaType != ispec.MediaTypeImageManifest { - return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", meta.From.Descriptor().MediaType), "invalid saved from descriptor") + return fmt.Errorf("invalid saved from descriptor: descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", meta.From.Descriptor().MediaType) } // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() @@ -122,13 +122,13 @@ func repack(ctx *cli.Context) error { // Create the mutator. mutator, err := mutate.New(engineExt, meta.From) if err != nil { - return errors.Wrap(err, "create mutator for base image") + return fmt.Errorf("create mutator for base image: %w", err) } // We need to mask config.Volumes. config, err := mutator.Config(context.Background()) if err != nil { - return errors.Wrap(err, "get config") + return fmt.Errorf("get config: %w", err) } maskedPaths := ctx.StringSlice("mask-path") @@ -140,7 +140,7 @@ func repack(ctx *cli.Context) error { imageMeta, err := mutator.Meta(context.Background()) if err != nil { - return errors.Wrap(err, "get image metadata") + return fmt.Errorf("get image metadata: %w", err) } var history *ispec.History @@ -163,7 +163,7 @@ func repack(ctx *cli.Context) error { if ctx.IsSet("history.created") { created, err := time.Parse(igen.ISO8601, ctx.String("history.created")) if err != nil { - return errors.Wrap(err, "parsing --history.created") + return fmt.Errorf("parsing --history.created: %w", err) } history.Created = &created } diff --git a/cmd/umoci/stat.go b/cmd/umoci/stat.go index 14682bba8..74f69c50a 100644 --- a/cmd/umoci/stat.go +++ b/cmd/umoci/stat.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package main import ( "context" "encoding/json" + "errors" "fmt" "os" @@ -27,7 +28,6 @@ import ( "github.com/opencontainers/umoci" "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -48,7 +48,7 @@ humans to read, and might change in future versions.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 0 { - return errors.Errorf("invalid number of positional arguments: expected none") + return errors.New("invalid number of positional arguments: expected none") } return nil }, @@ -70,44 +70,44 @@ func stat(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() manifestDescriptorPaths, err := engineExt.ResolveReference(context.Background(), tagName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(manifestDescriptorPaths) == 0 { - return errors.Errorf("tag not found: %s", tagName) + return fmt.Errorf("tag not found: %s", tagName) } if len(manifestDescriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", tagName) + return fmt.Errorf("tag is ambiguous: %s", tagName) } manifestDescriptor := manifestDescriptorPaths[0].Descriptor() // FIXME: Implement support for manifest lists. if manifestDescriptor.MediaType != ispec.MediaTypeImageManifest { - return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestDescriptor.MediaType), "invalid saved from descriptor") + return fmt.Errorf("invalid saved from descriptor: descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestDescriptor.MediaType) } // Get stat information. ms, err := umoci.Stat(context.Background(), engineExt, manifestDescriptor) if err != nil { - return errors.Wrap(err, "stat") + return fmt.Errorf("stat: %w", err) } // Output the stat information. if ctx.Bool("json") { // Use JSON. if err := json.NewEncoder(os.Stdout).Encode(ms); err != nil { - return errors.Wrap(err, "encoding stat") + return fmt.Errorf("encoding stat: %w", err) } } else { if err := ms.Format(os.Stdout); err != nil { - return errors.Wrap(err, "format stat") + return fmt.Errorf("format stat: %w", err) } } diff --git a/cmd/umoci/tag.go b/cmd/umoci/tag.go index d2a90b034..f5450e42c 100644 --- a/cmd/umoci/tag.go +++ b/cmd/umoci/tag.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,12 @@ package main import ( "context" + "errors" "fmt" "github.com/apex/log" "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -43,13 +43,13 @@ the tag and "" is the new name of the tag.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 1 { - return errors.Errorf("invalid number of positional arguments: expected ") + return errors.New("invalid number of positional arguments: expected ") } if ctx.Args().First() == "" { - return errors.Errorf("new tag cannot be empty") + return errors.New("new tag cannot be empty") } if !casext.IsValidReferenceName(ctx.Args().First()) { - return errors.Errorf("new tag is an invalid reference") + return errors.New("new tag is an invalid reference") } ctx.App.Metadata["new-tag"] = ctx.Args().First() return nil @@ -64,7 +64,7 @@ func tagAdd(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() @@ -72,20 +72,20 @@ func tagAdd(ctx *cli.Context) error { // Get original descriptor. descriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(descriptorPaths) == 0 { - return errors.Errorf("tag not found: %s", fromName) + return fmt.Errorf("tag not found: %s", fromName) } if len(descriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", fromName) + return fmt.Errorf("tag is ambiguous: %s", fromName) } descriptor := descriptorPaths[0].Descriptor() // Add it. if err := engineExt.UpdateReference(context.Background(), tagName, descriptor); err != nil { - return errors.Wrap(err, "put reference") + return fmt.Errorf("put reference: %w", err) } log.Infof("created new tag: %q -> %q", tagName, fromName) @@ -107,7 +107,7 @@ tag to remove.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 0 { - return errors.Errorf("invalid number of positional arguments: expected none") + return errors.New("invalid number of positional arguments: expected none") } return nil }, @@ -122,14 +122,14 @@ func tagRemove(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() // Remove it. if err := engineExt.DeleteReference(context.Background(), tagName); err != nil { - return errors.Wrap(err, "delete reference") + return fmt.Errorf("delete reference: %w", err) } log.Infof("removed tag: %s", tagName) @@ -152,7 +152,7 @@ line. See umoci-stat(1) to get more information about each tagged image.`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 0 { - return errors.Errorf("invalid number of positional arguments: expected none") + return errors.New("invalid number of positional arguments: expected none") } return nil }, @@ -166,14 +166,14 @@ func tagList(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() names, err := engineExt.ListReferences(context.Background()) if err != nil { - return errors.Wrap(err, "list references") + return fmt.Errorf("list references: %w", err) } for _, name := range names { diff --git a/cmd/umoci/unpack.go b/cmd/umoci/unpack.go index 256815876..dd617371d 100644 --- a/cmd/umoci/unpack.go +++ b/cmd/umoci/unpack.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,13 @@ package main import ( + "errors" + "fmt" + "github.com/opencontainers/umoci" "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" "github.com/opencontainers/umoci/oci/layer" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -53,10 +55,10 @@ creation with umoci-repack(1).`, Before: func(ctx *cli.Context) error { if ctx.NArg() != 1 { - return errors.Errorf("invalid number of positional arguments: expected ") + return errors.New("invalid number of positional arguments: expected ") } if ctx.Args().First() == "" { - return errors.Errorf("bundle path cannot be empty") + return errors.New("bundle path cannot be empty") } ctx.App.Metadata["bundle"] = ctx.Args().First() return nil @@ -84,7 +86,7 @@ func unpack(ctx *cli.Context) error { // Get a reference to the CAS. engine, err := dir.Open(imagePath) if err != nil { - return errors.Wrap(err, "open CAS") + return fmt.Errorf("open CAS: %w", err) } engineExt := casext.NewEngine(engine) defer engine.Close() diff --git a/cmd/umoci/utils_ux.go b/cmd/umoci/utils_ux.go index bdc914145..df555db29 100644 --- a/cmd/umoci/utils_ux.go +++ b/cmd/umoci/utils_ux.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ package main import ( + "errors" "fmt" "strings" "github.com/opencontainers/umoci/oci/casext" - "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -73,7 +73,7 @@ func uxHistory(cmd cli.Command) cli.Command { if name := flag.GetName(); name == "no-history" { continue } else if ctx.IsSet(name) { - return errors.Errorf("--no-history and --%s may not be specified together", name) + return fmt.Errorf("--no-history and --%s may not be specified together", name) } } } @@ -103,10 +103,10 @@ func uxTag(cmd cli.Command) cli.Command { if ctx.IsSet("tag") { tag := ctx.String("tag") if !casext.IsValidReferenceName(tag) { - return errors.Wrap(fmt.Errorf("tag contains invalid characters: '%s'", tag), "invalid --tag") + return fmt.Errorf("invalid --tag: tag contains invalid characters: %q", tag) } if tag == "" { - return errors.Wrap(fmt.Errorf("tag is empty"), "invalid --tag") + return errors.New("invalid --tag: tag is empty") } ctx.App.Metadata["--tag"] = tag } @@ -150,15 +150,15 @@ func uxImage(cmd cli.Command) cli.Command { // Verify directory value. if dir == "" { - return errors.Wrap(fmt.Errorf("path is empty"), "invalid --image") + return errors.New("invalid --image: path is empty") } // Verify tag value. if !casext.IsValidReferenceName(tag) { - return errors.Wrap(fmt.Errorf("tag contains invalid characters: '%s'", tag), "invalid --image") + return fmt.Errorf("invalid --image: tag contains invalid characters: %q", tag) } if tag == "" { - return errors.Wrap(fmt.Errorf("tag is empty"), "invalid --image") + return errors.New("invalid --image: tag is empty") } ctx.App.Metadata["--image-path"] = dir @@ -191,10 +191,10 @@ func uxLayout(cmd cli.Command) cli.Command { // Verify directory value. if strings.Contains(layout, ":") { - return errors.Wrap(fmt.Errorf("path contains ':' character: '%s'", layout), "invalid --layout") + return fmt.Errorf("invalid --layout: path contains ':' character: %q", layout) } if layout == "" { - return errors.Wrap(fmt.Errorf("path is empty"), "invalid --layout") + return errors.New("invalid --layout: path is empty") } ctx.App.Metadata["--image-path"] = layout diff --git a/go.mod b/go.mod index 897f4ee7c..1a5c36399 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.2 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 - github.com/pkg/errors v0.9.1 github.com/rootless-containers/proto/go-proto v0.0.0-20230421021042-4cd87ebadd67 github.com/stretchr/testify v1.9.0 github.com/urfave/cli v1.22.12 @@ -32,6 +31,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect diff --git a/mutate/compress.go b/mutate/compress.go index 175de5dbe..4039fd617 100644 --- a/mutate/compress.go +++ b/mutate/compress.go @@ -1,6 +1,7 @@ package mutate import ( + "fmt" "io" "io/ioutil" "runtime" @@ -9,7 +10,6 @@ import ( zstd "github.com/klauspost/compress/zstd" gzip "github.com/klauspost/pgzip" "github.com/opencontainers/umoci/pkg/system" - "github.com/pkg/errors" ) // Compressor is an interface which users can use to implement different @@ -57,21 +57,21 @@ func (gz *gzipCompressor) Compress(reader io.Reader) (io.ReadCloser, error) { gzw := gzip.NewWriter(pipeWriter) if err := gzw.SetConcurrency(256<<10, 2*runtime.NumCPU()); err != nil { - return nil, errors.Wrapf(err, "set concurrency level to %v blocks", 2*runtime.NumCPU()) + return nil, fmt.Errorf("set concurrency level to %v blocks: %w", 2*runtime.NumCPU(), err) } go func() { bytesRead, err := system.Copy(gzw, reader) if err != nil { log.Warnf("gzip compress: could not compress layer: %v", err) // #nosec G104 - _ = pipeWriter.CloseWithError(errors.Wrap(err, "compressing layer")) + _ = pipeWriter.CloseWithError(fmt.Errorf("compressing layer: %w", err)) return } gz.bytesRead = bytesRead if err := gzw.Close(); err != nil { log.Warnf("gzip compress: could not close gzip writer: %v", err) // #nosec G104 - _ = pipeWriter.CloseWithError(errors.Wrap(err, "close gzip writer")) + _ = pipeWriter.CloseWithError(fmt.Errorf("close gzip writer: %w", err)) return } if err := pipeWriter.Close(); err != nil { @@ -111,14 +111,14 @@ func (zs *zstdCompressor) Compress(reader io.Reader) (io.ReadCloser, error) { if err != nil { log.Warnf("zstd compress: could not compress layer: %v", err) // #nosec G104 - _ = pipeWriter.CloseWithError(errors.Wrap(err, "compressing layer")) + _ = pipeWriter.CloseWithError(fmt.Errorf("compressing layer: %w", err)) return } zs.bytesRead = bytesRead if err := zw.Close(); err != nil { log.Warnf("zstd compress: could not close gzip writer: %v", err) // #nosec G104 - _ = pipeWriter.CloseWithError(errors.Wrap(err, "close zstd writer")) + _ = pipeWriter.CloseWithError(fmt.Errorf("close zstd writer: %w", err)) return } if err := pipeWriter.Close(); err != nil { diff --git a/mutate/mutate.go b/mutate/mutate.go index f0c8f5f05..a975110e0 100644 --- a/mutate/mutate.go +++ b/mutate/mutate.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,9 +34,14 @@ import ( ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/umoci/oci/cas" "github.com/opencontainers/umoci/oci/casext" - "github.com/pkg/errors" ) +// UmociUncompressedBlobSizeAnnotation is an umoci-specific annotation to +// provide information in descriptors to compressed blobs about the size of the +// underlying uncompressed blob for users that need that information. Note that +// this annotation value should be treated as a hint -- an attacker could +// create an image that has a dummy UmociUncompressedBlobSizeAnnotation value +// for a zip-bomb blob. const UmociUncompressedBlobSizeAnnotation = "ci.umo.uncompressed_blob_size" func configPtr(c ispec.Image) *ispec.Image { return &c } @@ -97,14 +102,14 @@ func (m *Mutator) cache(ctx context.Context) error { if m.manifest == nil { blob, err := m.engine.FromDescriptor(ctx, m.source.Descriptor()) if err != nil { - return errors.Wrap(err, "cache source manifest") + return fmt.Errorf("cache source manifest: %w", err) } defer blob.Close() manifest, ok := blob.Data.(ispec.Manifest) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] unknown manifest blob type: %s", blob.Descriptor.MediaType) + return fmt.Errorf("[internal error] unknown manifest blob type: %s", blob.Descriptor.MediaType) } // Make a copy of the manifest. @@ -114,14 +119,14 @@ func (m *Mutator) cache(ctx context.Context) error { if m.config == nil { blob, err := m.engine.FromDescriptor(ctx, m.manifest.Config) if err != nil { - return errors.Wrap(err, "cache source config") + return fmt.Errorf("cache source config: %w", err) } defer blob.Close() config, ok := blob.Data.(ispec.Image) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] unknown config blob type: %s", blob.Descriptor.MediaType) + return fmt.Errorf("[internal error] unknown config blob type: %s", blob.Descriptor.MediaType) } // Make a copy of the config and configDescriptor. @@ -136,7 +141,7 @@ func (m *Mutator) cache(ctx context.Context) error { func New(engine cas.Engine, src casext.DescriptorPath) (*Mutator, error) { // We currently only support changing a given manifest through a walk. if mt := src.Descriptor().MediaType; mt != ispec.MediaTypeImageManifest { - return nil, errors.Errorf("unsupported source type: %s", mt) + return nil, fmt.Errorf("unsupported source type: %s", mt) } return &Mutator{ @@ -150,7 +155,7 @@ func New(engine cas.Engine, src casext.DescriptorPath) (*Mutator, error) { // Set. func (m *Mutator) Config(ctx context.Context) (ispec.Image, error) { if err := m.cache(ctx); err != nil { - return ispec.Image{}, errors.Wrap(err, "getting cache failed") + return ispec.Image{}, fmt.Errorf("getting cache failed: %w", err) } return *m.config, nil @@ -161,7 +166,7 @@ func (m *Mutator) Config(ctx context.Context) (ispec.Image, error) { // Commit()ed if no further changes are made. func (m *Mutator) Manifest(ctx context.Context) (ispec.Manifest, error) { if err := m.cache(ctx); err != nil { - return ispec.Manifest{}, errors.Wrap(err, "getting cache failed") + return ispec.Manifest{}, fmt.Errorf("getting cache failed: %w", err) } return *m.manifest, nil @@ -171,7 +176,7 @@ func (m *Mutator) Manifest(ctx context.Context) (ispec.Manifest, error) { // the source for any modifications of the configuration using Set. func (m *Mutator) Meta(ctx context.Context) (Meta, error) { if err := m.cache(ctx); err != nil { - return Meta{}, errors.Wrap(err, "getting cache failed") + return Meta{}, fmt.Errorf("getting cache failed: %w", err) } var created time.Time @@ -192,7 +197,7 @@ func (m *Mutator) Meta(ctx context.Context) (Meta, error) { // Set. func (m *Mutator) Annotations(ctx context.Context) (map[string]string, error) { if err := m.cache(ctx); err != nil { - return nil, errors.Wrap(err, "getting cache failed") + return nil, fmt.Errorf("getting cache failed: %w", err) } annotations := map[string]string{} @@ -207,7 +212,7 @@ func (m *Mutator) Annotations(ctx context.Context) (map[string]string, error) { // correspond to what operations were made to the configuration. func (m *Mutator) Set(ctx context.Context, config ispec.ImageConfig, meta Meta, annotations map[string]string, history *ispec.History) error { if err := m.cache(ctx); err != nil { - return errors.Wrap(err, "getting cache failed") + return fmt.Errorf("getting cache failed: %w", err) } // Ensure the mediatype is correct. @@ -254,7 +259,7 @@ func (m *Mutator) appendToConfig(history *ispec.History, layerDiffID digest.Dige // layer (which is compressed by us). func (m *Mutator) add(ctx context.Context, reader io.Reader, history *ispec.History, compressor Compressor) (digest.Digest, int64, error) { if err := m.cache(ctx); err != nil { - return "", -1, errors.Wrap(err, "getting cache failed") + return "", -1, fmt.Errorf("getting cache failed: %w", err) } diffidDigester := cas.BlobAlgorithm.Digester() @@ -262,13 +267,13 @@ func (m *Mutator) add(ctx context.Context, reader io.Reader, history *ispec.Hist compressed, err := compressor.Compress(hashReader) if err != nil { - return "", -1, errors.Wrapf(err, "couldn't create compression for blob") + return "", -1, fmt.Errorf("couldn't create compression for blob: %w", err) } defer compressed.Close() layerDigest, layerSize, err := m.engine.PutBlob(ctx, compressed) if err != nil { - return "", -1, errors.Wrap(err, "put layer blob") + return "", -1, fmt.Errorf("put layer blob: %w", err) } // Add DiffID to configuration. @@ -285,12 +290,12 @@ func (m *Mutator) add(ctx context.Context, reader io.Reader, history *ispec.Hist func (m *Mutator) Add(ctx context.Context, mediaType string, r io.Reader, history *ispec.History, compressor Compressor, annotations map[string]string) (ispec.Descriptor, error) { desc := ispec.Descriptor{} if err := m.cache(ctx); err != nil { - return desc, errors.Wrap(err, "getting cache failed") + return desc, fmt.Errorf("getting cache failed: %w", err) } digest, size, err := m.add(ctx, r, history, compressor) if err != nil { - return desc, errors.Wrap(err, "add layer") + return desc, fmt.Errorf("add layer: %w", err) } compressedMediaType := mediaType @@ -321,7 +326,7 @@ func (m *Mutator) Add(ctx context.Context, mediaType string, r io.Reader, histor // validate the DiffID. func (m *Mutator) AddExisting(ctx context.Context, desc ispec.Descriptor, history *ispec.History, diffID digest.Digest) error { if err := m.cache(ctx); err != nil { - return errors.Wrap(err, "getting cache failed") + return fmt.Errorf("getting cache failed: %w", err) } m.appendToConfig(history, diffID) @@ -335,13 +340,13 @@ func (m *Mutator) AddExisting(ctx context.Context, desc ispec.Descriptor, histor // New). func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { if err := m.cache(ctx); err != nil { - return casext.DescriptorPath{}, errors.Wrap(err, "getting cache failed") + return casext.DescriptorPath{}, fmt.Errorf("getting cache failed: %w", err) } // We first have to commit the configuration blob. configDigest, configSize, err := m.engine.PutBlobJSON(ctx, m.config) if err != nil { - return casext.DescriptorPath{}, errors.Wrap(err, "commit mutated config blob") + return casext.DescriptorPath{}, fmt.Errorf("commit mutated config blob: %w", err) } m.manifest.Config = ispec.Descriptor{ @@ -353,7 +358,7 @@ func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { // Now commit the manifest. manifestDigest, manifestSize, err := m.engine.PutBlobJSON(ctx, m.manifest) if err != nil { - return casext.DescriptorPath{}, errors.Wrap(err, "commit mutated manifest blob") + return casext.DescriptorPath{}, fmt.Errorf("commit mutated manifest blob: %w", err) } // We now have to create a new DescriptorPath that replaces the one we were @@ -375,7 +380,7 @@ func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { // Get the blob of the parent. parentBlob, err := m.engine.FromDescriptor(ctx, newPath.Walk[idx-1]) if err != nil { - return casext.DescriptorPath{}, errors.Wrapf(err, "get parent-%d blob", idx) + return casext.DescriptorPath{}, fmt.Errorf("get parent-%d blob: %w", idx, err) } defer parentBlob.Close() @@ -389,7 +394,7 @@ func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { } return d }); err != nil { - return casext.DescriptorPath{}, errors.Wrapf(err, "rewrite parent-%d blob", idx) + return casext.DescriptorPath{}, fmt.Errorf("rewrite parent-%d blob: %w", idx, err) } // Re-commit the blob. @@ -397,7 +402,7 @@ func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { // possible to write a modified blob through the blob API. blobDigest, blobSize, err := m.engine.PutBlobJSON(ctx, parentBlob.Data) if err != nil { - return casext.DescriptorPath{}, errors.Wrapf(err, "put json parent-%d blob", idx) + return casext.DescriptorPath{}, fmt.Errorf("put json parent-%d blob: %w", idx, err) } // Update the key parts of the descriptor. diff --git a/mutate/mutate_test.go b/mutate/mutate_test.go index 3fc488435..1d46a2a7c 100644 --- a/mutate/mutate_test.go +++ b/mutate/mutate_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -261,7 +261,7 @@ func TestMutateAdd(t *testing.T) { t.Errorf("manifest.Layers[1].Annotations['hello'] was not set correctly!: %+v", mutator.manifest.Layers[1].Annotations) } if mutator.manifest.Layers[1].Annotations[UmociUncompressedBlobSizeAnnotation] != fmt.Sprintf("%d", bufferSize) { - t.Errorf("manifest.Layers[1].Annotations['%s'] was not set correctly!: %q, expected %d", + t.Errorf("manifest.Layers[1].Annotations[%q] was not set correctly!: %q, expected %d", UmociUncompressedBlobSizeAnnotation, mutator.manifest.Layers[1].Annotations[UmociUncompressedBlobSizeAnnotation], bufferSize) diff --git a/new.go b/new.go index d9b10a2d7..a800069ed 100644 --- a/new.go +++ b/new.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package umoci import ( "context" + "fmt" "runtime" "time" @@ -27,7 +28,6 @@ import ( ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/umoci/oci/casext" igen "github.com/opencontainers/umoci/oci/config/generate" - "github.com/pkg/errors" ) // NewImage creates a new empty image (tag) in the existing layout. @@ -55,7 +55,7 @@ func NewImage(engineExt casext.Engine, tagName string) error { config := g.Image() configDigest, configSize, err := engineExt.PutBlobJSON(context.Background(), config) if err != nil { - return errors.Wrap(err, "put config blob") + return fmt.Errorf("put config blob: %w", err) } log.WithFields(log.Fields{ @@ -80,7 +80,7 @@ func NewImage(engineExt casext.Engine, tagName string) error { manifestDigest, manifestSize, err := engineExt.PutBlobJSON(context.Background(), manifest) if err != nil { - return errors.Wrap(err, "put manifest blob") + return fmt.Errorf("put manifest blob: %w", err) } log.WithFields(log.Fields{ @@ -101,7 +101,7 @@ func NewImage(engineExt casext.Engine, tagName string) error { log.Infof("new image manifest created: %s", descriptor.Digest) if err := engineExt.UpdateReference(context.Background(), tagName, descriptor); err != nil { - return errors.Wrap(err, "add new tag") + return fmt.Errorf("add new tag: %w", err) } log.Infof("created new tag for image manifest: %s", tagName) diff --git a/oci/cas/cas.go b/oci/cas/cas.go index 5fa51daf2..3f8aac35a 100644 --- a/oci/cas/cas.go +++ b/oci/cas/cas.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ package cas import ( "context" - "fmt" + "errors" "io" // We need to include sha256 in order for go-digest to properly handle such @@ -41,23 +41,23 @@ const ( var ( // ErrNotExist is effectively an implementation-neutral version of // os.ErrNotExist. - ErrNotExist = fmt.Errorf("no such blob or index") + ErrNotExist = errors.New("no such blob or index") // ErrInvalid is returned when an image was detected as being invalid. - ErrInvalid = fmt.Errorf("invalid image detected") + ErrInvalid = errors.New("invalid image detected") // ErrUnknownType is returned when an unknown (or otherwise unparseable) // mediatype is encountered. Callers should not ignore this error unless it // is in a context where ignoring it is more friendly to spec extensions. - ErrUnknownType = fmt.Errorf("unknown mediatype encountered") + ErrUnknownType = errors.New("unknown mediatype encountered") // ErrNotImplemented is returned when a requested operation has not been // implementing the backing image store. - ErrNotImplemented = fmt.Errorf("operation not implemented") + ErrNotImplemented = errors.New("operation not implemented") // ErrClobber is returned when a requested operation would require clobbering a // reference or blob which already exists. - ErrClobber = fmt.Errorf("operation would clobber existing object") + ErrClobber = errors.New("operation would clobber existing object") ) // Engine is an interface that provides methods for accessing and modifying an diff --git a/oci/cas/dir/dir.go b/oci/cas/dir/dir.go index 3fc7f87ea..be6b49584 100644 --- a/oci/cas/dir/dir.go +++ b/oci/cas/dir/dir.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ package dir import ( "context" "encoding/json" + "errors" + "fmt" "io" "io/ioutil" "os" @@ -32,7 +34,6 @@ import ( "github.com/opencontainers/umoci/oci/cas" "github.com/opencontainers/umoci/pkg/hardening" "github.com/opencontainers/umoci/pkg/system" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -59,14 +60,14 @@ const ( // of the OCI image. The digest must be of the form algorithm:hex. func blobPath(digest digest.Digest) (string, error) { if err := digest.Validate(); err != nil { - return "", errors.Wrapf(err, "invalid digest: %q", digest) + return "", fmt.Errorf("invalid digest: %q: %w", digest, err) } algo := digest.Algorithm() hash := digest.Hex() if algo != cas.BlobAlgorithm { - return "", errors.Errorf("unsupported algorithm: %q", algo) + return "", fmt.Errorf("unsupported algorithm: %q", algo) } return filepath.Join(blobDirectory, algo.String(), hash), nil @@ -82,7 +83,7 @@ func (e *dirEngine) ensureTempDir() error { if e.temp == "" { tempDir, err := ioutil.TempDir(e.path, ".umoci-") if err != nil { - return errors.Wrap(err, "create tempdir") + return fmt.Errorf("create tempdir: %w", err) } // We get an advisory lock to ensure that GC() won't delete our @@ -91,10 +92,10 @@ func (e *dirEngine) ensureTempDir() error { e.tempFile, err = os.Open(tempDir) if err != nil { - return errors.Wrap(err, "open tempdir for lock") + return fmt.Errorf("open tempdir for lock: %w", err) } if err := unix.Flock(int(e.tempFile.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil { - return errors.Wrap(err, "lock tempdir") + return fmt.Errorf("lock tempdir: %w", err) } e.temp = tempDir @@ -106,21 +107,21 @@ func (e *dirEngine) ensureTempDir() error { func (e *dirEngine) validate() error { content, err := ioutil.ReadFile(filepath.Join(e.path, layoutFile)) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { err = cas.ErrInvalid } - return errors.Wrap(err, "read oci-layout") + return fmt.Errorf("read oci-layout: %w", err) } var ociLayout ispec.ImageLayout if err := json.Unmarshal(content, &ociLayout); err != nil { - return errors.Wrap(err, "parse oci-layout") + return fmt.Errorf("parse oci-layout: %w", err) } // XXX: Currently the meaning of this field is not adequately defined by // the spec, nor is the "official" value determined by the spec. if ociLayout.Version != ImageLayoutVersion { - return errors.Wrap(cas.ErrInvalid, "layout version is not supported") + return fmt.Errorf("layout version is not supported: %w", cas.ErrInvalid) } // Check that "blobs" and "index.json" exist in the image. @@ -128,21 +129,21 @@ func (e *dirEngine) validate() error { // directory (with no subdirectories) and that refs *only* contains // files (optionally also making sure they're all JSON descriptors). if fi, err := os.Stat(filepath.Join(e.path, blobDirectory)); err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { err = cas.ErrInvalid } - return errors.Wrap(err, "check blobdir") + return fmt.Errorf("check blobdir: %w", err) } else if !fi.IsDir() { - return errors.Wrap(cas.ErrInvalid, "blobdir is not a directory") + return fmt.Errorf("blobdir is not a directory: %w", cas.ErrInvalid) } if fi, err := os.Stat(filepath.Join(e.path, indexFile)); err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { err = cas.ErrInvalid } - return errors.Wrap(err, "check index") + return fmt.Errorf("check index: %w", err) } else if fi.IsDir() { - return errors.Wrap(cas.ErrInvalid, "index is a directory") + return fmt.Errorf("index is a directory: %w", cas.ErrInvalid) } return nil @@ -153,7 +154,7 @@ func (e *dirEngine) validate() error { // of this PutBlob() call". func (e *dirEngine) PutBlob(ctx context.Context, reader io.Reader) (digest.Digest, int64, error) { if err := e.ensureTempDir(); err != nil { - return "", -1, errors.Wrap(err, "ensure tempdir") + return "", -1, fmt.Errorf("ensure tempdir: %w", err) } digester := cas.BlobAlgorithm.Digester() @@ -162,7 +163,7 @@ func (e *dirEngine) PutBlob(ctx context.Context, reader io.Reader) (digest.Diges // but also to avoid half-writing an invalid blob. fh, err := ioutil.TempFile(e.temp, "blob-") if err != nil { - return "", -1, errors.Wrap(err, "create temporary blob") + return "", -1, fmt.Errorf("create temporary blob: %w", err) } tempPath := fh.Name() defer fh.Close() @@ -170,22 +171,22 @@ func (e *dirEngine) PutBlob(ctx context.Context, reader io.Reader) (digest.Diges writer := io.MultiWriter(fh, digester.Hash()) size, err := system.Copy(writer, reader) if err != nil { - return "", -1, errors.Wrap(err, "copy to temporary blob") + return "", -1, fmt.Errorf("copy to temporary blob: %w", err) } if err := fh.Close(); err != nil { - return "", -1, errors.Wrap(err, "close temporary blob") + return "", -1, fmt.Errorf("close temporary blob: %w", err) } // Get the digest. path, err := blobPath(digester.Digest()) if err != nil { - return "", -1, errors.Wrap(err, "compute blob name") + return "", -1, fmt.Errorf("compute blob name: %w", err) } // Move the blob to its correct path. path = filepath.Join(e.path, path) if err := os.Rename(tempPath, path); err != nil { - return "", -1, errors.Wrap(err, "rename temporary blob") + return "", -1, fmt.Errorf("rename temporary blob: %w", err) } return digester.Digest(), int64(size), nil @@ -205,14 +206,17 @@ func (e *dirEngine) PutBlob(ctx context.Context, reader io.Reader) (digest.Diges func (e *dirEngine) GetBlob(ctx context.Context, digest digest.Digest) (io.ReadCloser, error) { path, err := blobPath(digest) if err != nil { - return nil, errors.Wrap(err, "compute blob path") + return nil, fmt.Errorf("compute blob path: %w", err) } fh, err := os.Open(filepath.Join(e.path, path)) + if err != nil { + return nil, fmt.Errorf("open blob: %w", err) + } return &hardening.VerifiedReadCloser{ Reader: fh, ExpectedDigest: digest, ExpectedSize: int64(-1), // We don't know the expected size. - }, errors.Wrap(err, "open blob") + }, nil } // StatBlob returns whether the specified blob exists in the image. Returns @@ -223,14 +227,14 @@ func (e *dirEngine) GetBlob(ctx context.Context, digest digest.Digest) (io.ReadC func (e *dirEngine) StatBlob(ctx context.Context, digest digest.Digest) (bool, error) { path, err := blobPath(digest) if err != nil { - return false, errors.Wrap(err, "compute blob path") + return false, fmt.Errorf("compute blob path: %w", err) } _, err = os.Stat(path) - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { return false, nil } if err != nil { - return false, errors.Wrap(err, "stat blob path") + return false, fmt.Errorf("stat blob path: %w", err) } return true, nil } @@ -241,7 +245,7 @@ func (e *dirEngine) StatBlob(ctx context.Context, digest digest.Digest) (bool, e // new or old index. func (e *dirEngine) PutIndex(ctx context.Context, index ispec.Index) error { if err := e.ensureTempDir(); err != nil { - return errors.Wrap(err, "ensure tempdir") + return fmt.Errorf("ensure tempdir: %w", err) } // Make sure the index has the mediatype field set. @@ -251,23 +255,23 @@ func (e *dirEngine) PutIndex(ctx context.Context, index ispec.Index) error { // operation. fh, err := ioutil.TempFile(e.temp, "index-") if err != nil { - return errors.Wrap(err, "create temporary index") + return fmt.Errorf("create temporary index: %w", err) } tempPath := fh.Name() defer fh.Close() // Encode the index. if err := json.NewEncoder(fh).Encode(index); err != nil { - return errors.Wrap(err, "write temporary index") + return fmt.Errorf("write temporary index: %w", err) } if err := fh.Close(); err != nil { - return errors.Wrap(err, "close temporary index") + return fmt.Errorf("close temporary index: %w", err) } // Move the blob to its correct path. path := filepath.Join(e.path, indexFile) if err := os.Rename(tempPath, path); err != nil { - return errors.Wrap(err, "rename temporary index") + return fmt.Errorf("rename temporary index: %w", err) } return nil } @@ -284,15 +288,15 @@ func (e *dirEngine) PutIndex(ctx context.Context, index ispec.Index) error { func (e *dirEngine) GetIndex(ctx context.Context) (ispec.Index, error) { content, err := ioutil.ReadFile(filepath.Join(e.path, indexFile)) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { err = cas.ErrInvalid } - return ispec.Index{}, errors.Wrap(err, "read index") + return ispec.Index{}, fmt.Errorf("read index: %w", err) } var index ispec.Index if err := json.Unmarshal(content, &index); err != nil { - return ispec.Index{}, errors.Wrap(err, "parse index") + return ispec.Index{}, fmt.Errorf("parse index: %w", err) } return index, nil @@ -304,12 +308,12 @@ func (e *dirEngine) GetIndex(ctx context.Context) (ispec.Index, error) { func (e *dirEngine) DeleteBlob(ctx context.Context, digest digest.Digest) error { path, err := blobPath(digest) if err != nil { - return errors.Wrap(err, "compute blob path") + return fmt.Errorf("compute blob path: %w", err) } err = os.Remove(filepath.Join(e.path, path)) - if err != nil && !os.IsNotExist(err) { - return errors.Wrap(err, "remove blob") + if err != nil && !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("remove blob: %w", err) } return nil } @@ -324,13 +328,12 @@ func (e *dirEngine) ListBlobs(ctx context.Context) ([]digest.Digest, error) { if path == blobDir { return nil } - // XXX: Do we need to handle multiple-directory-deep cases? digest := digest.NewDigestFromHex(cas.BlobAlgorithm.String(), filepath.Base(path)) digests = append(digests, digest) return nil }); err != nil { - return nil, errors.Wrap(err, "walk blobdir") + return nil, fmt.Errorf("walk blobdir: %w", err) } return digests, nil @@ -343,7 +346,7 @@ func (e *dirEngine) Clean(ctx context.Context) error { // Remove every .umoci directory that isn't flocked. matches, err := filepath.Glob(filepath.Join(e.path, ".umoci-*")) if err != nil { - return errors.Wrap(err, "glob .umoci-*") + return fmt.Errorf("glob .umoci-*: %w", err) } for _, path := range matches { err = e.cleanPath(ctx, path) @@ -357,8 +360,8 @@ func (e *dirEngine) Clean(ctx context.Context) error { func (e *dirEngine) cleanPath(ctx context.Context, path string) error { cfh, err := os.Open(path) - if err != nil && !os.IsNotExist(err) { - return errors.Wrap(err, "open for locking") + if err != nil && !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("open for locking: %w", err) } defer cfh.Close() @@ -369,7 +372,7 @@ func (e *dirEngine) cleanPath(ctx context.Context, path string) error { } defer unix.Flock(int(cfh.Fd()), unix.LOCK_UN) - if err := os.RemoveAll(path); os.IsNotExist(err) { + if err := os.RemoveAll(path); errors.Is(err, os.ErrNotExist) { return nil // somebody else beat us to it } else if err != nil { log.Warnf("failed to remove %s: %v", path, err) @@ -385,13 +388,13 @@ func (e *dirEngine) cleanPath(ctx context.Context, path string) error { func (e *dirEngine) Close() error { if e.temp != "" { if err := unix.Flock(int(e.tempFile.Fd()), unix.LOCK_UN); err != nil { - return errors.Wrap(err, "unlock tempdir") + return fmt.Errorf("unlock tempdir: %w", err) } if err := e.tempFile.Close(); err != nil { - return errors.Wrap(err, "close tempdir") + return fmt.Errorf("close tempdir: %w", err) } if err := os.RemoveAll(e.temp); err != nil { - return errors.Wrap(err, "remove tempdir") + return fmt.Errorf("remove tempdir: %w", err) } } return nil @@ -406,7 +409,7 @@ func Open(path string) (cas.Engine, error) { } if err := engine.validate(); err != nil { - return nil, errors.Wrap(err, "validate") + return nil, fmt.Errorf("validate: %w", err) } return engine, nil @@ -421,24 +424,24 @@ func Create(path string) error { dir := filepath.Dir(path) if dir != "." { if err := os.MkdirAll(dir, 0755); err != nil { - return errors.Wrap(err, "mkdir parent") + return fmt.Errorf("mkdir parent: %w", err) } } if err := os.Mkdir(path, 0755); err != nil { - return errors.Wrap(err, "mkdir") + return fmt.Errorf("mkdir: %w", err) } // Create the necessary directories and "oci-layout" file. if err := os.Mkdir(filepath.Join(path, blobDirectory), 0755); err != nil { - return errors.Wrap(err, "mkdir blobdir") + return fmt.Errorf("mkdir blobdir: %w", err) } if err := os.Mkdir(filepath.Join(path, blobDirectory, cas.BlobAlgorithm.String()), 0755); err != nil { - return errors.Wrap(err, "mkdir algorithm") + return fmt.Errorf("mkdir algorithm: %w", err) } indexFh, err := os.Create(filepath.Join(path, indexFile)) if err != nil { - return errors.Wrap(err, "create index.json") + return fmt.Errorf("create index.json: %w", err) } defer indexFh.Close() @@ -449,12 +452,12 @@ func Create(path string) error { MediaType: ispec.MediaTypeImageIndex, } if err := json.NewEncoder(indexFh).Encode(defaultIndex); err != nil { - return errors.Wrap(err, "encode index.json") + return fmt.Errorf("encode index.json: %w", err) } layoutFh, err := os.Create(filepath.Join(path, layoutFile)) if err != nil { - return errors.Wrap(err, "create oci-layout") + return fmt.Errorf("create oci-layout: %w", err) } defer layoutFh.Close() @@ -462,7 +465,7 @@ func Create(path string) error { Version: ImageLayoutVersion, } if err := json.NewEncoder(layoutFh).Encode(ociLayout); err != nil { - return errors.Wrap(err, "encode oci-layout") + return fmt.Errorf("encode oci-layout: %w", err) } // Everything is now set up. diff --git a/oci/cas/dir/dir_cas_test.go b/oci/cas/dir/dir_cas_test.go index 0e69f3f9b..e69c048bc 100644 --- a/oci/cas/dir/dir_cas_test.go +++ b/oci/cas/dir/dir_cas_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package dir import ( "bytes" "context" + "errors" "io" "io/ioutil" "os" @@ -28,7 +29,6 @@ import ( "github.com/opencontainers/umoci/oci/cas" "github.com/opencontainers/umoci/pkg/testutils" - "github.com/pkg/errors" ) // NOTE: These tests aren't really testing OCI-style manifests. It's all just @@ -135,7 +135,7 @@ func TestEngineBlob(t *testing.T) { t.Errorf("DeleteBlob: unexpected error: %+v", err) } - if br, err := engine.GetBlob(ctx, digest); !os.IsNotExist(errors.Cause(err)) { + if br, err := engine.GetBlob(ctx, digest); !errors.Is(err, os.ErrNotExist) { if err == nil { br.Close() t.Errorf("GetBlob: still got blob contents after DeleteBlob!") @@ -384,7 +384,7 @@ func TestEngineGCLocking(t *testing.T) { } { if _, err := os.Lstat(path); err == nil { t.Errorf("expected %s to not exist after GC", path) - } else if !os.IsNotExist(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrNotExist) { t.Errorf("expected IsNotExist for %s after GC: %+v", path, err) } } diff --git a/oci/casext/blob.go b/oci/casext/blob.go index 486fdeb9e..287374979 100644 --- a/oci/casext/blob.go +++ b/oci/casext/blob.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +19,14 @@ package casext import ( "context" + "errors" + "fmt" "io" "io/ioutil" ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/umoci/oci/casext/mediatype" "github.com/opencontainers/umoci/pkg/system" - "github.com/pkg/errors" ) // Blob represents a "parsed" blob in an OCI image's blob store. MediaType @@ -63,7 +64,7 @@ func (b *Blob) Close() error { func (e Engine) FromDescriptor(ctx context.Context, descriptor ispec.Descriptor) (_ *Blob, Err error) { reader, err := e.GetVerifiedBlob(ctx, descriptor) if err != nil { - return nil, errors.Wrap(err, "get blob") + return nil, fmt.Errorf("get blob: %w", err) } blob := Blob{ @@ -73,22 +74,22 @@ func (e Engine) FromDescriptor(ctx context.Context, descriptor ispec.Descriptor) if fn := mediatype.GetParser(descriptor.MediaType); fn != nil { defer func() { - if _, err := system.Copy(ioutil.Discard, reader); Err == nil { - Err = errors.Wrapf(err, "discard trailing %q blob", descriptor.MediaType) + if _, err := system.Copy(ioutil.Discard, reader); Err == nil && err != nil { + Err = fmt.Errorf("discard trailing %q blob: %w", descriptor.MediaType, err) } - if err := reader.Close(); Err == nil { - Err = errors.Wrapf(err, "close %q blob", descriptor.MediaType) + if err := reader.Close(); Err == nil && err != nil { + Err = fmt.Errorf("close %q blob: %w", descriptor.MediaType, err) } }() data, err := fn(reader) if err != nil { - return nil, errors.Wrapf(err, "parse %s", descriptor.MediaType) + return nil, fmt.Errorf("parse %s: %w", descriptor.MediaType, err) } blob.Data = data } if blob.Data == nil { - return nil, errors.Errorf("[internal error] b.Data was nil after parsing") + return nil, errors.New("[internal error] b.Data was nil after parsing") } return &blob, nil } diff --git a/oci/casext/gc.go b/oci/casext/gc.go index 3a9b14d28..80b04562e 100644 --- a/oci/casext/gc.go +++ b/oci/casext/gc.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,11 @@ package casext import ( "context" + "fmt" "github.com/apex/log" "github.com/opencontainers/go-digest" ispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" ) // GCPolicy is a policy function that returns 'true' if a blob can be GC'ed @@ -49,7 +49,7 @@ func (e Engine) GC(ctx context.Context, policies ...GCPolicy) error { index, err := e.GetIndex(ctx) if err != nil { - return errors.Wrap(err, "get top-level index") + return fmt.Errorf("get top-level index: %w", err) } for _, descriptor := range index.Manifests { @@ -68,7 +68,7 @@ func (e Engine) GC(ctx context.Context, policies ...GCPolicy) error { reachables, err := e.reachable(ctx, descriptor) if err != nil { - return errors.Wrapf(err, "getting reachables from root %d", idx) + return fmt.Errorf("getting reachables from root %d: %w", idx, err) } for _, reachable := range reachables { black[reachable] = struct{}{} @@ -78,7 +78,7 @@ func (e Engine) GC(ctx context.Context, policies ...GCPolicy) error { // Sweep all blobs in the white set. blobs, err := e.ListBlobs(ctx) if err != nil { - return errors.Wrap(err, "get blob list") + return fmt.Errorf("get blob list: %w", err) } n := 0 @@ -92,7 +92,7 @@ sweep: for i, policy := range policies { ok, err := policy(ctx, digest) if err != nil { - return errors.Wrapf(err, "invoking policy %d failed", i) + return fmt.Errorf("invoking policy %d failed: %w", i, err) } if !ok { @@ -104,14 +104,14 @@ sweep: log.Debugf("garbage collecting blob: %s", digest) if err := e.DeleteBlob(ctx, digest); err != nil { - return errors.Wrapf(err, "remove unmarked blob %s", digest) + return fmt.Errorf("remove unmarked blob %s: %w", digest, err) } n++ } // Finally, tell CAS to GC it. if err := e.Clean(ctx); err != nil { - return errors.Wrapf(err, "clean engine") + return fmt.Errorf("clean engine: %w", err) } log.Debugf("garbage collected %d blobs", n) diff --git a/oci/casext/gc_test.go b/oci/casext/gc_test.go index 42ebc96ad..6c27ad90c 100644 --- a/oci/casext/gc_test.go +++ b/oci/casext/gc_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "fmt" + "errors" "io/ioutil" "os" "path/filepath" @@ -210,7 +210,7 @@ func gcSkipFunc(t *testing.T, expectedDigest digest.Digest) GCPolicy { } func errFunc(ctx context.Context, digest digest.Digest) (bool, error) { - return false, fmt.Errorf("err policy") + return false, errors.New("err policy") } func TestGCWithPolicy(t *testing.T) { diff --git a/oci/casext/json.go b/oci/casext/json.go index e7f562f11..04f08538c 100644 --- a/oci/casext/json.go +++ b/oci/casext/json.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,9 @@ import ( "bytes" "context" "encoding/json" + "fmt" "github.com/opencontainers/go-digest" - "github.com/pkg/errors" ) // PutBlobJSON adds a new JSON blob to the image (marshalled from the given @@ -40,7 +40,7 @@ import ( func (e Engine) PutBlobJSON(ctx context.Context, data interface{}) (digest.Digest, int64, error) { var buffer bytes.Buffer if err := json.NewEncoder(&buffer).Encode(data); err != nil { - return "", -1, errors.Wrap(err, "encode JSON") + return "", -1, fmt.Errorf("encode JSON: %w", err) } return e.PutBlob(ctx, &buffer) } diff --git a/oci/casext/json_dir_test.go b/oci/casext/json_dir_test.go index 8b928c11c..f31c6ecac 100644 --- a/oci/casext/json_dir_test.go +++ b/oci/casext/json_dir_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package casext import ( "context" "encoding/json" + "errors" "io/ioutil" "os" "path/filepath" @@ -28,7 +29,6 @@ import ( "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/pkg/testutils" - "github.com/pkg/errors" ) func TestEngineBlobJSON(t *testing.T) { @@ -92,7 +92,7 @@ func TestEngineBlobJSON(t *testing.T) { t.Errorf("DeleteBlob: unexpected error: %+v", err) } - if br, err := engine.GetBlob(ctx, digest); !os.IsNotExist(errors.Cause(err)) { + if br, err := engine.GetBlob(ctx, digest); !errors.Is(err, os.ErrNotExist) { if err == nil { br.Close() t.Errorf("GetBlob: still got blob contents after DeleteBlob!") diff --git a/oci/casext/map.go b/oci/casext/map.go index d39dad25b..553a77710 100644 --- a/oci/casext/map.go +++ b/oci/casext/map.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,12 @@ package casext import ( + "fmt" "reflect" "github.com/apex/log" ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/umoci/oci/casext/mediatype" - "github.com/pkg/errors" ) // Used by walkState.mark() to determine which struct members are descriptors to @@ -60,7 +60,7 @@ func mapDescriptors(V reflect.Value, mapFunc DescriptorMapFunc) error { P := V if !P.CanSet() { // This is a programmer error. - return errors.Errorf("[internal error] cannot apply map function to %v: %v is not settable!", P, P.Type()) + return fmt.Errorf("[internal error] cannot apply map function to %v: %v is not settable", P, P.Type()) } P.Set(reflect.ValueOf(new)) } @@ -74,15 +74,17 @@ func mapDescriptors(V reflect.Value, mapFunc DescriptorMapFunc) error { if V.IsNil() { return nil } - err := mapDescriptors(V.Elem(), mapFunc) - return errors.Wrapf(err, "%v", V.Type()) + if err := mapDescriptors(V.Elem(), mapFunc); err != nil { + return fmt.Errorf("%v: %w", V.Type(), err) + } + return nil case reflect.Slice, reflect.Array: // Iterate over each element. for idx := 0; idx < V.Len(); idx++ { err := mapDescriptors(V.Index(idx), mapFunc) if err != nil { - return errors.Wrapf(err, "%v[%d]->%v", V.Type(), idx, V.Index(idx).Type()) + return fmt.Errorf("%v[%d]->%v: %w", V.Type(), idx, V.Index(idx).Type(), err) } } return nil @@ -101,7 +103,7 @@ func mapDescriptors(V reflect.Value, mapFunc DescriptorMapFunc) error { for idx := 0; idx < V.NumField(); idx++ { err := mapDescriptors(V.Field(idx), mapFunc) if err != nil { - return errors.Wrapf(err, "%v[%d=%s]->%v", V.Type(), idx, V.Type().Field(idx).Name, V.Field(idx).Type()) + return fmt.Errorf("%v[%d=%s]->%v: %w", V.Type(), idx, V.Type().Field(idx).Name, V.Field(idx).Type(), err) } } return nil diff --git a/oci/casext/mediatype/parse.go b/oci/casext/mediatype/parse.go index 9009d9d08..97b54f0b2 100644 --- a/oci/casext/mediatype/parse.go +++ b/oci/casext/mediatype/parse.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,13 @@ package mediatype import ( "encoding/json" + "errors" + "fmt" "io" "reflect" "sync" ispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" ) // ErrNilReader is returned by the parsers in this package when they are called @@ -170,7 +171,7 @@ func indexParser(rdr io.Reader) (interface{}, error) { return nil, err } if index.MediaType != "" && index.MediaType != ispec.MediaTypeImageIndex { - return nil, errors.Errorf("malicious image detected: index contained incorrect mediaType: %s", index.MediaType) + return nil, fmt.Errorf("malicious image detected: index contained incorrect mediaType: %s", index.MediaType) } if len(index.Config) != 0 { return nil, errors.New("malicious image detected: index contained forbidden 'config' field") @@ -196,7 +197,7 @@ func manifestParser(rdr io.Reader) (interface{}, error) { return nil, err } if manifest.MediaType != "" && manifest.MediaType != ispec.MediaTypeImageManifest { - return nil, errors.Errorf("malicious manifest detected: manifest contained incorrect mediaType: %s", manifest.MediaType) + return nil, fmt.Errorf("malicious manifest detected: manifest contained incorrect mediaType: %s", manifest.MediaType) } if len(manifest.Manifests) != 0 { return nil, errors.New("malicious manifest detected: manifest contained forbidden 'manifests' field") diff --git a/oci/casext/refname.go b/oci/casext/refname.go index 4d28d6799..21dc8bfc3 100644 --- a/oci/casext/refname.go +++ b/oci/casext/refname.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,12 @@ package casext import ( "context" + "fmt" "regexp" "github.com/apex/log" ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/umoci/oci/casext/mediatype" - "github.com/pkg/errors" ) // refnameRegex is a regex that only matches reference names that are valid @@ -61,12 +61,12 @@ func (e Engine) ResolveReference(ctx context.Context, refname string) ([]Descrip // dealing with an image that abuses the image specification in some // way. if !IsValidReferenceName(refname) { - return nil, errors.Errorf("refusing to resolve invalid reference %q", refname) + return nil, fmt.Errorf("refusing to resolve invalid reference %q", refname) } index, err := e.GetIndex(ctx) if err != nil { - return nil, errors.Wrap(err, "get top-level index") + return nil, fmt.Errorf("get top-level index: %w", err) } // Set of root links that match the given refname. @@ -90,7 +90,6 @@ func (e Engine) ResolveReference(ctx context.Context, refname string) ([]Descrip // descriptor. if err := e.Walk(ctx, root, func(descriptorPath DescriptorPath) error { descriptor := descriptorPath.Descriptor() - // If the media-type should be treated as a "target media-type" for // reference resolution, we stop resolution here and add it to the // set of resolved paths. @@ -100,7 +99,7 @@ func (e Engine) ResolveReference(ctx context.Context, refname string) ([]Descrip } return nil }); err != nil { - return nil, errors.Wrapf(err, "walk %s", root.Digest) + return nil, fmt.Errorf("walk %s: %w", root.Digest, err) } } @@ -123,13 +122,13 @@ func (e Engine) UpdateReference(ctx context.Context, refname string, descriptor // dealing with an image that abuses the image specification in some // way. if !IsValidReferenceName(refname) { - return errors.Errorf("refusing to update invalid reference %q", refname) + return fmt.Errorf("refusing to update invalid reference %q", refname) } // Get index to modify. index, err := e.GetIndex(ctx) if err != nil { - return errors.Wrap(err, "get top-level index") + return fmt.Errorf("get top-level index: %w", err) } // TODO: Handle refname = "". @@ -154,7 +153,7 @@ func (e Engine) UpdateReference(ctx context.Context, refname string, descriptor // Commit to image. index.Manifests = newIndex if err := e.PutIndex(ctx, index); err != nil { - return errors.Wrap(err, "replace index") + return fmt.Errorf("replace index: %w", err) } return nil } @@ -166,13 +165,13 @@ func (e Engine) DeleteReference(ctx context.Context, refname string) error { // dealing with an image that abuses the image specification in some // way. if !IsValidReferenceName(refname) { - return errors.Errorf("refusing to delete invalid reference %q", refname) + return fmt.Errorf("refusing to delete invalid reference %q", refname) } // Get index to modify. index, err := e.GetIndex(ctx) if err != nil { - return errors.Wrap(err, "get top-level index") + return fmt.Errorf("get top-level index: %w", err) } // TODO: Handle refname = "". @@ -190,7 +189,7 @@ func (e Engine) DeleteReference(ctx context.Context, refname string) error { // Commit to image. index.Manifests = newIndex if err := e.PutIndex(ctx, index); err != nil { - return errors.Wrap(err, "replace index") + return fmt.Errorf("replace index: %w", err) } return nil } @@ -202,7 +201,7 @@ func (e Engine) ListReferences(ctx context.Context) ([]string, error) { // Get index. index, err := e.GetIndex(ctx) if err != nil { - return nil, errors.Wrap(err, "get top-level index") + return nil, fmt.Errorf("get top-level index: %w", err) } var refs []string diff --git a/oci/casext/refname_dir_test.go b/oci/casext/refname_dir_test.go index 7c493570b..85d1c5a6d 100644 --- a/oci/casext/refname_dir_test.go +++ b/oci/casext/refname_dir_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/oci/casext/refname_test.go b/oci/casext/refname_test.go index 8561bb1a2..e467ce463 100644 --- a/oci/casext/refname_test.go +++ b/oci/casext/refname_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ func TestValidateRefname(t *testing.T) { } { valid := IsValidReferenceName(test.refname) if valid != test.valid { - t.Errorf("incorrectly determined validity of refname '%s': expected %v got %v", test.refname, test.valid, valid) + t.Errorf("incorrectly determined validity of refname %q: expected %v got %v", test.refname, test.valid, valid) } } } diff --git a/oci/config/convert/default.go b/oci/config/convert/default.go index 28c0e5bdf..d0847e47b 100644 --- a/oci/config/convert/default.go +++ b/oci/config/convert/default.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ package convert import ( + "fmt" "strings" "github.com/blang/semver/v4" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) // FIXME: We currently use an unreleased version of the runtime-spec and so we @@ -248,7 +248,7 @@ func ToRootless(spec *rspec.Spec) error { // thus cannot change any flags that are locked. unprivOpts, err := getUnprivilegedMountFlags(resolvConf) if err != nil { - return errors.Wrapf(err, "inspecting mount flags of %s", resolvConf) + return fmt.Errorf("inspecting mount flags of %s: %w", resolvConf, err) } mounts = append(mounts, rspec.Mount{ // NOTE: "type: bind" is silly here, see opencontainers/runc#2035. diff --git a/oci/config/convert/runtime.go b/oci/config/convert/runtime.go index b46b7c898..fe966809f 100644 --- a/oci/config/convert/runtime.go +++ b/oci/config/convert/runtime.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package convert import ( + "fmt" "path/filepath" "strings" @@ -27,7 +28,6 @@ import ( ispec "github.com/opencontainers/image-spec/specs-go/v1" rspec "github.com/opencontainers/runtime-spec/specs-go" igen "github.com/opencontainers/umoci/oci/config/generate" - "github.com/pkg/errors" ) // Annotations described by the OCI image-spec document (these represent fields @@ -60,12 +60,12 @@ func ToRuntimeSpec(rootfs string, image ispec.Image) (rspec.Spec, error) { func parseEnv(env string) (string, string, error) { parts := strings.SplitN(env, "=", 2) if len(parts) != 2 { - return "", "", errors.Errorf("environment variable must contain '=': %s", env) + return "", "", fmt.Errorf("environment variable must contain '=': %s", env) } name, value := parts[0], parts[1] if name == "" { - return "", "", errors.Errorf("environment variable must have non-empty name: %s", env) + return "", "", fmt.Errorf("environment variable must have non-empty name: %s", env) } return name, value, nil } @@ -108,11 +108,11 @@ func allocateNilStruct(spec *rspec.Spec) { func MutateRuntimeSpec(spec *rspec.Spec, rootfs string, image ispec.Image) error { ig, err := igen.NewFromImage(image) if err != nil { - return errors.Wrap(err, "creating image generator") + return fmt.Errorf("creating image generator: %w", err) } if ig.OS() != "linux" { - return errors.Errorf("unsupported OS: %s", image.OS) + return fmt.Errorf("unsupported OS: %s", image.OS) } allocateNilStruct(spec) @@ -127,13 +127,13 @@ func MutateRuntimeSpec(spec *rspec.Spec, rootfs string, image ispec.Image) error // might drop fields that the user finds important). oldVersion, err := semver.Parse(spec.Version) if err != nil { - return errors.Wrap(err, "parsing original runtime-spec config version") + return fmt.Errorf("parsing original runtime-spec config version: %w", err) } if oldVersion.GT(curSpecVersion) { - return errors.Errorf("original runtime-spec config version %s is unsupported: %s > %s", oldVersion, oldVersion, curSpecVersion) + return fmt.Errorf("original runtime-spec config version %s is unsupported: %s > %s", oldVersion, oldVersion, curSpecVersion) } if oldVersion.Major != curSpecVersion.Major { - return errors.Errorf("original runtime-spec config version %s is incompatible with version %s: mismatching major number", oldVersion, curSpecVersion) + return fmt.Errorf("original runtime-spec config version %s is incompatible with version %s: mismatching major number", oldVersion, curSpecVersion) } // Set verbatim fields @@ -149,7 +149,7 @@ func MutateRuntimeSpec(spec *rspec.Spec, rootfs string, image ispec.Image) error for _, env := range ig.ConfigEnv() { name, value, err := parseEnv(env) if err != nil { - return errors.Wrap(err, "parsing image.Config.Env") + return fmt.Errorf("parsing image.Config.Env: %w", err) } appendEnv(&spec.Process.Env, name, value) } @@ -185,9 +185,9 @@ func MutateRuntimeSpec(spec *rspec.Spec, rootfs string, image ispec.Image) error // We only log an error if were not given a rootfs, and we set execUser // to the "default" (root:root). if rootfs != "" { - return errors.Wrapf(err, "cannot parse user spec: '%s'", ig.ConfigUser()) + return fmt.Errorf("cannot parse user spec: %q: %w", ig.ConfigUser(), err) } - log.Warnf("could not parse user spec '%s' without a rootfs -- defaulting to root:root", ig.ConfigUser()) + log.Warnf("could not parse user spec %q without a rootfs -- defaulting to root:root", ig.ConfigUser()) execUser = new(user.ExecUser) } diff --git a/oci/config/generate/save.go b/oci/config/generate/save.go index 3bf31bc21..8c52a311b 100644 --- a/oci/config/generate/save.go +++ b/oci/config/generate/save.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +19,8 @@ package generate import ( "encoding/json" + "fmt" "io" - - "github.com/pkg/errors" ) // fakeBuffer implements the io.Writer interface but just counts the number of @@ -48,7 +47,7 @@ func (g *Generator) WriteTo(w io.Writer) (n int64, err error) { w = io.MultiWriter(w, &fb) if err := json.NewEncoder(w).Encode(g.image); err != nil { - return fb.n, errors.Wrap(err, "encode image") + return fb.n, fmt.Errorf("encode image: %w", err) } return fb.n, nil diff --git a/oci/layer/generate.go b/oci/layer/generate.go index a231d2ea4..3f189860d 100644 --- a/oci/layer/generate.go +++ b/oci/layer/generate.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package layer import ( + "fmt" "io" "os" "path" @@ -26,7 +27,6 @@ import ( "github.com/apex/log" "github.com/opencontainers/umoci/pkg/unpriv" - "github.com/pkg/errors" "github.com/vbatts/go-mtree" ) @@ -54,11 +54,13 @@ func GenerateLayer(path string, deltas []mtree.InodeDelta, opt *RepackOptions) ( go func() (Err error) { // Close with the returned error. defer func() { + var closeErr error if Err != nil { log.Warnf("could not generate layer: %v", Err) + closeErr = fmt.Errorf("generate layer: %w", Err) } // #nosec G104 - _ = writer.CloseWithError(errors.Wrap(Err, "generate layer")) + _ = writer.CloseWithError(closeErr) }() // We can't just dump all of the file contents into a tar file. We need @@ -85,7 +87,7 @@ func GenerateLayer(path string, deltas []mtree.InodeDelta, opt *RepackOptions) ( if packOptions.TranslateOverlayWhiteouts { fi, err := os.Stat(fullPath) if err != nil { - return errors.Wrapf(err, "couldn't determine overlay whiteout for %s", fullPath) + return fmt.Errorf("couldn't determine overlay whiteout for %s: %w", fullPath, err) } whiteout, err := isOverlayWhiteout(fi) @@ -94,26 +96,26 @@ func GenerateLayer(path string, deltas []mtree.InodeDelta, opt *RepackOptions) ( } if whiteout { if err := tg.AddWhiteout(fullPath); err != nil { - return errors.Wrap(err, "generate whiteout from overlayfs") + return fmt.Errorf("generate whiteout from overlayfs: %w", err) } } continue } if err := tg.AddFile(name, fullPath); err != nil { - log.Warnf("generate layer: could not add file '%s': %s", name, err) - return errors.Wrap(err, "generate layer file") + log.Warnf("generate layer: could not add file %q: %s", name, err) + return fmt.Errorf("generate layer file: %w", err) } case mtree.Missing: if err := tg.AddWhiteout(name); err != nil { - log.Warnf("generate layer: could not add whiteout '%s': %s", name, err) - return errors.Wrap(err, "generate whiteout layer file") + log.Warnf("generate layer: could not add whiteout %q: %s", name, err) + return fmt.Errorf("generate whiteout layer file: %w", err) } } } if err := tg.tw.Close(); err != nil { log.Warnf("generate layer: could not close tar.Writer: %s", err) - return errors.Wrap(err, "close tar writer") + return fmt.Errorf("close tar writer: %w", err) } return nil @@ -137,11 +139,13 @@ func GenerateInsertLayer(root string, target string, opaque bool, opt *RepackOpt go func() (Err error) { defer func() { + var closeErr error if Err != nil { log.Warnf("could not generate insert layer: %v", Err) + closeErr = fmt.Errorf("generate insert layer: %w", Err) } // #nosec G104 - _ = writer.CloseWithError(errors.Wrap(Err, "generate insert layer")) + _ = writer.CloseWithError(closeErr) }() tg := newTarGenerator(writer, packOptions.MapOptions) diff --git a/oci/layer/tar_extract.go b/oci/layer/tar_extract.go index c3cdde8ea..aa438ea81 100644 --- a/oci/layer/tar_extract.go +++ b/oci/layer/tar_extract.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package layer import ( "archive/tar" "bytes" + "errors" "fmt" "io" "os" @@ -32,7 +33,6 @@ import ( "github.com/opencontainers/umoci/pkg/fseval" "github.com/opencontainers/umoci/pkg/system" "github.com/opencontainers/umoci/third_party/shared" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -116,7 +116,7 @@ func (te *TarExtractor) restoreMetadata(path string, hdr *tar.Header) error { if !te.mapOptions.Rootless { // NOTE: This is not done through fsEval. if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil { - return errors.Wrapf(err, "restore chown metadata: %s", path) + return fmt.Errorf("restore chown metadata: %s: %w", path, err) } } @@ -126,7 +126,7 @@ func (te *TarExtractor) restoreMetadata(path string, hdr *tar.Header) error { // owner (in rootless we don't care because we're always the owner). if !isSymlink { if err := te.fsEval.Chmod(path, fi.Mode()); err != nil { - return errors.Wrapf(err, "restore chmod metadata: %s", path) + return fmt.Errorf("restore chmod metadata: %s: %w", path, err) } } @@ -149,8 +149,8 @@ func (te *TarExtractor) restoreMetadata(path string, hdr *tar.Header) error { // set in the tar.Header. err := te.fsEval.Lclearxattrs(path, ignoreXattrs) if err != nil { - if errors.Cause(err) != unix.ENOTSUP { - return errors.Wrapf(err, "clear xattr metadata: %s", path) + if !errors.Is(err, unix.ENOTSUP) { + return fmt.Errorf("clear xattr metadata: %s: %w", path, err) } if !te.enotsupWarned { log.Warnf("xattr{%s} ignoring ENOTSUP on clearxattrs", path) @@ -192,14 +192,14 @@ func (te *TarExtractor) restoreMetadata(path string, hdr *tar.Header) error { // into v3 capabilities, which allow us to write them as // unprivileged users (we also would need to translate them // back when creating archives). - if te.partialRootless && os.IsPermission(errors.Cause(err)) { + if te.partialRootless && errors.Is(err, os.ErrPermission) { log.Warnf("rootless{%s} ignoring (usually) harmless EPERM on setxattr %q", hdr.Name, name) continue } // We cannot do much if we get an ENOTSUP -- this usually means // that extended attributes are simply unsupported by the // underlying filesystem (such as AUFS or NFS). - if errors.Cause(err) == unix.ENOTSUP { + if errors.Is(err, unix.ENOTSUP) { if !te.enotsupWarned { log.Warnf("xattr{%s} ignoring ENOTSUP on setxattr %q", hdr.Name, name) log.Warnf("xattr{%s} destination filesystem does not support xattrs, further warnings will be suppressed", path) @@ -209,12 +209,12 @@ func (te *TarExtractor) restoreMetadata(path string, hdr *tar.Header) error { } continue } - return errors.Wrapf(err, "restore xattr metadata: %s", path) + return fmt.Errorf("restore xattr metadata: %s: %w", path, err) } } if err := te.fsEval.Lutimes(path, atime, mtime); err != nil { - return errors.Wrapf(err, "restore lutimes metadata: %s", path) + return fmt.Errorf("restore lutimes metadata: %s: %w", path, err) } return nil @@ -228,7 +228,7 @@ func (te *TarExtractor) restoreMetadata(path string, hdr *tar.Header) error { func (te *TarExtractor) applyMetadata(path string, hdr *tar.Header) error { // Modify the header. if err := unmapHeader(hdr, te.mapOptions); err != nil { - return errors.Wrap(err, "unmap header") + return fmt.Errorf("unmap header: %w", err) } // Restore it on the filesystme. @@ -243,13 +243,13 @@ func (te *TarExtractor) applyMetadata(path string, hdr *tar.Header) error { func (te *TarExtractor) isDirlink(root string, path string) (bool, error) { // Make sure it exists and is a symlink. if _, err := te.fsEval.Readlink(path); err != nil { - return false, errors.Wrap(err, "read dirlink") + return false, fmt.Errorf("read dirlink: %w", err) } // Technically a string.TrimPrefix would also work... unsafePath, err := filepath.Rel(root, path) if err != nil { - return false, errors.Wrap(err, "get relative-to-root path") + return false, fmt.Errorf("get relative-to-root path: %w", err) } // It should be noted that SecureJoin will evaluate all symlinks in the @@ -260,10 +260,10 @@ func (te *TarExtractor) isDirlink(root string, path string) (bool, error) { if err != nil { // We hit a symlink loop -- which is fine but that means that this // cannot be considered a dirlink. - if errno := InnerErrno(err); errno == unix.ELOOP { - err = nil + if errors.Is(err, unix.ELOOP) { + return false, nil } - return false, errors.Wrap(err, "sanitize old target") + return false, fmt.Errorf("sanitize old target: %w", err) } targetInfo, err := te.fsEval.Lstat(targetPath) @@ -309,9 +309,9 @@ func (te *TarExtractor) ociWhiteout(root string, dir string, file string) error if _, err := te.fsEval.Lstat(path); err != nil { // Need to use securejoin.IsNotExist to handle ENOTDIR. if securejoin.IsNotExist(err) { - err = nil + return nil } - return errors.Wrap(err, "check whiteout target") + return fmt.Errorf("check whiteout target: %w", err) } // Walk over the path to remove it. We remove a given path as soon as @@ -320,13 +320,13 @@ func (te *TarExtractor) ociWhiteout(root string, dir string, file string) error // we iterate over any children and try again. The only difference // between opaque whiteouts and regular whiteouts is that we don't // delete the directory itself with opaque whiteouts. - err := te.fsEval.Walk(path, func(subpath string, info os.FileInfo, err error) error { + if err := te.fsEval.Walk(path, func(subpath string, info os.FileInfo, err error) error { // If we are passed an error, bail unless it's ENOENT. if err != nil { // If something was deleted outside of our knowledge it's not // the end of the world. In principle this shouldn't happen // though, so we log it for posterity. - if os.IsNotExist(errors.Cause(err)) { + if errors.Is(err, os.ErrNotExist) { log.Debugf("whiteout removal hit already-deleted path: %s", subpath) err = filepath.SkipDir } @@ -337,7 +337,7 @@ func (te *TarExtractor) ociWhiteout(root string, dir string, file string) error // te.upperPaths. upperPath, err := filepath.Rel(root, subpath) if err != nil { - return errors.Wrap(err, "find relative-to-root [should never happen]") + return fmt.Errorf("find relative-to-root [should never happen]: %w", err) } // Remove the path only if it hasn't been touched. @@ -351,15 +351,18 @@ func (te *TarExtractor) ociWhiteout(root string, dir string, file string) error // Purge the path. We skip anything underneath (if it's a // directory) since we just purged it -- and we don't want to // hit ENOENT during iteration for no good reason. - err := errors.Wrap(te.fsEval.RemoveAll(subpath), "whiteout subpath") - if err == nil && info.IsDir() { - err = filepath.SkipDir + if err := te.fsEval.RemoveAll(subpath); err != nil { + return fmt.Errorf("whiteout subpath: %w", err) + } + if info.IsDir() { + return filepath.SkipDir } - return err } return nil - }) - return errors.Wrap(err, "whiteout remove") + }); err != nil { + return fmt.Errorf("whiteout remove: %w", err) + } + return nil } func (te *TarExtractor) overlayFSWhiteout(dir string, file string) error { @@ -367,18 +370,22 @@ func (te *TarExtractor) overlayFSWhiteout(dir string, file string) error { // if this is an opaque whiteout, whiteout the directory if isOpaque { - err := te.fsEval.Lsetxattr(dir, "trusted.overlay.opaque", []byte("y"), 0) - return errors.Wrapf(err, "couldn't set overlayfs whiteout attr for %s", dir) + if err := te.fsEval.Lsetxattr(dir, "trusted.overlay.opaque", []byte("y"), 0); err != nil { + return fmt.Errorf("couldn't set overlayfs whiteout attr for %s: %w", dir, err) + } + return nil } // otherwise, white out the file itself. p := filepath.Join(dir, strings.TrimPrefix(file, whPrefix)) - if err := os.RemoveAll(p); err != nil && !os.IsNotExist(err) { - return errors.Wrapf(err, "couldn't create overlayfs whiteout for %s", p) + if err := os.RemoveAll(p); err != nil && !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("couldn't create overlayfs whiteout for %s: %w", p, err) } - err := te.fsEval.Mknod(p, unix.S_IFCHR|0666, unix.Mkdev(0, 0)) - return errors.Wrapf(err, "couldn't create overlayfs whiteout for %s", p) + if err := te.fsEval.Mknod(p, unix.S_IFCHR|0666, unix.Mkdev(0, 0)); err != nil { + return fmt.Errorf("couldn't create overlayfs whiteout for %s: %w", p, err) + } + return nil } // UnpackEntry extracts the given tar.Header to the provided root, ensuring @@ -412,7 +419,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( } dir, err := securejoin.SecureJoinVFS(root, unsafeDir, te.fsEval) if err != nil { - return errors.Wrap(err, "sanitise symlinks in root") + return fmt.Errorf("sanitise symlinks in root: %w", err) } path := filepath.Join(dir, file) @@ -428,7 +435,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( link, _ := te.fsEval.Readlink(dir) dirHdr, err := tar.FileInfoHeader(dirFi, link) if err != nil { - return errors.Wrap(err, "convert dirFi to dirHdr") + return fmt.Errorf("convert dirFi to dirHdr: %w", err) } // More faking to trick restoreMetadata to actually restore the directory. @@ -443,8 +450,8 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( // tar_generate.go. xattrs, err := te.fsEval.Llistxattr(dir) if err != nil { - if errors.Cause(err) != unix.ENOTSUP { - return errors.Wrap(err, "get dirHdr.Xattrs") + if !errors.Is(err, unix.ENOTSUP) { + return fmt.Errorf("get dirHdr.Xattrs: %w", err) } if !te.enotsupWarned { log.Warnf("xattr{%s} ignoring ENOTSUP on llistxattr", dir) @@ -459,7 +466,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( for _, xattr := range xattrs { value, err := te.fsEval.Lgetxattr(dir, xattr) if err != nil { - return errors.Wrap(err, "get xattr") + return fmt.Errorf("get xattr: %w", err) } dirHdr.Xattrs[xattr] = string(value) } @@ -472,7 +479,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( // Only overwrite the error if there wasn't one already. if err := te.restoreMetadata(dir, dirHdr); err != nil { if Err == nil { - Err = errors.Wrap(err, "restore parent directory") + Err = fmt.Errorf("restore parent directory: %w", err) } } }() @@ -490,7 +497,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( case OverlayFSWhiteout: return te.overlayFSWhiteout(dir, file) default: - return errors.Errorf("unknown whiteout mode %d", te.whiteoutMode) + return fmt.Errorf("unknown whiteout mode %d", te.whiteoutMode) } } @@ -511,7 +518,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( // have entries for some of these components we won't be able to // verify that we have consistent results during unpacking. if err := te.fsEval.MkdirAll(dir, 0777); err != nil { - return errors.Wrap(err, "mkdir parent") + return fmt.Errorf("mkdir parent: %w", err) } isDirlink := false @@ -548,12 +555,12 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( fi.Mode()&os.ModeSymlink == os.ModeSymlink && hdr.Typeflag == tar.TypeDir { isDirlink, err = te.isDirlink(root, path) if err != nil { - return errors.Wrap(err, "check is dirlink") + return fmt.Errorf("check is dirlink: %w", err) } } if !(isDirlink && te.keepDirlinks) { if err := te.fsEval.RemoveAll(path); err != nil { - return errors.Wrap(err, "clobber old path") + return fmt.Errorf("clobber old path: %w", err) } } } @@ -568,7 +575,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( // Create a new file, then just copy the data. fh, err := te.fsEval.Create(path) if err != nil { - return errors.Wrap(err, "create regular") + return fmt.Errorf("create regular: %w", err) } defer fh.Close() @@ -576,18 +583,18 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( n, err := system.Copy(fh, r) if int64(n) != hdr.Size { if err != nil { - err = errors.Wrapf(err, "short write") + err = fmt.Errorf("short write: %w", err) } else { err = io.ErrShortWrite } } if err != nil { - return errors.Wrap(err, "unpack to regular file") + return fmt.Errorf("unpack to regular file: %w", err) } // Force close here so that we don't affect the metadata. if err := fh.Close(); err != nil { - return errors.Wrap(err, "close unpacked regular file") + return fmt.Errorf("close unpacked regular file: %w", err) } // directory @@ -600,7 +607,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( // though you need to have a tar entry for every component of a new // path, applyMetadata will correct any inconsistencies. if err := te.fsEval.MkdirAll(path, 0777); err != nil { - return errors.Wrap(err, "mkdirall") + return fmt.Errorf("mkdirall: %w", err) } // hard link, symbolic link @@ -621,7 +628,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( unsafeLinkDir, linkFile := filepath.Split(CleanPath(linkname)) linkDir, err := securejoin.SecureJoinVFS(root, unsafeLinkDir, te.fsEval) if err != nil { - return errors.Wrap(err, "sanitise hardlink target in root") + return fmt.Errorf("sanitise hardlink target in root: %w", err) } linkname = filepath.Join(linkDir, linkFile) case tar.TypeSymlink: @@ -636,7 +643,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( // way of handling this is to delay link creation until the // very end. Unfortunately this won't work with symlinks // (which can link to directories). - return errors.Wrap(err, "link") + return fmt.Errorf("link: %w", err) } // character device node, block device node @@ -653,11 +660,11 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( log.Warnf("rootless{%s} creating empty file in place of device %d:%d", hdr.Name, hdr.Devmajor, hdr.Devminor) fh, err := te.fsEval.Create(path) if err != nil { - return errors.Wrap(err, "create rootless block") + return fmt.Errorf("create rootless block: %w", err) } defer fh.Close() if err := fh.Chmod(0); err != nil { - return errors.Wrap(err, "chmod 0 rootless block") + return fmt.Errorf("chmod 0 rootless block: %w", err) } goto out } @@ -675,7 +682,7 @@ func (te *TarExtractor) UnpackEntry(root string, hdr *tar.Header, r io.Reader) ( // Create the node. if err := te.fsEval.Mknod(path, os.FileMode(int64(mode)|hdr.Mode), dev); err != nil { - return errors.Wrap(err, "mknod") + return fmt.Errorf("mknod: %w", err) } // We should never hit any other headers (Go abstracts them away from us), @@ -690,7 +697,7 @@ out: // metadata from their link (and the tar headers might not be filled). if hdr.Typeflag != tar.TypeLink { if err := te.applyMetadata(path, hdr); err != nil { - return errors.Wrap(err, "apply hdr metadata") + return fmt.Errorf("apply hdr metadata: %w", err) } } @@ -700,7 +707,7 @@ out: upperPath, err := filepath.Rel(root, path) if err != nil { // Really shouldn't happen because of the guarantees of SecureJoinVFS. - return errors.Wrap(err, "find relative-to-root [should never happen]") + return fmt.Errorf("find relative-to-root [should never happen]: %w", err) } for pth := upperPath; pth != filepath.Dir(pth); pth = filepath.Dir(pth) { te.upperPaths[pth] = struct{}{} diff --git a/oci/layer/tar_extract_linux_test.go b/oci/layer/tar_extract_linux_test.go index 7ab746624..421285464 100644 --- a/oci/layer/tar_extract_linux_test.go +++ b/oci/layer/tar_extract_linux_test.go @@ -22,6 +22,7 @@ package layer import ( "archive/tar" + "errors" "io/ioutil" "os" "path/filepath" @@ -35,7 +36,7 @@ func canMknod(dir string) (bool, error) { testNode := filepath.Join(dir, "test") err := system.Mknod(testNode, unix.S_IFCHR|0666, unix.Mkdev(0, 0)) if err != nil { - if os.IsPermission(err) { + if errors.Is(err, os.ErrPermission) { return false, nil } diff --git a/oci/layer/tar_extract_test.go b/oci/layer/tar_extract_test.go index 5f119c980..23447038c 100644 --- a/oci/layer/tar_extract_test.go +++ b/oci/layer/tar_extract_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import ( "archive/tar" "bytes" "crypto/rand" + "errors" "io" "io/ioutil" "os" @@ -32,7 +33,6 @@ import ( rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/umoci/pkg/testutils" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -84,10 +84,10 @@ func testUnpackEntrySanitiseHelper(t *testing.T, dir, file, prefix string) { } if !bytes.Equal(ctrValue, ctrValueGot) { - t.Errorf("ctr path was not updated: expected='%s' got='%s'", string(ctrValue), string(ctrValueGot)) + t.Errorf("ctr path was not updated: expected=%q got=%q", string(ctrValue), string(ctrValueGot)) } if !bytes.Equal(hostValue, hostValueGot) { - t.Errorf("HOST PATH WAS CHANGED! THIS IS A PATH ESCAPE! expected='%s' got='%s'", string(hostValue), string(hostValueGot)) + t.Errorf("HOST PATH WAS CHANGED! THIS IS A PATH ESCAPE! expected=%q got=%q", string(hostValue), string(hostValueGot)) } } @@ -197,7 +197,7 @@ func TestUnpackEntryParentDir(t *testing.T) { } if !bytes.Equal(ctrValue, ctrValueGot) { - t.Errorf("ctr path was not updated: expected='%s' got='%s'", string(ctrValue), string(ctrValueGot)) + t.Errorf("ctr path was not updated: expected=%q got=%q", string(ctrValue), string(ctrValueGot)) } } @@ -277,7 +277,7 @@ func TestUnpackEntryWhiteout(t *testing.T) { } // Make sure that the path is gone. - if _, err := os.Lstat(filepath.Join(dir, test.path)); !os.IsNotExist(err) { + if _, err := os.Lstat(filepath.Join(dir, test.path)); !errors.Is(err, os.ErrNotExist) { if err != nil { t.Fatalf("unexpected error checking whiteout out path: %s", err) } @@ -294,10 +294,10 @@ func TestUnpackEntryWhiteout(t *testing.T) { } if !hdr.ModTime.Equal(testMtime) { - t.Errorf("mtime of parent directory changed after whiteout: got='%s' expected='%s'", hdr.ModTime, testMtime) + t.Errorf("mtime of parent directory changed after whiteout: got=%q expected=%q", hdr.ModTime, testMtime) } if !hdr.AccessTime.Equal(testAtime) { - t.Errorf("atime of parent directory changed after whiteout: got='%s' expected='%s'", hdr.ModTime, testAtime) + t.Errorf("atime of parent directory changed after whiteout: got=%q expected=%q", hdr.ModTime, testAtime) } } }) @@ -533,7 +533,7 @@ func TestUnpackOpaqueWhiteout(t *testing.T) { fullPath := filepath.Join(whiteoutRoot, ph.path) _, err := te.fsEval.Lstat(fullPath) - if err != nil && !os.IsNotExist(errors.Cause(err)) { + if err != nil && !errors.Is(err, os.ErrNotExist) { t.Errorf("unexpected lstat error of %s: %v", ph.path, err) } else if ph.upper && err != nil { t.Errorf("expected upper %s to exist: got %v", ph.path, err) @@ -546,7 +546,7 @@ func TestUnpackOpaqueWhiteout(t *testing.T) { // Make sure the whiteoutRoot still exists. if fi, err := te.fsEval.Lstat(whiteoutRoot); err != nil { - if os.IsNotExist(errors.Cause(err)) { + if errors.Is(err, os.ErrNotExist) { t.Errorf("expected whiteout root to still exist: %v", err) } else { t.Errorf("unexpected error in lstat of whiteout root: %v", err) diff --git a/oci/layer/tar_generate.go b/oci/layer/tar_generate.go index 776203460..e90779330 100644 --- a/oci/layer/tar_generate.go +++ b/oci/layer/tar_generate.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package layer import ( "archive/tar" + "errors" + "fmt" "io" "os" "path/filepath" @@ -28,7 +30,6 @@ import ( "github.com/opencontainers/umoci/pkg/fseval" "github.com/opencontainers/umoci/pkg/system" "github.com/opencontainers/umoci/pkg/testutils" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -127,7 +128,7 @@ func normalise(rawPath string, isDir bool) (string, error) { // of the tar archive. While this might seem paranoid, it is a legitimate // concern. if "/"+path != filepath.Join("/", path) { - return "", errors.Errorf("escape warning: generated path is outside tar root: %s", rawPath) + return "", fmt.Errorf("escape warning: generated path is outside tar root: %s", rawPath) } // With some other tar formats, you needed to have a '/' at the end of a @@ -147,19 +148,19 @@ func normalise(rawPath string, isDir bool) (string, error) { func (tg *tarGenerator) AddFile(name, path string) error { fi, err := tg.fsEval.Lstat(path) if err != nil { - return errors.Wrap(err, "add file lstat") + return fmt.Errorf("add file lstat: %w", err) } linkname := "" if fi.Mode()&os.ModeSymlink == os.ModeSymlink { if linkname, err = tg.fsEval.Readlink(path); err != nil { - return errors.Wrap(err, "add file readlink") + return fmt.Errorf("add file readlink: %w", err) } } hdr, err := tar.FileInfoHeader(fi, linkname) if err != nil { - return errors.Wrap(err, "convert fi to hdr") + return fmt.Errorf("convert fi to hdr: %w", err) } hdr.Xattrs = map[string]string{} // Usually incorrect for containers and was added in Go 1.10 causing @@ -169,7 +170,7 @@ func (tg *tarGenerator) AddFile(name, path string) error { name, err = normalise(name, fi.IsDir()) if err != nil { - return errors.Wrap(err, "normalise path") + return fmt.Errorf("normalise path: %w", err) } hdr.Name = name @@ -177,7 +178,7 @@ func (tg *tarGenerator) AddFile(name, path string) error { // will almost certainly confuse some users (unfortunately) but there's // nothing we can do to store such files on-disk. if strings.HasPrefix(filepath.Base(name), whPrefix) { - return errors.Errorf("invalid path has whiteout prefix %q: %s", whPrefix, name) + return fmt.Errorf("invalid path has whiteout prefix %q: %s", whPrefix, name) } // FIXME: Do we need to ensure that the parent paths have all been added to @@ -193,7 +194,7 @@ func (tg *tarGenerator) AddFile(name, path string) error { // by us. statx, err := tg.fsEval.Lstatx(path) if err != nil { - return errors.Wrapf(err, "lstatx %q", path) + return fmt.Errorf("lstatx %q: %w", path, err) } updateHeader(hdr, statx) @@ -202,8 +203,8 @@ func (tg *tarGenerator) AddFile(name, path string) error { // XXX: This should probably be moved to a function in tar_unix.go. names, err := tg.fsEval.Llistxattr(path) if err != nil { - if errors.Cause(err) != unix.EOPNOTSUPP { - return errors.Wrap(err, "get xattr list") + if !errors.Is(err, unix.EOPNOTSUPP) { + return fmt.Errorf("get xattr list: %w", err) } names = []string{} } @@ -219,13 +220,13 @@ func (tg *tarGenerator) AddFile(name, path string) error { // (we'd need to use libcap to parse it). value, err := tg.fsEval.Lgetxattr(path, name) if err != nil { - v := errors.Cause(err) - log.Debugf("failure reading xattr from list on %q: %q", name, v) - if v != unix.EOPNOTSUPP && v != unix.ENODATA { + // TODO: Should we use errors.As? + log.Debugf("failure reading xattr from list on %q: %q", name, err) + if !errors.Is(err, unix.EOPNOTSUPP) && !errors.Is(err, unix.ENODATA) { // XXX: I'm not sure if we're unprivileged whether Lgetxattr can // fail with EPERM. If it can, we should ignore it (like when // we try to clear xattrs). - return errors.Wrapf(err, "get xattr: %s", name) + return fmt.Errorf("get xattr: %s: %w", name, err) } } // https://golang.org/issues/20698 -- We don't just error out here @@ -255,26 +256,26 @@ func (tg *tarGenerator) AddFile(name, path string) error { // Apply any header mappings. if err := mapHeader(hdr, tg.mapOptions); err != nil { - return errors.Wrap(err, "map header") + return fmt.Errorf("map header: %w", err) } if err := tg.tw.WriteHeader(hdr); err != nil { - return errors.Wrap(err, "write header") + return fmt.Errorf("write header: %w", err) } // Write the contents of regular files. if hdr.Typeflag == tar.TypeReg { fh, err := tg.fsEval.Open(path) if err != nil { - return errors.Wrap(err, "open file") + return fmt.Errorf("open file: %w", err) } defer fh.Close() n, err := system.Copy(tg.tw, fh) if err != nil { - return errors.Wrap(err, "copy to layer") + return fmt.Errorf("copy to layer: %w", err) } if n != hdr.Size { - return errors.Wrap(io.ErrShortWrite, "copy to layer") + return fmt.Errorf("copy to layer: %w", io.ErrShortWrite) } } @@ -297,13 +298,13 @@ const whOpaque = whPrefix + whPrefix + ".opq" func (tg *tarGenerator) addWhiteout(name string, opaque bool) error { name, err := normalise(name, false) if err != nil { - return errors.Wrap(err, "normalise path") + return fmt.Errorf("normalise path: %w", err) } // Disallow having a whiteout of a whiteout, purely for our own sanity. dir, file := filepath.Split(name) if strings.HasPrefix(file, whPrefix) { - return errors.Errorf("invalid path has whiteout prefix %q: %s", whPrefix, name) + return fmt.Errorf("invalid path has whiteout prefix %q: %s", whPrefix, name) } // Figure out the whiteout name. @@ -313,10 +314,10 @@ func (tg *tarGenerator) addWhiteout(name string, opaque bool) error { } // Add a dummy header for the whiteout file. - return errors.Wrap(tg.tw.WriteHeader(&tar.Header{ - Name: whiteout, - Size: 0, - }), "write whiteout header") + if err := tg.tw.WriteHeader(&tar.Header{Name: whiteout, Size: 0}); err != nil { + return fmt.Errorf("write whiteout header: %w", err) + } + return nil } // AddWhiteout creates a whiteout for the provided path. diff --git a/oci/layer/tar_generate_test.go b/oci/layer/tar_generate_test.go index 1ad44c4b6..0cd0b40c0 100644 --- a/oci/layer/tar_generate_test.go +++ b/oci/layer/tar_generate_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/oci/layer/unpack.go b/oci/layer/unpack.go index 323870371..3ebc4214c 100644 --- a/oci/layer/unpack.go +++ b/oci/layer/unpack.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import ( "archive/tar" "context" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -42,7 +43,6 @@ import ( "github.com/opencontainers/umoci/pkg/fseval" "github.com/opencontainers/umoci/pkg/idtools" "github.com/opencontainers/umoci/pkg/system" - "github.com/pkg/errors" ) // AfterLayerUnpackCallback is called after each layer is unpacked. @@ -65,10 +65,10 @@ func UnpackLayer(root string, layer io.Reader, opt *UnpackOptions) error { break } if err != nil { - return errors.Wrap(err, "read next entry") + return fmt.Errorf("read next entry: %w", err) } if err := te.UnpackEntry(root, hdr, tr); err != nil { - return errors.Wrapf(err, "unpack entry: %s", hdr.Name) + return fmt.Errorf("unpack entry: %s: %w", hdr.Name, err) } } return nil @@ -99,24 +99,24 @@ func UnpackManifest(ctx context.Context, engine cas.Engine, bundle string, manif // already exists, because we cannot be sure that the user intended us to // extract over an existing bundle. if err := os.MkdirAll(bundle, 0755); err != nil { - return errors.Wrap(err, "mkdir bundle") + return fmt.Errorf("mkdir bundle: %w", err) } // We change the mode of the bundle directory to 0700. A user can easily // change this after-the-fact, but we do this explicitly to avoid cases // where an unprivileged user could recurse into an otherwise unsafe image // (giving them potential root access through setuid binaries for example). if err := os.Chmod(bundle, 0700); err != nil { - return errors.Wrap(err, "chmod bundle 0700") + return fmt.Errorf("chmod bundle 0700: %w", err) } configPath := filepath.Join(bundle, "config.json") rootfsPath := filepath.Join(bundle, RootfsName) - if _, err := os.Lstat(configPath); !os.IsNotExist(err) { + if _, err := os.Lstat(configPath); !errors.Is(err, os.ErrNotExist) { if err == nil { - return errors.Errorf("config.json already exists in %s", bundle) + return fmt.Errorf("config.json already exists in %s", bundle) } - return errors.Wrap(err, "problem accessing bundle config") + return fmt.Errorf("problem accessing bundle config: %w", err) } defer func() { @@ -131,27 +131,27 @@ func UnpackManifest(ctx context.Context, engine cas.Engine, bundle string, manif } }() - if _, err := os.Lstat(rootfsPath); !os.IsNotExist(err) && opt.StartFrom.MediaType == "" { + if _, err := os.Lstat(rootfsPath); !errors.Is(err, os.ErrNotExist) && opt.StartFrom.MediaType == "" { if err == nil { err = fmt.Errorf("%s already exists", rootfsPath) } - return errors.Wrapf(err, "detecting rootfs") + return fmt.Errorf("detecting rootfs: %w", err) } log.Infof("unpack rootfs: %s", rootfsPath) if err := UnpackRootfs(ctx, engine, rootfsPath, manifest, opt); err != nil { - return errors.Wrap(err, "unpack rootfs") + return fmt.Errorf("unpack rootfs: %w", err) } // Generate a runtime configuration file from ispec.Image. configFile, err := os.Create(configPath) if err != nil { - return errors.Wrap(err, "open config.json") + return fmt.Errorf("open config.json: %w", err) } defer configFile.Close() if err := UnpackRuntimeJSON(ctx, engine, configFile, rootfsPath, manifest, &opt.MapOptions); err != nil { - return errors.Wrap(err, "unpack config.json") + return fmt.Errorf("unpack config.json: %w", err) } return nil } @@ -162,7 +162,7 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man engineExt := casext.NewEngine(engine) if err := os.Mkdir(rootfsPath, 0755); err != nil && !os.IsExist(err) { - return errors.Wrap(err, "mkdir rootfs") + return fmt.Errorf("mkdir rootfs: %w", err) } // In order to avoid having a broken rootfs in the case of an error, we @@ -183,14 +183,14 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man // Make sure that the owner is correct. rootUID, err := idtools.ToHost(0, opt.MapOptions.UIDMappings) if err != nil { - return errors.Wrap(err, "ensure rootuid has mapping") + return fmt.Errorf("ensure rootuid has mapping: %w", err) } rootGID, err := idtools.ToHost(0, opt.MapOptions.GIDMappings) if err != nil { - return errors.Wrap(err, "ensure rootgid has mapping") + return fmt.Errorf("ensure rootgid has mapping: %w", err) } if err := os.Lchown(rootfsPath, rootUID, rootGID); err != nil { - return errors.Wrap(err, "chown rootfs") + return fmt.Errorf("chown rootfs: %w", err) } // Currently, many different images in the wild don't specify what the @@ -200,7 +200,7 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man // (which is as good of an arbitrary choice as any). epoch := time.Unix(0, 0) if err := system.Lutimes(rootfsPath, epoch, epoch); err != nil { - return errors.Wrap(err, "set initial root time") + return fmt.Errorf("set initial root time: %w", err) } // In order to verify the DiffIDs as we extract layers, we have to get the @@ -208,21 +208,21 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man // config) until after we have the full rootfs generated. configBlob, err := engineExt.FromDescriptor(ctx, manifest.Config) if err != nil { - return errors.Wrap(err, "get config blob") + return fmt.Errorf("get config blob: %w", err) } defer configBlob.Close() if configBlob.Descriptor.MediaType != ispec.MediaTypeImageConfig { - return errors.Errorf("unpack rootfs: config blob is not correct mediatype %s: %s", ispec.MediaTypeImageConfig, configBlob.Descriptor.MediaType) + return fmt.Errorf("unpack rootfs: config blob is not correct mediatype %s: %s", ispec.MediaTypeImageConfig, configBlob.Descriptor.MediaType) } config, ok := configBlob.Data.(ispec.Image) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] unknown config blob type: %s", configBlob.Descriptor.MediaType) + return fmt.Errorf("[internal error] unknown config blob type: %s", configBlob.Descriptor.MediaType) } // We can't understand non-layer images. if config.RootFS.Type != "layers" { - return errors.Errorf("unpack rootfs: config: unsupported rootfs.type: %s", config.RootFS.Type) + return fmt.Errorf("unpack rootfs: config: unsupported rootfs.type: %s", config.RootFS.Type) } // Layer extraction. @@ -238,16 +238,16 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man layerBlob, err := engineExt.FromDescriptor(ctx, layerDescriptor) if err != nil { - return errors.Wrap(err, "get layer blob") + return fmt.Errorf("get layer blob: %w", err) } defer layerBlob.Close() if !isLayerType(layerBlob.Descriptor.MediaType) { - return errors.Errorf("unpack rootfs: layer %s: blob is not correct mediatype: %s", layerBlob.Descriptor.Digest, layerBlob.Descriptor.MediaType) + return fmt.Errorf("unpack rootfs: layer %s: blob is not correct mediatype: %s", layerBlob.Descriptor.Digest, layerBlob.Descriptor.MediaType) } layerData, ok := layerBlob.Data.(io.ReadCloser) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] layerBlob was not an io.ReadCloser") + return errors.New("[internal error] layerBlob was not an io.ReadCloser") } layerRaw := layerData @@ -257,7 +257,7 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man // sha256 sum of the *uncompressed* layer). layerRaw, err = gzip.NewReader(layerData) if err != nil { - return errors.Wrap(err, "create gzip reader") + return fmt.Errorf("create gzip reader: %w", err) } } @@ -265,7 +265,7 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man layer := io.TeeReader(layerRaw, layerDigester.Hash()) if err := UnpackLayer(rootfsPath, layer, opt); err != nil { - return errors.Wrap(err, "unpack layer") + return fmt.Errorf("unpack layer: %w", err) } // Different tar implementations can have different levels of redundant // padding and other similar weird behaviours. While on paper they are @@ -275,7 +275,7 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man // the whole uncompressed stream). Just blindly consume anything left // in the layer. if n, err := system.Copy(ioutil.Discard, layer); err != nil { - return errors.Wrap(err, "discard trailing archive bits") + return fmt.Errorf("discard trailing archive bits: %w", err) } else if n != 0 { log.Debugf("unpack manifest: layer %s: ignoring %d trailing 'junk' bytes in the tar stream -- probably from GNU tar", layerDescriptor.Digest, n) } @@ -287,17 +287,17 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man // WriteTo, which causes havoc with system.Copy. Ideally we would use // layerRaw. See . if n, err := system.Copy(ioutil.Discard, layerData); err != nil { - return errors.Wrap(err, "discard trailing raw bits") + return fmt.Errorf("discard trailing raw bits: %w", err) } else if n != 0 { log.Warnf("unpack manifest: layer %s: ignoring %d trailing 'junk' bytes in the blob stream -- this may indicate a bug in the tool which built this image", layerDescriptor.Digest, n) } if err := layerData.Close(); err != nil { - return errors.Wrap(err, "close layer data") + return fmt.Errorf("close layer data: %w", err) } layerDigest := layerDigester.Digest() if layerDigest != layerDiffID { - return errors.Errorf("unpack manifest: layer %s: diffid mismatch: got %s expected %s", layerDescriptor.Digest, layerDigest, layerDiffID) + return fmt.Errorf("unpack manifest: layer %s: diffid mismatch: got %s expected %s", layerDescriptor.Digest, layerDigest, layerDiffID) } if opt.AfterLayerUnpack != nil { @@ -332,21 +332,21 @@ func UnpackRuntimeJSON(ctx context.Context, engine cas.Engine, configFile io.Wri // config) until after we have the full rootfs generated. configBlob, err := engineExt.FromDescriptor(ctx, manifest.Config) if err != nil { - return errors.Wrap(err, "get config blob") + return fmt.Errorf("get config blob: %w", err) } defer configBlob.Close() if configBlob.Descriptor.MediaType != ispec.MediaTypeImageConfig { - return errors.Errorf("unpack manifest: config blob is not correct mediatype %s: %s", ispec.MediaTypeImageConfig, configBlob.Descriptor.MediaType) + return fmt.Errorf("unpack manifest: config blob is not correct mediatype %s: %s", ispec.MediaTypeImageConfig, configBlob.Descriptor.MediaType) } config, ok := configBlob.Data.(ispec.Image) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] unknown config blob type: %s", configBlob.Descriptor.MediaType) + return fmt.Errorf("[internal error] unknown config blob type: %s", configBlob.Descriptor.MediaType) } spec, err := iconv.ToRuntimeSpec(rootfs, config) if err != nil { - return errors.Wrap(err, "generate config.json") + return fmt.Errorf("generate config.json: %w", err) } // Add UIDMapping / GIDMapping options. @@ -366,12 +366,15 @@ func UnpackRuntimeJSON(ctx context.Context, engine cas.Engine, configFile io.Wri spec.Linux.GIDMappings = mapOptions.GIDMappings if mapOptions.Rootless { if err := iconv.ToRootless(&spec); err != nil { - return errors.Wrap(err, "convert spec to rootless") + return fmt.Errorf("convert spec to rootless: %w", err) } } // Save the config.json. enc := json.NewEncoder(configFile) enc.SetIndent("", "\t") - return errors.Wrap(enc.Encode(spec), "write config.json") + if err := enc.Encode(spec); err != nil { + return fmt.Errorf("write config.json: %w", err) + } + return nil } diff --git a/oci/layer/unpack_test.go b/oci/layer/unpack_test.go index 4f36094b8..8046892f3 100644 --- a/oci/layer/unpack_test.go +++ b/oci/layer/unpack_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import ( "bytes" "context" "encoding/base64" + "errors" "io" "io/ioutil" "os" @@ -216,7 +217,7 @@ func TestUnpackStartFromDescriptor(t *testing.T) { } _, err = os.Stat(filepath.Join(bundle, "rootfs/test_file")) - if err == nil || !os.IsNotExist(err) { + if err == nil || !errors.Is(err, os.ErrNotExist) { t.Errorf("test file present? %+v\n", err) } } diff --git a/oci/layer/utils.go b/oci/layer/utils.go index f22122f78..fae16ac99 100644 --- a/oci/layer/utils.go +++ b/oci/layer/utils.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package layer import ( "archive/tar" + "fmt" "os" "path/filepath" "syscall" @@ -26,7 +27,6 @@ import ( "github.com/apex/log" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/umoci/pkg/idtools" - "github.com/pkg/errors" rootlesscontainers "github.com/rootless-containers/proto/go-proto" "golang.org/x/sys/unix" "google.golang.org/protobuf/proto" @@ -58,11 +58,11 @@ func mapHeader(hdr *tar.Header, mapOptions MapOptions) error { var err error newUID, err = idtools.ToContainer(hdr.Uid, mapOptions.UIDMappings) if err != nil { - return errors.Wrap(err, "map uid to container") + return fmt.Errorf("map uid to container: %w", err) } newGID, err = idtools.ToContainer(hdr.Gid, mapOptions.GIDMappings) if err != nil { - return errors.Wrap(err, "map gid to container") + return fmt.Errorf("map gid to container: %w", err) } } @@ -81,7 +81,7 @@ func mapHeader(hdr *tar.Header, mapOptions MapOptions) error { } else { var payload rootlesscontainers.Resource if err := proto.Unmarshal([]byte(value), &payload); err != nil { - return errors.Wrap(err, "unmarshal rootlesscontainers payload") + return fmt.Errorf("unmarshal rootlesscontainers payload: %w", err) } // If the payload isn't uint32(-1) we apply it. The xattr includes the @@ -152,7 +152,7 @@ func unmapHeader(hdr *tar.Header, mapOptions MapOptions) error { if !rootlesscontainers.IsDefault(payload) { valueBytes, err := proto.Marshal(payload) if err != nil { - return errors.Wrap(err, "marshal rootlesscontainers payload") + return fmt.Errorf("marshal rootlesscontainers payload: %w", err) } // While the payload is almost certainly not UTF-8, Go strings can // actually be arbitrary bytes (in case you didn't know this and @@ -167,11 +167,11 @@ func unmapHeader(hdr *tar.Header, mapOptions MapOptions) error { newUID, err := idtools.ToHost(hdr.Uid, mapOptions.UIDMappings) if err != nil { - return errors.Wrap(err, "map uid to host") + return fmt.Errorf("map uid to host: %w", err) } newGID, err := idtools.ToHost(hdr.Gid, mapOptions.GIDMappings) if err != nil { - return errors.Wrap(err, "map gid to host") + return fmt.Errorf("map gid to host: %w", err) } hdr.Uid = newUID @@ -211,23 +211,6 @@ func CleanPath(path string) string { return filepath.Clean(path) } -// InnerErrno returns the "real" system error from an error that originally -// came from the "os" package. The returned error can be compared directly with -// unix.* (or syscall.*) errno values. If the type could not be detected we just return -func InnerErrno(err error) error { - // All of the os.* cases as well as an explicit - errno := errors.Cause(err) - switch err := errno.(type) { - case *os.PathError: - errno = err.Err - case *os.LinkError: - errno = err.Err - case *os.SyscallError: - errno = err.Err - } - return errno -} - // isOverlayWhiteout returns true if the FileInfo represents an overlayfs style // whiteout (i.e. mknod c 0 0) and false otherwise. func isOverlayWhiteout(info os.FileInfo) (bool, error) { @@ -240,7 +223,7 @@ func isOverlayWhiteout(info os.FileInfo) (bool, error) { major = unix.Major(uint64(stat.Rdev)) minor = unix.Minor(uint64(stat.Rdev)) default: - return false, errors.Errorf("[internal error] unknown stat info type %T", info.Sys()) + return false, fmt.Errorf("[internal error] unknown stat info type %T", info.Sys()) } return major == 0 && minor == 0 && diff --git a/pkg/hardening/verified_reader.go b/pkg/hardening/verified_reader.go index feb531def..4cbf546b4 100644 --- a/pkg/hardening/verified_reader.go +++ b/pkg/hardening/verified_reader.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package hardening import ( + "errors" "fmt" "io" "io/ioutil" @@ -26,15 +27,13 @@ import ( "github.com/apex/log" "github.com/opencontainers/go-digest" "github.com/opencontainers/umoci/pkg/system" - "github.com/pkg/errors" ) // Exported errors for verification issues that occur during processing within -// VerifiedReadCloser. Note that you will need to use -// "github.com/pkg/errors".Cause to get these exported errors in most cases. +// VerifiedReadCloser. var ( - ErrDigestMismatch = errors.Errorf("verified reader digest mismatch") - ErrSizeMismatch = errors.Errorf("verified reader size mismatch") + ErrDigestMismatch = errors.New("verified reader digest mismatch") + ErrSizeMismatch = errors.New("verified reader size mismatch") ) // VerifiedReadCloser is a basic io.ReadCloser which allows for simple @@ -90,17 +89,17 @@ func (v *VerifiedReadCloser) isNoop() bool { func (v *VerifiedReadCloser) verify(nilErr error) error { // Digest mismatch (always takes precedence)? if actualDigest := v.digester.Digest(); actualDigest != v.ExpectedDigest { - return errors.Wrapf(ErrDigestMismatch, "expected %s not %s", v.ExpectedDigest, actualDigest) + return fmt.Errorf("expected %s not %s: %w", v.ExpectedDigest, actualDigest, ErrDigestMismatch) } // Do we need to check the size for mismatches? if v.ExpectedSize >= 0 { switch { // Not enough bytes in the stream. case v.currentSize < v.ExpectedSize: - return errors.Wrapf(ErrSizeMismatch, "expected %d bytes (only %d bytes in stream)", v.ExpectedSize, v.currentSize) + return fmt.Errorf("expected %d bytes (only %d bytes in stream): %w", v.ExpectedSize, v.currentSize, ErrSizeMismatch) // We don't read the entire blob, so the message needs to be slightly adjusted. case v.currentSize > v.ExpectedSize: - return errors.Wrapf(ErrSizeMismatch, "expected %d bytes (extra bytes in stream)", v.ExpectedSize) + return fmt.Errorf("expected %d bytes (extra bytes in stream): %w", v.ExpectedSize, ErrSizeMismatch) } } // Forward the provided error. @@ -155,7 +154,7 @@ func (v *VerifiedReadCloser) Read(p []byte) (n int, err error) { } } // We have finished reading -- let's verify the state! - if errors.Cause(err) == io.EOF { + if errors.Is(err, io.EOF) { err = v.verify(err) } return n, err @@ -184,7 +183,7 @@ func (v *VerifiedReadCloser) Close() error { // end of the stream. VerifiedReadCloser.Read will not read past // ExpectedSize+1, so we don't need to add a limit here. if n, err := system.Copy(ioutil.Discard, v); err != nil { - return errors.Wrap(err, "consume remaining unverified stream") + return fmt.Errorf("consume remaining unverified stream: %w", err) } else if n != 0 { // If there's trailing bytes being discarded at this point, that // indicates whatever you used to generate this blob is adding trailing diff --git a/pkg/hardening/verified_reader_test.go b/pkg/hardening/verified_reader_test.go index a95deb146..15a649bc4 100644 --- a/pkg/hardening/verified_reader_test.go +++ b/pkg/hardening/verified_reader_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package hardening import ( "bytes" "crypto/rand" + "errors" "fmt" "io" "io/ioutil" @@ -29,7 +30,6 @@ import ( _ "crypto/sha256" "github.com/opencontainers/go-digest" - "github.com/pkg/errors" ) func TestValid(t *testing.T) { @@ -142,12 +142,12 @@ func TestInvalidDigest(t *testing.T) { } // Make sure everything if we copy-to-EOF we get the right error. - if _, err := io.Copy(ioutil.Discard, verifiedReader); errors.Cause(err) != ErrDigestMismatch { + if _, err := io.Copy(ioutil.Discard, verifiedReader); !errors.Is(err, ErrDigestMismatch) { t.Errorf("expected digest to be invalid on EOF: got wrong error: %v", err) } // And on close we should get the error. - if err := verifiedReader.Close(); errors.Cause(err) != ErrDigestMismatch { + if err := verifiedReader.Close(); !errors.Is(err, ErrDigestMismatch) { t.Errorf("expected digest to be invalid on Close: got wrong error: %v", err) } }) @@ -187,11 +187,10 @@ func TestInvalidDigest_Trailing(t *testing.T) { } // And on close we should get the error. - if err := verifiedReader.Close(); errors.Cause(err) != ErrDigestMismatch { + if err := verifiedReader.Close(); !errors.Is(err, ErrDigestMismatch) { t.Errorf("expected digest to be invalid on Close: got wrong error: %v", err) } }) - } } } @@ -217,16 +216,15 @@ func TestInvalidSize_Short(t *testing.T) { } // Make sure everything if we copy-to-EOF we get the right error. - if _, err := io.Copy(ioutil.Discard, verifiedReader); errors.Cause(err) != ErrSizeMismatch { + if _, err := io.Copy(ioutil.Discard, verifiedReader); !errors.Is(err, ErrSizeMismatch) { t.Errorf("expected size to be invalid on EOF: got wrong error: %v", err) } // And on close we should get the error. - if err := verifiedReader.Close(); errors.Cause(err) != ErrSizeMismatch { + if err := verifiedReader.Close(); !errors.Is(err, ErrSizeMismatch) { t.Errorf("expected size to be invalid on Close: got wrong error: %v", err) } }) - } } } @@ -253,12 +251,12 @@ func TestInvalidSize_LongBuffer(t *testing.T) { } // Make sure everything if we copy-to-EOF we get the right error. - if _, err := io.Copy(ioutil.Discard, verifiedReader); errors.Cause(err) != ErrSizeMismatch { + if _, err := io.Copy(ioutil.Discard, verifiedReader); !errors.Is(err, ErrSizeMismatch) { t.Errorf("expected size to be invalid on EOF: got wrong error: %v", err) } // And on close we should get the error. - if err := verifiedReader.Close(); errors.Cause(err) != ErrSizeMismatch { + if err := verifiedReader.Close(); !errors.Is(err, ErrSizeMismatch) { t.Errorf("expected size to be invalid on Close: got wrong error: %v", err) } }) @@ -285,12 +283,12 @@ func TestInvalidSize_Long(t *testing.T) { } // Make sure everything if we copy-to-EOF we get the right error. - if _, err := io.Copy(ioutil.Discard, verifiedReader); errors.Cause(err) != ErrSizeMismatch { + if _, err := io.Copy(ioutil.Discard, verifiedReader); !errors.Is(err, ErrSizeMismatch) { t.Errorf("expected size to be invalid on EOF: got wrong error: %v", err) } // And on close we should get the error. - if err := verifiedReader.Close(); errors.Cause(err) != ErrSizeMismatch { + if err := verifiedReader.Close(); !errors.Is(err, ErrSizeMismatch) { t.Errorf("expected size to be invalid on Close: got wrong error: %v", err) } }) diff --git a/pkg/idtools/idtools.go b/pkg/idtools/idtools.go index 54ece8dc3..cbe9a015c 100644 --- a/pkg/idtools/idtools.go +++ b/pkg/idtools/idtools.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ package idtools import ( + "fmt" "strconv" "strings" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) // ToHost translates a remapped container ID to an unmapped host ID using the @@ -39,7 +39,7 @@ func ToHost(contID int, idMap []rspec.LinuxIDMapping) (int, error) { } } - return -1, errors.Errorf("container id %d cannot be mapped to a host id", contID) + return -1, fmt.Errorf("container id %d cannot be mapped to a host id", contID) } // ToContainer takes an unmapped host ID and translates it to a remapped @@ -57,7 +57,7 @@ func ToContainer(hostID int, idMap []rspec.LinuxIDMapping) (int, error) { } } - return -1, errors.Errorf("host id %d cannot be mapped to a container id", hostID) + return -1, fmt.Errorf("host id %d cannot be mapped to a container id", hostID) } // Helper to return a uint32 from strconv.ParseUint type-safely. @@ -78,22 +78,22 @@ func ParseMapping(spec string) (rspec.LinuxIDMapping, error) { case 3: size, err = parseUint32(parts[2]) if err != nil { - return rspec.LinuxIDMapping{}, errors.Wrap(err, "invalid size in mapping") + return rspec.LinuxIDMapping{}, fmt.Errorf("invalid size in mapping: %w", err) } case 2: size = 1 default: - return rspec.LinuxIDMapping{}, errors.Errorf("invalid number of fields in mapping '%s': %d", spec, len(parts)) + return rspec.LinuxIDMapping{}, fmt.Errorf("invalid number of fields in mapping %q: %d", spec, len(parts)) } contID, err = parseUint32(parts[0]) if err != nil { - return rspec.LinuxIDMapping{}, errors.Wrap(err, "invalid containerID in mapping") + return rspec.LinuxIDMapping{}, fmt.Errorf("invalid containerID in mapping: %w", err) } hostID, err = parseUint32(parts[1]) if err != nil { - return rspec.LinuxIDMapping{}, errors.Wrap(err, "invalid hostID in mapping") + return rspec.LinuxIDMapping{}, fmt.Errorf("invalid hostID in mapping: %w", err) } return rspec.LinuxIDMapping{ diff --git a/pkg/idtools/idtools_test.go b/pkg/idtools/idtools_test.go index 0d82cc318..7cdb54bda 100644 --- a/pkg/idtools/idtools_test.go +++ b/pkg/idtools/idtools_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -329,5 +329,4 @@ func TestParseIDMapping(t *testing.T) { } } } - } diff --git a/pkg/system/utime_unix_test.go b/pkg/system/utime_unix_test.go index 757a6a962..937941295 100644 --- a/pkg/system/utime_unix_test.go +++ b/pkg/system/utime_unix_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,13 +67,13 @@ func TestLutimesFile(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } } @@ -116,13 +116,13 @@ func TestLutimesDirectory(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } } @@ -171,13 +171,13 @@ func TestLutimesSymlink(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } // Make sure that the parent directory was unchanged. @@ -187,10 +187,10 @@ func TestLutimesSymlink(t *testing.T) { mtimeParentNew := time.Unix(fiParentNew.Mtim.Unix()) if !atimeParentOld.Equal(atimeParentNew) { - t.Errorf("parent directory atime was changed! old='%s' new='%s'", atimeParentOld, atimeParentNew) + t.Errorf("parent directory atime was changed! old=%q new=%q", atimeParentOld, atimeParentNew) } if !mtimeParentOld.Equal(mtimeParentNew) { - t.Errorf("parent directory mtime was changed! old='%s' new='%s'", mtimeParentOld, mtimeParentNew) + t.Errorf("parent directory mtime was changed! old=%q new=%q", mtimeParentOld, mtimeParentNew) } } @@ -249,13 +249,13 @@ func TestLutimesRelative(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } // Make sure that the parent directory was unchanged. @@ -265,9 +265,9 @@ func TestLutimesRelative(t *testing.T) { mtimeParentNew := time.Unix(fiParentNew.Mtim.Unix()) if !atimeParentOld.Equal(atimeParentNew) { - t.Errorf("parent directory atime was changed! old='%s' new='%s'", atimeParentOld, atimeParentNew) + t.Errorf("parent directory atime was changed! old=%q new=%q", atimeParentOld, atimeParentNew) } if !mtimeParentOld.Equal(mtimeParentNew) { - t.Errorf("parent directory mtime was changed! old='%s' new='%s'", mtimeParentOld, mtimeParentNew) + t.Errorf("parent directory mtime was changed! old=%q new=%q", mtimeParentOld, mtimeParentNew) } } diff --git a/pkg/system/xattr_unix.go b/pkg/system/xattr_unix.go index fe223bc35..caa491aaa 100644 --- a/pkg/system/xattr_unix.go +++ b/pkg/system/xattr_unix.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +19,10 @@ package system import ( "bytes" + "errors" + "fmt" "os" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -101,7 +102,7 @@ func Lgetxattr(path string, name string) ([]byte, error) { func Lclearxattrs(path string, except map[string]struct{}) error { names, err := Llistxattr(path) if err != nil { - return errors.Wrap(err, "lclearxattrs: get list") + return fmt.Errorf("lclearxattrs: get list: %w", err) } for _, name := range names { if _, skip := except[name]; skip { @@ -110,10 +111,10 @@ func Lclearxattrs(path string, except map[string]struct{}) error { if err := unix.Lremovexattr(path, name); err != nil { // Ignore permission errors, because hitting a permission error // means that it's a security.* xattr label or something similar. - if os.IsPermission(errors.Cause(err)) { + if errors.Is(err, os.ErrPermission) { continue } - return errors.Wrap(err, "lclearxattrs: remove xattr") + return fmt.Errorf("lclearxattrs: remove xattr: %w", err) } } return nil diff --git a/pkg/system/xattr_unix_test.go b/pkg/system/xattr_unix_test.go index a317b7173..386a3781e 100644 --- a/pkg/system/xattr_unix_test.go +++ b/pkg/system/xattr_unix_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ package system import ( + "errors" "io/ioutil" "os" "testing" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -58,7 +58,7 @@ func TestClearxattrFilter(t *testing.T) { } if err := unix.Lsetxattr(path, xattr.name, []byte(xattr.value), 0); err != nil { - if errors.Cause(err) == unix.ENOTSUP { + if errors.Is(err, unix.ENOTSUP) { t.Skip("xattrs unsupported on backing filesystem") } t.Fatalf("unexpected error setting %v=%v on %v: %v", xattr.name, xattr.value, path, err) diff --git a/pkg/unpriv/unpriv.go b/pkg/unpriv/unpriv.go index 93fcdc56b..c7f6ac813 100644 --- a/pkg/unpriv/unpriv.go +++ b/pkg/unpriv/unpriv.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package unpriv import ( "archive/tar" + "errors" + "fmt" "os" "path/filepath" "sort" @@ -27,7 +29,6 @@ import ( securejoin "github.com/cyphar/filepath-securejoin" "github.com/opencontainers/umoci/pkg/system" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -72,7 +73,7 @@ type WrapFunc func(path string) error // trickery is reverted when this function returns (which is when fn returns). func Wrap(path string, fn WrapFunc) error { // FIXME: Should we be calling fn() here first? - if err := fn(path); err == nil || !os.IsPermission(errors.Cause(err)) { + if err := fn(path); err == nil || !errors.Is(err, os.ErrPermission) { return err } @@ -89,9 +90,9 @@ func Wrap(path string, fn WrapFunc) error { // We've hit the first element we can chown. break } - if !os.IsPermission(err) { + if !errors.Is(err, os.ErrPermission) { // This is a legitimate error. - return errors.Wrapf(err, "unpriv.wrap: lstat parent: %s", current) + return fmt.Errorf("unpriv.wrap: lstat parent: %s: %w", current, err) } start-- } @@ -100,12 +101,12 @@ func Wrap(path string, fn WrapFunc) error { current := filepath.Join(parts[:i]...) fi, err := os.Lstat(current) if err != nil { - return errors.Wrapf(err, "unpriv.wrap: lstat parent: %s", current) + return fmt.Errorf("unpriv.wrap: lstat parent: %s: %w", current, err) } // Add +rwx permissions to directories. If we have the access to change // the mode at all then we are the user owner (not just a group owner). if err := os.Chmod(current, fi.Mode()|0700); err != nil { - return errors.Wrapf(err, "unpriv.wrap: chmod parent: %s", current) + return fmt.Errorf("unpriv.wrap: chmod parent: %s: %w", current, err) } defer fiRestore(current, fi) } @@ -126,13 +127,13 @@ func Open(path string) (*os.File, error) { // Get information so we can revert it. fi, err := os.Lstat(path) if err != nil { - return errors.Wrap(err, "lstat file") + return fmt.Errorf("lstat file: %w", err) } if fi.Mode()&0400 != 0400 { // Add +r permissions to the file. if err := os.Chmod(path, fi.Mode()|0400); err != nil { - return errors.Wrap(err, "chmod +r") + return fmt.Errorf("chmod +r: %w", err) } defer fiRestore(path, fi) } @@ -141,7 +142,10 @@ func Open(path string) (*os.File, error) { fh, err = os.Open(path) return err }) - return fh, errors.Wrap(err, "unpriv.open") + if err != nil { + return nil, fmt.Errorf("unpriv.open: %w", err) + } + return fh, nil } // Create is a wrapper around os.Create which has been wrapped with unpriv.Wrap @@ -156,7 +160,10 @@ func Create(path string) (*os.File, error) { fh, err = os.Create(path) return err }) - return fh, errors.Wrap(err, "unpriv.create") + if err != nil { + return nil, fmt.Errorf("unpriv.create: %w", err) + } + return fh, nil } // Readdir is a wrapper around (*os.File).Readdir which has been wrapper with @@ -171,19 +178,19 @@ func Readdir(path string) ([]os.FileInfo, error) { // Get information so we can revert it. fi, err := os.Lstat(path) if err != nil { - return errors.Wrap(err, "lstat dir") + return fmt.Errorf("lstat dir: %w", err) } // Add +rx permissions to the file. if err := os.Chmod(path, fi.Mode()|0500); err != nil { - return errors.Wrap(err, "chmod +rx") + return fmt.Errorf("chmod +rx: %w", err) } defer fiRestore(path, fi) // Open the damn thing. fh, err := os.Open(path) if err != nil { - return errors.Wrap(err, "opendir") + return fmt.Errorf("opendir: %w", err) } defer fh.Close() @@ -191,7 +198,10 @@ func Readdir(path string) ([]os.FileInfo, error) { infos, err = fh.Readdir(-1) return err }) - return infos, errors.Wrap(err, "unpriv.readdir") + if err != nil { + return nil, fmt.Errorf("unpriv.readdir: %w", err) + } + return infos, nil } // Lstat is a wrapper around os.Lstat which has been wrapped with unpriv.Wrap @@ -207,7 +217,10 @@ func Lstat(path string) (os.FileInfo, error) { fi, err = os.Lstat(path) return err }) - return fi, errors.Wrap(err, "unpriv.lstat") + if err != nil { + return nil, fmt.Errorf("unpriv.lstat: %w", err) + } + return fi, nil } // Lstatx is like Lstat but uses unix.Lstat and returns unix.Stat_t instead @@ -216,7 +229,10 @@ func Lstatx(path string) (unix.Stat_t, error) { err := Wrap(path, func(path string) error { return unix.Lstat(path, &s) }) - return s, errors.Wrap(err, "unpriv.lstatx") + if err != nil { + return s, fmt.Errorf("unpriv.lstatx: %w", err) + } + return s, nil } // Readlink is a wrapper around os.Readlink which has been wrapped with @@ -232,7 +248,10 @@ func Readlink(path string) (string, error) { target, err = os.Readlink(path) return err }) - return target, errors.Wrap(err, "unpriv.readlink") + if err != nil { + return "", fmt.Errorf("unpriv.readlink: %w", err) + } + return target, nil } // Symlink is a wrapper around os.Symlink which has been wrapped with @@ -241,9 +260,11 @@ func Readlink(path string) (string, error) { // may not have resolve access after this function returns because all of the // trickery is reverted by unpriv.Wrap. func Symlink(target, linkname string) error { - return errors.Wrap(Wrap(linkname, func(linkname string) error { - return os.Symlink(target, linkname) - }), "unpriv.symlink") + err := Wrap(linkname, func(linkname string) error { return os.Symlink(target, linkname) }) + if err != nil { + return fmt.Errorf("unpriv.symlink: %w", err) + } + return nil } // Link is a wrapper around unix.Link(..., 0) which has been wrapped with @@ -252,53 +273,71 @@ func Symlink(target, linkname string) error { // you may not have resolve access after this function returns because all of // the trickery is reverted by unpriv.Wrap. func Link(target, linkname string) error { - return errors.Wrap(Wrap(linkname, func(linkname string) error { + err := Wrap(linkname, func(linkname string) error { // We have to double-wrap this, because you need search access to the // linkname. This is safe because any common ancestors will be reverted // in reverse call stack order. - return errors.Wrap(Wrap(target, func(target string) error { + err := Wrap(target, func(target string) error { // We need to explicitly pass 0 as a flag because POSIX allows the // default behaviour of link(2) when it comes to target being a // symlink to be implementation-defined. Only linkat(2) allows us // to guarantee the right behaviour. // return unix.Linkat(unix.AT_FDCWD, target, unix.AT_FDCWD, linkname, 0) - }), "unpriv.wrap target") - }), "unpriv.link") + }) + if err != nil { + return fmt.Errorf("unpriv.wrap target: %w", err) + } + return nil + }) + if err != nil { + return fmt.Errorf("unpriv.link: %w", err) + } + return nil } // Chmod is a wrapper around os.Chmod which has been wrapped with unpriv.Wrap // to make it possible to change the permission bits of a path even if you do // not currently have the required access bits to access the path. func Chmod(path string, mode os.FileMode) error { - return errors.Wrap(Wrap(path, func(path string) error { - return os.Chmod(path, mode) - }), "unpriv.chmod") + err := Wrap(path, func(path string) error { return os.Chmod(path, mode) }) + if err != nil { + return fmt.Errorf("unpriv.chmod: %w", err) + } + return nil } // Chtimes is a wrapper around os.Chtimes which has been wrapped with // unpriv.Wrap to make it possible to change the modified times of a path even // if you do not currently have the required access bits to access the path. func Chtimes(path string, atime, mtime time.Time) error { - return errors.Wrap(Wrap(path, func(path string) error { - return os.Chtimes(path, atime, mtime) - }), "unpriv.chtimes") + err := Wrap(path, func(path string) error { return os.Chtimes(path, atime, mtime) }) + if err != nil { + return fmt.Errorf("unpriv.chtimes: %w", err) + } + return nil } // Lutimes is a wrapper around system.Lutimes which has been wrapped with // unpriv.Wrap to make it possible to change the modified times of a path even // if you do no currently have the required access bits to access the path. func Lutimes(path string, atime, mtime time.Time) error { - return errors.Wrap(Wrap(path, func(path string) error { - return system.Lutimes(path, atime, mtime) - }), "unpriv.lutimes") + err := Wrap(path, func(path string) error { return system.Lutimes(path, atime, mtime) }) + if err != nil { + return fmt.Errorf("unpriv.lutimes: %w", err) + } + return nil } // Remove is a wrapper around os.Remove which has been wrapped with unpriv.Wrap // to make it possible to remove a path even if you do not currently have the // required access bits to modify or resolve the path. func Remove(path string) error { - return errors.Wrap(Wrap(path, os.Remove), "unpriv.remove") + err := Wrap(path, os.Remove) + if err != nil { + return fmt.Errorf("unpriv.remove: %w", err) + } + return nil } // foreachSubpath executes WrapFunc for each child of the given path (not @@ -310,7 +349,7 @@ func foreachSubpath(path string, wrapFn WrapFunc) error { // Is the path a directory? fi, err := os.Lstat(path) if err != nil { - return errors.WithStack(err) + return err } if !fi.IsDir() { return nil @@ -319,7 +358,7 @@ func foreachSubpath(path string, wrapFn WrapFunc) error { // Open the directory. fd, err := Open(path) if err != nil { - return errors.WithStack(err) + return err } defer fd.Close() @@ -332,7 +371,7 @@ func foreachSubpath(path string, wrapFn WrapFunc) error { names, err := fd.Readdirnames(-1) fiRestore(path, fi) if err != nil { - return errors.WithStack(err) + return err } // Make iteration order consistent. @@ -353,10 +392,10 @@ func foreachSubpath(path string, wrapFn WrapFunc) error { // wrapped with unpriv.Wrap to make it possible to remove a path (even if it // has child paths) even if you do not currently have enough access bits. func RemoveAll(path string) error { - return errors.Wrap(Wrap(path, func(path string) error { + err := Wrap(path, func(path string) error { // If remove works, we're done. err := os.Remove(path) - if err == nil || os.IsNotExist(errors.Cause(err)) { + if err == nil || errors.Is(err, os.ErrNotExist) { return nil } @@ -364,17 +403,16 @@ func RemoveAll(path string) error { fi, serr := os.Lstat(path) if serr != nil { // Use securejoin's IsNotExist to handle ENOTDIR sanely. - if securejoin.IsNotExist(errors.Cause(serr)) { - serr = nil + if !securejoin.IsNotExist(serr) { + return fmt.Errorf("lstat: %w", serr) } - return errors.Wrap(serr, "lstat") + return nil } // Return error from remove if it's not a directory. if !fi.IsDir() { - return errors.Wrap(err, "remove non-directory") + return fmt.Errorf("remove non-directory: %w", err) } err = nil - err1 := foreachSubpath(path, func(subpath string) error { err2 := RemoveAll(subpath) if err == nil { @@ -384,38 +422,44 @@ func RemoveAll(path string) error { }) if err1 != nil { // We must have hit a race, but we don't care. - if os.IsNotExist(errors.Cause(err1)) { + if errors.Is(err1, os.ErrNotExist) { err1 = nil } - return errors.Wrap(err1, "foreach subpath") + return fmt.Errorf("foreach subpath: %w", err1) } // Remove the directory. This should now work. err1 = os.Remove(path) - if err1 == nil || os.IsNotExist(errors.Cause(err1)) { + if err1 == nil || errors.Is(err1, os.ErrNotExist) { return nil } if err == nil { err = err1 } - return errors.Wrap(err, "remove") - }), "unpriv.removeall") + return fmt.Errorf("remove: %w", err) + }) + if err != nil { + return fmt.Errorf("unpriv.removeall: %w", err) + } + return nil } // Mkdir is a wrapper around os.Mkdir which has been wrapped with unpriv.Wrap // to make it possible to remove a path even if you do not currently have the // required access bits to modify or resolve the path. func Mkdir(path string, perm os.FileMode) error { - return errors.Wrap(Wrap(path, func(path string) error { - return os.Mkdir(path, perm) - }), "unpriv.mkdir") + err := Wrap(path, func(path string) error { return os.Mkdir(path, perm) }) + if err != nil { + return fmt.Errorf("unpriv.mkdir: %w", err) + } + return nil } // MkdirAll is similar to os.MkdirAll but in order to implement it properly all // of the internal functions were wrapped with unpriv.Wrap to make it possible // to create a path even if you do not currently have enough access bits. func MkdirAll(path string, perm os.FileMode) error { - return errors.Wrap(Wrap(path, func(path string) error { + err := Wrap(path, func(path string) error { // Check whether the path already exists. fi, err := os.Stat(path) if err == nil { @@ -445,16 +489,22 @@ func MkdirAll(path string, perm os.FileMode) error { return err } return nil - }), "unpriv.mkdirall") + }) + if err != nil { + return fmt.Errorf("unpriv.mkdirall: %w", err) + } + return nil } // Mknod is a wrapper around unix.Mknod which has been wrapped with unpriv.Wrap // to make it possible to remove a path even if you do not currently have the // required access bits to modify or resolve the path. func Mknod(path string, mode os.FileMode, dev uint64) error { - return errors.Wrap(Wrap(path, func(path string) error { - return system.Mknod(path, uint32(mode), dev) - }), "unpriv.mknod") + err := Wrap(path, func(path string) error { return system.Mknod(path, uint32(mode), dev) }) + if err != nil { + return fmt.Errorf("unpriv.mknod: %w", err) + } + return nil } // Llistxattr is a wrapper around system.Llistxattr which has been wrapped with @@ -467,25 +517,32 @@ func Llistxattr(path string) ([]string, error) { xattrs, err = system.Llistxattr(path) return err }) - return xattrs, errors.Wrap(err, "unpriv.llistxattr") + if err != nil { + return nil, fmt.Errorf("unpriv.llistxattr: %w", err) + } + return xattrs, nil } // Lremovexattr is a wrapper around system.Lremovexattr which has been wrapped // with unpriv.Wrap to make it possible to remove a path even if you do not // currently have the required access bits to resolve the path. func Lremovexattr(path, name string) error { - return errors.Wrap(Wrap(path, func(path string) error { - return unix.Lremovexattr(path, name) - }), "unpriv.lremovexattr") + err := Wrap(path, func(path string) error { return unix.Lremovexattr(path, name) }) + if err != nil { + return fmt.Errorf("unpriv.lremovexattr: %w", err) + } + return nil } // Lsetxattr is a wrapper around system.Lsetxattr which has been wrapped // with unpriv.Wrap to make it possible to set a path even if you do not // currently have the required access bits to resolve the path. func Lsetxattr(path, name string, value []byte, flags int) error { - return errors.Wrap(Wrap(path, func(path string) error { - return unix.Lsetxattr(path, name, value, flags) - }), "unpriv.lsetxattr") + err := Wrap(path, func(path string) error { return unix.Lsetxattr(path, name, value, flags) }) + if err != nil { + return fmt.Errorf("unpriv.lsetxattr: %w", err) + } + return nil } // Lgetxattr is a wrapper around system.Lgetxattr which has been wrapped @@ -498,7 +555,10 @@ func Lgetxattr(path, name string) ([]byte, error) { value, err = system.Lgetxattr(path, name) return err }) - return value, errors.Wrap(err, "unpriv.lgetxattr") + if err != nil { + return nil, fmt.Errorf("unpriv.lgetxattr: %w", err) + } + return value, nil } // Lclearxattrs is similar to system.Lclearxattrs but in order to implement it @@ -506,7 +566,7 @@ func Lgetxattr(path, name string) ([]byte, error) { // it possible to create a path even if you do not currently have enough access // bits. func Lclearxattrs(path string, except map[string]struct{}) error { - return errors.Wrap(Wrap(path, func(path string) error { + err := Wrap(path, func(path string) error { names, err := Llistxattr(path) if err != nil { return err @@ -520,14 +580,18 @@ func Lclearxattrs(path string, except map[string]struct{}) error { // security reasons), so we don't clear xattrs if attempting to // clear them causes an EPERM. This EPERM will not be due to // resolution issues (Llistxattr already has done that for us). - if os.IsPermission(errors.Cause(err)) { + if errors.Is(err, os.ErrPermission) { continue } return err } } return nil - }), "unpriv.lclearxattrs") + }) + if err != nil { + return fmt.Errorf("unpriv.lclearxattrs: %w", err) + } + return nil } // walk is the inner implementation of Walk. @@ -546,14 +610,14 @@ func walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { // If it doesn't exist, just pass it directly to walkFn. if err := walkFn(subpath, info, err); err != nil { // Ignore SkipDir. - if errors.Cause(err) != filepath.SkipDir { + if errors.Is(err, filepath.SkipDir) { return err } } } else { if err := walk(subpath, info, walkFn); err != nil { // Ignore error if it's SkipDir and subpath is a directory. - if !(info.IsDir() && errors.Cause(err) == filepath.SkipDir) { + if !(info.IsDir() && errors.Is(err, filepath.SkipDir)) { return err } } @@ -575,9 +639,9 @@ func Walk(root string, walkFn filepath.WalkFunc) error { } else { err = walk(root, info, walkFn) } - if errors.Cause(err) == filepath.SkipDir { - err = nil + if err == nil || errors.Is(err, filepath.SkipDir) { + return nil } - return errors.Wrap(err, "unpriv.walk") + return fmt.Errorf("unpriv.walk: %w", err) }) } diff --git a/pkg/unpriv/unpriv_test.go b/pkg/unpriv/unpriv_test.go index a5692ee3e..c1325da13 100644 --- a/pkg/unpriv/unpriv_test.go +++ b/pkg/unpriv/unpriv_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package unpriv import ( "archive/tar" "bytes" + "errors" "io/ioutil" "os" "path/filepath" @@ -27,7 +28,6 @@ import ( "testing" "github.com/opencontainers/umoci/pkg/testutils" - "github.com/pkg/errors" ) func TestWrapNoTricks(t *testing.T) { @@ -142,7 +142,7 @@ func TestLstat(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -225,13 +225,13 @@ func TestReadlink(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "link1")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "link2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -316,13 +316,13 @@ func TestSymlink(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "link1")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "link2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -459,7 +459,7 @@ func TestOpen(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -570,7 +570,7 @@ func TestReaddir(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } @@ -664,7 +664,7 @@ func TestWrapWrite(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -816,19 +816,19 @@ func TestLink(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "file2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -896,10 +896,10 @@ func TestChtimes(t *testing.T) { t.Errorf("mtime was unchanged! %s", hdrNew.ModTime) } if !hdrNew.ModTime.Equal(mtime) { - t.Errorf("mtime was not change to correct value. expected='%s' got='%s'", mtime, hdrNew.ModTime) + t.Errorf("mtime was not change to correct value. expected=%q got=%q", mtime, hdrNew.ModTime) } if !hdrNew.AccessTime.Equal(atime) { - t.Errorf("atime was not change to correct value. expected='%s' got='%s'", atime, hdrNew.AccessTime) + t.Errorf("atime was not change to correct value. expected=%q got=%q", atime, hdrNew.AccessTime) } // Check that the parents were unchanged. @@ -929,19 +929,19 @@ func TestChtimes(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "file2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -1012,10 +1012,10 @@ func TestLutimes(t *testing.T) { t.Errorf("mtime was unchanged! %s", hdrDirNew.ModTime) } if !hdrDirNew.ModTime.Equal(mtime) { - t.Errorf("mtime was not change to correct value. expected='%s' got='%s'", mtime, hdrDirNew.ModTime) + t.Errorf("mtime was not change to correct value. expected=%q got=%q", mtime, hdrDirNew.ModTime) } if !hdrDirNew.AccessTime.Equal(atime) { - t.Errorf("atime was not change to correct value. expected='%s' got='%s'", atime, hdrDirNew.AccessTime) + t.Errorf("atime was not change to correct value. expected=%q got=%q", atime, hdrDirNew.AccessTime) } // Do the same for a symlink. @@ -1043,10 +1043,10 @@ func TestLutimes(t *testing.T) { t.Errorf("mtime was unchanged! %s", hdrNew.ModTime) } if !hdrNew.ModTime.Equal(mtime) { - t.Errorf("mtime was not change to correct value. expected='%s' got='%s'", mtime, hdrNew.ModTime) + t.Errorf("mtime was not change to correct value. expected=%q got=%q", mtime, hdrNew.ModTime) } if !hdrNew.AccessTime.Equal(atime) { - t.Errorf("atime was not change to correct value. expected='%s' got='%s'", atime, hdrNew.AccessTime) + t.Errorf("atime was not change to correct value. expected=%q got=%q", atime, hdrNew.AccessTime) } // Make sure that the parent was not changed by Lutimes. @@ -1057,10 +1057,10 @@ func TestLutimes(t *testing.T) { hdrDirNew2, _ := tar.FileInfoHeader(fi, "") if !hdrDirNew2.AccessTime.Equal(hdrDirNew.AccessTime) { - t.Errorf("atime was changed! expected='%s' got='%s'", hdrDirNew.AccessTime, hdrDirNew2.AccessTime) + t.Errorf("atime was changed! expected=%q got=%q", hdrDirNew.AccessTime, hdrDirNew2.AccessTime) } if !hdrDirNew2.ModTime.Equal(hdrDirNew.ModTime) { - t.Errorf("mtime was changed! expected='%s' got='%s'", hdrDirNew.ModTime, hdrDirNew2.ModTime) + t.Errorf("mtime was changed! expected=%q got=%q", hdrDirNew.ModTime, hdrDirNew2.ModTime) } // Check that the parents were unchanged. @@ -1090,19 +1090,19 @@ func TestLutimes(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "directories", "file2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "parent", "file2")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -1175,16 +1175,16 @@ func TestRemove(t *testing.T) { } // Check that they are gone. - if _, err := Lstat(filepath.Join(dir, "some", "parent", "directories")); !os.IsNotExist(errors.Cause(err)) { + if _, err := Lstat(filepath.Join(dir, "some", "parent", "directories")); !errors.Is(err, os.ErrNotExist) { t.Errorf("expected deleted directory to give ENOENT: %s", err) } - if _, err := Lstat(filepath.Join(dir, "some", "cousin", "directories")); !os.IsNotExist(errors.Cause(err)) { + if _, err := Lstat(filepath.Join(dir, "some", "cousin", "directories")); !errors.Is(err, os.ErrNotExist) { t.Errorf("expected deleted directory to give ENOENT: %s", err) } - if _, err := Lstat(filepath.Join(dir, "some", "cousin", "directories")); !os.IsNotExist(errors.Cause(err)) { + if _, err := Lstat(filepath.Join(dir, "some", "cousin", "directories")); !errors.Is(err, os.ErrNotExist) { t.Errorf("expected deleted file to give ENOENT: %s", err) } - if _, err := Lstat(filepath.Join(dir, "some", "parent", "file2")); !os.IsNotExist(errors.Cause(err)) { + if _, err := Lstat(filepath.Join(dir, "some", "parent", "file2")); !errors.Is(err, os.ErrNotExist) { t.Errorf("expected deleted file to give ENOENT: %s", err) } } @@ -1248,7 +1248,7 @@ func TestRemoveAll(t *testing.T) { } // Check that they are gone. - if _, err := Lstat(filepath.Join(dir, "some", "parent")); !os.IsNotExist(errors.Cause(err)) { + if _, err := Lstat(filepath.Join(dir, "some", "parent")); !errors.Is(err, os.ErrNotExist) { t.Errorf("expected deleted directory to give ENOENT: %s", err) } if _, err := Lstat(filepath.Join(dir, "some")); err != nil { @@ -1328,19 +1328,19 @@ func TestMkdir(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "child")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "other-child")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "child", "dir")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } @@ -1447,19 +1447,19 @@ func TestMkdirAll(t *testing.T) { fi, err = os.Lstat(filepath.Join(dir, "some", "child")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "other-child")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } fi, err = os.Lstat(filepath.Join(dir, "some", "child", "dir")) if err == nil { t.Errorf("expected os.Lstat to give EPERM -- got no error!") - } else if !os.IsPermission(errors.Cause(err)) { + } else if !errors.Is(err, os.ErrPermission) { t.Errorf("expected os.Lstat to give EPERM -- got %s", err) } } diff --git a/pkg/unpriv/unpriv_utimes_test.go b/pkg/unpriv/unpriv_utimes_test.go index 2d3470f01..df2a1b56f 100644 --- a/pkg/unpriv/unpriv_utimes_test.go +++ b/pkg/unpriv/unpriv_utimes_test.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,13 +67,13 @@ func TestLutimesFile(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } } @@ -116,13 +116,13 @@ func TestLutimesDirectory(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } } @@ -171,13 +171,13 @@ func TestLutimesSymlink(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } // Make sure that the parent directory was unchanged. @@ -187,10 +187,10 @@ func TestLutimesSymlink(t *testing.T) { mtimeParentNew := time.Unix(fiParentNew.Mtim.Unix()) if !atimeParentOld.Equal(atimeParentNew) { - t.Errorf("parent directory atime was changed! old='%s' new='%s'", atimeParentOld, atimeParentNew) + t.Errorf("parent directory atime was changed! old=%q new=%q", atimeParentOld, atimeParentNew) } if !mtimeParentOld.Equal(mtimeParentNew) { - t.Errorf("parent directory mtime was changed! old='%s' new='%s'", mtimeParentOld, mtimeParentNew) + t.Errorf("parent directory mtime was changed! old=%q new=%q", mtimeParentOld, mtimeParentNew) } } @@ -249,13 +249,13 @@ func TestLutimesRelative(t *testing.T) { t.Errorf("atime was not changed at all!") } if !atimeNew.Equal(atime) { - t.Errorf("atime was not changed to expected value: expected='%s' got='%s' old='%s'", atime, atimeNew, atimeOld) + t.Errorf("atime was not changed to expected value: expected=%q got=%q old=%q", atime, atimeNew, atimeOld) } if mtimeOld.Equal(mtimeNew) { t.Errorf("mtime was not changed at all!") } if !mtimeNew.Equal(mtime) { - t.Errorf("mtime was not changed: expected='%s' got='%s' old='%s'", mtime, mtimeNew, mtimeOld) + t.Errorf("mtime was not changed: expected=%q got=%q old=%q", mtime, mtimeNew, mtimeOld) } // Make sure that the parent directory was unchanged. @@ -265,9 +265,9 @@ func TestLutimesRelative(t *testing.T) { mtimeParentNew := time.Unix(fiParentNew.Mtim.Unix()) if !atimeParentOld.Equal(atimeParentNew) { - t.Errorf("parent directory atime was changed! old='%s' new='%s'", atimeParentOld, atimeParentNew) + t.Errorf("parent directory atime was changed! old=%q new=%q", atimeParentOld, atimeParentNew) } if !mtimeParentOld.Equal(mtimeParentNew) { - t.Errorf("parent directory mtime was changed! old='%s' new='%s'", mtimeParentOld, mtimeParentNew) + t.Errorf("parent directory mtime was changed! old=%q new=%q", mtimeParentOld, mtimeParentNew) } } diff --git a/repack.go b/repack.go index 16fd443c6..7b48da4f3 100644 --- a/repack.go +++ b/repack.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package umoci import ( "context" + "fmt" "os" "path/filepath" "strings" @@ -30,7 +31,6 @@ import ( "github.com/opencontainers/umoci/oci/layer" "github.com/opencontainers/umoci/pkg/fseval" "github.com/opencontainers/umoci/pkg/mtreefilter" - "github.com/pkg/errors" "github.com/vbatts/go-mtree" ) @@ -49,13 +49,13 @@ func Repack(engineExt casext.Engine, tagName string, bundlePath string, meta Met mfh, err := os.Open(mtreePath) if err != nil { - return errors.Wrap(err, "open mtree") + return fmt.Errorf("open mtree: %w", err) } defer mfh.Close() spec, err := mtree.ParseSpec(mfh) if err != nil { - return errors.Wrap(err, "parse mtree") + return fmt.Errorf("parse mtree: %w", err) } log.WithFields(log.Fields{ @@ -70,7 +70,7 @@ func Repack(engineExt casext.Engine, tagName string, bundlePath string, meta Met log.Info("computing filesystem diff ...") diffs, err := mtree.Check(fullRootfsPath, spec, MtreeKeywords, fsEval) if err != nil { - return errors.Wrap(err, "check mtree") + return fmt.Errorf("check mtree: %w", err) } log.Info("... done") @@ -108,26 +108,26 @@ func Repack(engineExt casext.Engine, tagName string, bundlePath string, meta Met } reader, err := layer.GenerateLayer(fullRootfsPath, diffs, &packOptions) if err != nil { - return errors.Wrap(err, "generate diff layer") + return fmt.Errorf("generate diff layer: %w", err) } defer reader.Close() // TODO: We should add a flag to allow for a new layer to be made // non-distributable. if _, err := mutator.Add(context.Background(), ispec.MediaTypeImageLayer, reader, history, mutate.GzipCompressor, nil); err != nil { - return errors.Wrap(err, "add diff layer") + return fmt.Errorf("add diff layer: %w", err) } } newDescriptorPath, err := mutator.Commit(context.Background()) if err != nil { - return errors.Wrap(err, "commit mutated image") + return fmt.Errorf("commit mutated image: %w", err) } log.Infof("new image manifest created: %s->%s", newDescriptorPath.Root().Digest, newDescriptorPath.Descriptor().Digest) if err := engineExt.UpdateReference(context.Background(), tagName, newDescriptorPath.Root()); err != nil { - return errors.Wrap(err, "add new tag") + return fmt.Errorf("add new tag: %w", err) } log.Infof("created new tag for image manifest: %s", tagName) @@ -135,14 +135,14 @@ func Repack(engineExt casext.Engine, tagName string, bundlePath string, meta Met if refreshBundle { newMtreeName := strings.Replace(newDescriptorPath.Descriptor().Digest.String(), ":", "_", 1) if err := GenerateBundleManifest(newMtreeName, bundlePath, fsEval); err != nil { - return errors.Wrap(err, "write mtree metadata") + return fmt.Errorf("write mtree metadata: %w", err) } if err := os.Remove(mtreePath); err != nil { - return errors.Wrap(err, "remove old mtree metadata") + return fmt.Errorf("remove old mtree metadata: %w", err) } meta.From = newDescriptorPath if err := WriteBundleMeta(bundlePath, meta); err != nil { - return errors.Wrap(err, "write umoci.json metadata") + return fmt.Errorf("write umoci.json metadata: %w", err) } } return nil diff --git a/unpack.go b/unpack.go index 2e52c5f7f..87ca4f603 100644 --- a/unpack.go +++ b/unpack.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import ( "github.com/opencontainers/umoci/oci/casext" "github.com/opencontainers/umoci/oci/layer" "github.com/opencontainers/umoci/pkg/fseval" - "github.com/pkg/errors" ) // Unpack unpacks an image to the specified bundle path. @@ -40,25 +39,25 @@ func Unpack(engineExt casext.Engine, fromName string, bundlePath string, unpackO fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) if err != nil { - return errors.Wrap(err, "get descriptor") + return fmt.Errorf("get descriptor: %w", err) } if len(fromDescriptorPaths) == 0 { - return errors.Errorf("tag is not found: %s", fromName) + return fmt.Errorf("tag is not found: %s", fromName) } if len(fromDescriptorPaths) != 1 { // TODO: Handle this more nicely. - return errors.Errorf("tag is ambiguous: %s", fromName) + return fmt.Errorf("tag is ambiguous: %s", fromName) } meta.From = fromDescriptorPaths[0] manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor()) if err != nil { - return errors.Wrap(err, "get manifest") + return fmt.Errorf("get manifest: %w", err) } defer manifestBlob.Close() if manifestBlob.Descriptor.MediaType != ispec.MediaTypeImageManifest { - return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType), "invalid --image tag") + return fmt.Errorf("invalid --image tag: descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType) } mtreeName := strings.Replace(meta.From.Descriptor().Digest.String(), ":", "_", 1) @@ -72,18 +71,18 @@ func Unpack(engineExt casext.Engine, fromName string, bundlePath string, unpackO manifest, ok := manifestBlob.Data.(ispec.Manifest) if !ok { // Should _never_ be reached. - return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) + return fmt.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) } // Unpack the runtime bundle. if err := os.MkdirAll(bundlePath, 0755); err != nil { - return errors.Wrap(err, "create bundle path") + return fmt.Errorf("create bundle path: %w", err) } // XXX: We should probably defer os.RemoveAll(bundlePath). log.Info("unpacking bundle ...") if err := layer.UnpackManifest(context.Background(), engineExt, bundlePath, manifest, &unpackOptions); err != nil { - return errors.Wrap(err, "create runtime bundle") + return fmt.Errorf("create runtime bundle: %w", err) } log.Info("... done") @@ -93,7 +92,7 @@ func Unpack(engineExt casext.Engine, fromName string, bundlePath string, unpackO } if err := GenerateBundleManifest(mtreeName, bundlePath, fsEval); err != nil { - return errors.Wrap(err, "write mtree") + return fmt.Errorf("write mtree: %w", err) } log.WithFields(log.Fields{ @@ -103,7 +102,7 @@ func Unpack(engineExt casext.Engine, fromName string, bundlePath string, unpackO }).Debugf("umoci: saving Meta metadata") if err := WriteBundleMeta(bundlePath, meta); err != nil { - return errors.Wrap(err, "write umoci.json metadata") + return fmt.Errorf("write umoci.json metadata: %w", err) } log.Infof("unpacked image bundle: %s", bundlePath) diff --git a/utils.go b/utils.go index 1523ebc39..44f71785a 100644 --- a/utils.go +++ b/utils.go @@ -1,6 +1,6 @@ /* * umoci: Umoci Modifies Open Containers' Images - * Copyright (C) 2016-2020 SUSE LLC + * Copyright (C) 2016-2024 SUSE LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,6 @@ import ( igen "github.com/opencontainers/umoci/oci/config/generate" "github.com/opencontainers/umoci/oci/layer" "github.com/opencontainers/umoci/pkg/idtools" - "github.com/pkg/errors" "github.com/urfave/cli" "github.com/vbatts/go-mtree" ) @@ -106,12 +105,14 @@ func (m Meta) WriteTo(w io.Writer) (int64, error) { func WriteBundleMeta(bundle string, meta Meta) error { fh, err := os.Create(filepath.Join(bundle, MetaName)) if err != nil { - return errors.Wrap(err, "create metadata") + return fmt.Errorf("create metadata: %w", err) } defer fh.Close() - _, err = meta.WriteTo(fh) - return errors.Wrap(err, "write metadata") + if _, err := meta.WriteTo(fh); err != nil { + return fmt.Errorf("write metadata: %w", err) + } + return nil } // ReadBundleMeta reads and parses the umoci.json file from a given bundle path. @@ -120,7 +121,7 @@ func ReadBundleMeta(bundle string) (Meta, error) { fh, err := os.Open(filepath.Join(bundle, MetaName)) if err != nil { - return meta, errors.Wrap(err, "open metadata") + return meta, fmt.Errorf("open metadata: %w", err) } defer fh.Close() @@ -130,7 +131,10 @@ func ReadBundleMeta(bundle string) (Meta, error) { err = fmt.Errorf("unsupported umoci.json version: %s", meta.Version) } } - return meta, errors.Wrap(err, "decode metadata") + if err != nil { + return meta, fmt.Errorf("decode metadata: %w", err) + } + return meta, nil } // ManifestStat has information about a given OCI manifest. @@ -200,7 +204,7 @@ func Stat(ctx context.Context, engine casext.Engine, manifestDescriptor ispec.De var stat ManifestStat if manifestDescriptor.MediaType != ispec.MediaTypeImageManifest { - return stat, errors.Errorf("stat: cannot stat a non-manifest descriptor: invalid media type '%s'", manifestDescriptor.MediaType) + return stat, fmt.Errorf("stat: cannot stat a non-manifest descriptor: invalid media type %q", manifestDescriptor.MediaType) } // We have to get the actual manifest. @@ -211,18 +215,18 @@ func Stat(ctx context.Context, engine casext.Engine, manifestDescriptor ispec.De manifest, ok := manifestBlob.Data.(ispec.Manifest) if !ok { // Should _never_ be reached. - return stat, errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) + return stat, fmt.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) } // Now get the config. configBlob, err := engine.FromDescriptor(ctx, manifest.Config) if err != nil { - return stat, errors.Wrap(err, "stat") + return stat, fmt.Errorf("stat: %w", err) } config, ok := configBlob.Data.(ispec.Image) if !ok { // Should _never_ be reached. - return stat, errors.Errorf("[internal error] unknown config blob type: %s", configBlob.Descriptor.MediaType) + return stat, fmt.Errorf("[internal error] unknown config blob type: %s", configBlob.Descriptor.MediaType) } // TODO: This should probably be moved into separate functions. @@ -267,21 +271,21 @@ func GenerateBundleManifest(mtreeName string, bundlePath string, fsEval mtree.Fs log.Info("computing filesystem manifest ...") dh, err := mtree.Walk(fullRootfsPath, nil, MtreeKeywords, fsEval) if err != nil { - return errors.Wrap(err, "generate mtree spec") + return fmt.Errorf("generate mtree spec: %w", err) } log.Info("... done") flags := os.O_CREATE | os.O_WRONLY | os.O_EXCL fh, err := os.OpenFile(mtreePath, flags, 0644) if err != nil { - return errors.Wrap(err, "open mtree") + return fmt.Errorf("open mtree: %w", err) } defer fh.Close() log.Debugf("umoci: saving mtree manifest") if _, err := dh.WriteTo(fh); err != nil { - return errors.Wrap(err, "write mtree") + return fmt.Errorf("write mtree: %w", err) } return nil @@ -296,13 +300,13 @@ func ParseIdmapOptions(meta *Meta, ctx *cli.Context) error { if !ctx.IsSet("uid-map") { if err := ctx.Set("uid-map", fmt.Sprintf("0:%d:1", os.Geteuid())); err != nil { // Should _never_ be reached. - return errors.Wrap(err, "[internal error] failure auto-setting rootless --uid-map") + return fmt.Errorf("[internal error] failure auto-setting rootless --uid-map: %w", err) } } if !ctx.IsSet("gid-map") { if err := ctx.Set("gid-map", fmt.Sprintf("0:%d:1", os.Getegid())); err != nil { // Should _never_ be reached. - return errors.Wrap(err, "[internal error] failure auto-setting rootless --gid-map") + return fmt.Errorf("[internal error] failure auto-setting rootless --gid-map: %w", err) } } } @@ -310,14 +314,14 @@ func ParseIdmapOptions(meta *Meta, ctx *cli.Context) error { for _, uidmap := range ctx.StringSlice("uid-map") { idMap, err := idtools.ParseMapping(uidmap) if err != nil { - return errors.Wrapf(err, "failure parsing --uid-map %s", uidmap) + return fmt.Errorf("failure parsing --uid-map %s: %w", uidmap, err) } meta.MapOptions.UIDMappings = append(meta.MapOptions.UIDMappings, idMap) } for _, gidmap := range ctx.StringSlice("gid-map") { idMap, err := idtools.ParseMapping(gidmap) if err != nil { - return errors.Wrapf(err, "failure parsing --gid-map %s", gidmap) + return fmt.Errorf("failure parsing --gid-map %s: %w", gidmap, err) } meta.MapOptions.GIDMappings = append(meta.MapOptions.GIDMappings, idMap) }