github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/cmd/umoci/raw-unpack.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016-2020 SUSE LLC 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package main 19 20 import ( 21 "context" 22 "fmt" 23 24 "github.com/apex/log" 25 ispec "github.com/opencontainers/image-spec/specs-go/v1" 26 "github.com/opencontainers/umoci" 27 "github.com/opencontainers/umoci/oci/cas/dir" 28 "github.com/opencontainers/umoci/oci/casext" 29 "github.com/opencontainers/umoci/oci/layer" 30 "github.com/pkg/errors" 31 "github.com/urfave/cli" 32 ) 33 34 var rawUnpackCommand = uxRemap(cli.Command{ 35 Name: "unpack", 36 Usage: "unpacks a reference into a rootfs", 37 ArgsUsage: `--image <image-path>[:<tag>] <rootfs> 38 39 Where "<image-path>" is the path to the OCI image, "<tag>" is the name of the 40 tagged image to unpack (if not specified, defaults to "latest") and "<rootfs>" 41 is the destination to unpack the image to.`, 42 43 // unpack reads manifest information. 44 Category: "image", 45 46 Flags: []cli.Flag{ 47 cli.BoolFlag{ 48 Name: "keep-dirlinks", 49 Usage: "don't clobber underlying symlinks to directories", 50 }, 51 }, 52 53 Action: rawUnpack, 54 55 Before: func(ctx *cli.Context) error { 56 if ctx.NArg() != 1 { 57 return errors.Errorf("invalid number of positional arguments: expected <rootfs>") 58 } 59 if ctx.Args().First() == "" { 60 return errors.Errorf("rootfs path cannot be empty") 61 } 62 ctx.App.Metadata["rootfs"] = ctx.Args().First() 63 return nil 64 }, 65 }) 66 67 func rawUnpack(ctx *cli.Context) error { 68 imagePath := ctx.App.Metadata["--image-path"].(string) 69 fromName := ctx.App.Metadata["--image-tag"].(string) 70 rootfsPath := ctx.App.Metadata["rootfs"].(string) 71 72 var unpackOptions layer.UnpackOptions 73 var meta umoci.Meta 74 meta.Version = umoci.MetaVersion 75 76 // Parse map options. 77 // We need to set mappings if we're in rootless mode. 78 err := umoci.ParseIdmapOptions(&meta, ctx) 79 if err != nil { 80 return err 81 } 82 83 unpackOptions.KeepDirlinks = ctx.Bool("keep-dirlinks") 84 unpackOptions.MapOptions = meta.MapOptions 85 86 // Get a reference to the CAS. 87 engine, err := dir.Open(imagePath) 88 if err != nil { 89 return errors.Wrap(err, "open CAS") 90 } 91 engineExt := casext.NewEngine(engine) 92 defer engine.Close() 93 94 fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) 95 if err != nil { 96 return errors.Wrap(err, "get descriptor") 97 } 98 if len(fromDescriptorPaths) == 0 { 99 return errors.Errorf("tag is not found: %s", fromName) 100 } 101 if len(fromDescriptorPaths) != 1 { 102 // TODO: Handle this more nicely. 103 return errors.Errorf("tag is ambiguous: %s", fromName) 104 } 105 meta.From = fromDescriptorPaths[0] 106 107 manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor()) 108 if err != nil { 109 return errors.Wrap(err, "get manifest") 110 } 111 defer manifestBlob.Close() 112 113 if manifestBlob.Descriptor.MediaType != ispec.MediaTypeImageManifest { 114 return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType), "invalid --image tag") 115 } 116 117 log.WithFields(log.Fields{ 118 "image": imagePath, 119 "rootfs": rootfsPath, 120 "ref": fromName, 121 }).Debugf("umoci: unpacking OCI image") 122 123 // Get the manifest. 124 manifest, ok := manifestBlob.Data.(ispec.Manifest) 125 if !ok { 126 // Should _never_ be reached. 127 return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) 128 } 129 130 log.Warnf("unpacking rootfs ...") 131 if err := layer.UnpackRootfs(context.Background(), engineExt, rootfsPath, manifest, &unpackOptions); err != nil { 132 return errors.Wrap(err, "create rootfs") 133 } 134 log.Warnf("... done") 135 136 log.Warnf("unpacked image rootfs: %s", rootfsPath) 137 return nil 138 }