github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/pkg/loopback/attach_loopback.go (about) 1 //go:build linux 2 3 package loopback // import "github.com/Prakhar-Agarwal-byte/moby/pkg/loopback" 4 5 import ( 6 "context" 7 "errors" 8 "fmt" 9 "os" 10 11 "github.com/containerd/log" 12 "golang.org/x/sys/unix" 13 ) 14 15 // Loopback related errors 16 var ( 17 ErrAttachLoopbackDevice = errors.New("loopback attach failed") 18 ErrGetLoopbackBackingFile = errors.New("unable to get loopback backing file") 19 ErrSetCapacity = errors.New("unable set loopback capacity") 20 ) 21 22 func stringToLoopName(src string) [unix.LO_NAME_SIZE]uint8 { 23 var dst [unix.LO_NAME_SIZE]uint8 24 copy(dst[:], src[:]) 25 return dst 26 } 27 28 func getNextFreeLoopbackIndex() (int, error) { 29 f, err := os.OpenFile("/dev/loop-control", os.O_RDONLY, 0o644) 30 if err != nil { 31 return 0, err 32 } 33 defer f.Close() 34 return unix.IoctlRetInt(int(f.Fd()), unix.LOOP_CTL_GET_FREE) 35 } 36 37 func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.File, err error) { 38 // Start looking for a free /dev/loop 39 for { 40 target := fmt.Sprintf("/dev/loop%d", index) 41 index++ 42 43 fi, err := os.Stat(target) 44 if err != nil { 45 if os.IsNotExist(err) { 46 log.G(context.TODO()).Error("There are no more loopback devices available.") 47 } 48 return nil, ErrAttachLoopbackDevice 49 } 50 51 if fi.Mode()&os.ModeDevice != os.ModeDevice { 52 log.G(context.TODO()).Errorf("Loopback device %s is not a block device.", target) 53 continue 54 } 55 56 // OpenFile adds O_CLOEXEC 57 loopFile, err = os.OpenFile(target, os.O_RDWR, 0o644) 58 if err != nil { 59 log.G(context.TODO()).Errorf("Error opening loopback device: %s", err) 60 return nil, ErrAttachLoopbackDevice 61 } 62 63 // Try to attach to the loop file 64 if err = unix.IoctlSetInt(int(loopFile.Fd()), unix.LOOP_SET_FD, int(sparseFile.Fd())); err != nil { 65 loopFile.Close() 66 67 // If the error is EBUSY, then try the next loopback 68 if err != unix.EBUSY { 69 log.G(context.TODO()).Errorf("Cannot set up loopback device %s: %s", target, err) 70 return nil, ErrAttachLoopbackDevice 71 } 72 73 // Otherwise, we keep going with the loop 74 continue 75 } 76 // In case of success, we finished. Break the loop. 77 break 78 } 79 80 // This can't happen, but let's be sure 81 if loopFile == nil { 82 log.G(context.TODO()).Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name()) 83 return nil, ErrAttachLoopbackDevice 84 } 85 86 return loopFile, nil 87 } 88 89 // AttachLoopDevice attaches the given sparse file to the next 90 // available loopback device. It returns an opened *os.File. 91 // 92 // Deprecated: the loopback package is deprected and will be removed in the next release. 93 func AttachLoopDevice(sparseName string) (loop *os.File, err error) { 94 // Try to retrieve the next available loopback device via syscall. 95 // If it fails, we discard error and start looping for a 96 // loopback from index 0. 97 startIndex, err := getNextFreeLoopbackIndex() 98 if err != nil { 99 log.G(context.TODO()).Debugf("Error retrieving the next available loopback: %s", err) 100 } 101 102 // OpenFile adds O_CLOEXEC 103 sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0o644) 104 if err != nil { 105 log.G(context.TODO()).Errorf("Error opening sparse file %s: %s", sparseName, err) 106 return nil, ErrAttachLoopbackDevice 107 } 108 defer sparseFile.Close() 109 110 loopFile, err := openNextAvailableLoopback(startIndex, sparseFile) 111 if err != nil { 112 return nil, err 113 } 114 115 // Set the status of the loopback device 116 loopInfo := &unix.LoopInfo64{ 117 File_name: stringToLoopName(loopFile.Name()), 118 Offset: 0, 119 Flags: unix.LO_FLAGS_AUTOCLEAR, 120 } 121 122 if err = unix.IoctlLoopSetStatus64(int(loopFile.Fd()), loopInfo); err != nil { 123 log.G(context.TODO()).Errorf("Cannot set up loopback device info: %s", err) 124 125 // If the call failed, then free the loopback device 126 if err = unix.IoctlSetInt(int(loopFile.Fd()), unix.LOOP_CLR_FD, 0); err != nil { 127 log.G(context.TODO()).Error("Error while cleaning up the loopback device") 128 } 129 loopFile.Close() 130 return nil, ErrAttachLoopbackDevice 131 } 132 133 return loopFile, nil 134 }