github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/pkg/loopback/attach_loopback.go (about) 1 //go:build linux 2 // +build linux 3 4 package loopback // import "github.com/docker/docker/pkg/loopback" 5 6 import ( 7 "errors" 8 "fmt" 9 "os" 10 11 "github.com/sirupsen/logrus" 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) [LoNameSize]uint8 { 23 var dst [LoNameSize]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, 0644) 30 if err != nil { 31 return 0, err 32 } 33 defer f.Close() 34 35 index, err := ioctlLoopCtlGetFree(f.Fd()) 36 if index < 0 { 37 index = 0 38 } 39 return index, err 40 } 41 42 func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.File, err error) { 43 // Start looking for a free /dev/loop 44 for { 45 target := fmt.Sprintf("/dev/loop%d", index) 46 index++ 47 48 fi, err := os.Stat(target) 49 if err != nil { 50 if os.IsNotExist(err) { 51 logrus.Error("There are no more loopback devices available.") 52 } 53 return nil, ErrAttachLoopbackDevice 54 } 55 56 if fi.Mode()&os.ModeDevice != os.ModeDevice { 57 logrus.Errorf("Loopback device %s is not a block device.", target) 58 continue 59 } 60 61 // OpenFile adds O_CLOEXEC 62 loopFile, err = os.OpenFile(target, os.O_RDWR, 0644) 63 if err != nil { 64 logrus.Errorf("Error opening loopback device: %s", err) 65 return nil, ErrAttachLoopbackDevice 66 } 67 68 // Try to attach to the loop file 69 if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil { 70 loopFile.Close() 71 72 // If the error is EBUSY, then try the next loopback 73 if err != unix.EBUSY { 74 logrus.Errorf("Cannot set up loopback device %s: %s", target, err) 75 return nil, ErrAttachLoopbackDevice 76 } 77 78 // Otherwise, we keep going with the loop 79 continue 80 } 81 // In case of success, we finished. Break the loop. 82 break 83 } 84 85 // This can't happen, but let's be sure 86 if loopFile == nil { 87 logrus.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name()) 88 return nil, ErrAttachLoopbackDevice 89 } 90 91 return loopFile, nil 92 } 93 94 // AttachLoopDevice attaches the given sparse file to the next 95 // available loopback device. It returns an opened *os.File. 96 func AttachLoopDevice(sparseName string) (loop *os.File, err error) { 97 98 // Try to retrieve the next available loopback device via syscall. 99 // If it fails, we discard error and start looping for a 100 // loopback from index 0. 101 startIndex, err := getNextFreeLoopbackIndex() 102 if err != nil { 103 logrus.Debugf("Error retrieving the next available loopback: %s", err) 104 } 105 106 // OpenFile adds O_CLOEXEC 107 sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0644) 108 if err != nil { 109 logrus.Errorf("Error opening sparse file %s: %s", sparseName, err) 110 return nil, ErrAttachLoopbackDevice 111 } 112 defer sparseFile.Close() 113 114 loopFile, err := openNextAvailableLoopback(startIndex, sparseFile) 115 if err != nil { 116 return nil, err 117 } 118 119 // Set the status of the loopback device 120 loopInfo := &unix.LoopInfo64{ 121 File_name: stringToLoopName(loopFile.Name()), 122 Offset: 0, 123 Flags: LoFlagsAutoClear, 124 } 125 126 if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil { 127 logrus.Errorf("Cannot set up loopback device info: %s", err) 128 129 // If the call failed, then free the loopback device 130 if err := ioctlLoopClrFd(loopFile.Fd()); err != nil { 131 logrus.Error("Error while cleaning up the loopback device") 132 } 133 loopFile.Close() 134 return nil, ErrAttachLoopbackDevice 135 } 136 137 return loopFile, nil 138 }