github.com/containerd/Containerd@v1.4.13/snapshots/devmapper/losetup/losetup.go (about) 1 // +build linux 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package losetup 20 21 import ( 22 "os/exec" 23 "strings" 24 25 "github.com/pkg/errors" 26 "golang.org/x/sys/unix" 27 ) 28 29 // FindAssociatedLoopDevices returns a list of loop devices attached to a given image 30 func FindAssociatedLoopDevices(imagePath string) ([]string, error) { 31 output, err := losetup("--list", "--output", "NAME", "--associated", imagePath) 32 if err != nil { 33 return nil, errors.Wrapf(err, "failed to get loop devices: '%s'", output) 34 } 35 36 if output == "" { 37 return []string{}, nil 38 } 39 40 items := strings.Split(output, "\n") 41 if len(items) <= 1 { 42 return []string{}, nil 43 } 44 45 // Skip header with column names 46 return items[1:], nil 47 } 48 49 // AttachLoopDevice finds first available loop device and associates it with an image. 50 func AttachLoopDevice(imagePath string) (string, error) { 51 return losetup("--find", "--show", imagePath) 52 } 53 54 // DetachLoopDevice detaches loop devices 55 func DetachLoopDevice(loopDevice ...string) error { 56 args := append([]string{"--detach"}, loopDevice...) 57 _, err := losetup(args...) 58 return err 59 } 60 61 // RemoveLoopDevicesAssociatedWithImage detaches all loop devices attached to a given sparse image 62 func RemoveLoopDevicesAssociatedWithImage(imagePath string) error { 63 loopDevices, err := FindAssociatedLoopDevices(imagePath) 64 if err != nil { 65 return err 66 } 67 68 for _, loopDevice := range loopDevices { 69 if err = DetachLoopDevice(loopDevice); err != nil && err != unix.ENOENT { 70 return err 71 } 72 } 73 74 return nil 75 } 76 77 // losetup is a wrapper around losetup command line tool 78 func losetup(args ...string) (string, error) { 79 cmd := exec.Command("losetup", args...) 80 cmd.Env = append(cmd.Env, "LANG=C") 81 data, err := cmd.CombinedOutput() 82 output := string(data) 83 if err != nil { 84 if strings.Contains(output, "No such file or directory") || strings.Contains(output, "No such device") { 85 return "", unix.ENOENT 86 } 87 88 return "", errors.Wrapf(err, "losetup %s\nerror: %s\n", strings.Join(args, " "), output) 89 } 90 91 return strings.TrimSuffix(output, "\n"), err 92 }