github.com/rumpl/bof@v23.0.0-rc.2+incompatible/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) [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, 0644) 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 logrus.Error("There are no more loopback devices available.") 47 } 48 return nil, ErrAttachLoopbackDevice 49 } 50 51 if fi.Mode()&os.ModeDevice != os.ModeDevice { 52 logrus.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, 0644) 58 if err != nil { 59 logrus.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 logrus.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 logrus.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 func AttachLoopDevice(sparseName string) (loop *os.File, err error) { 92 // Try to retrieve the next available loopback device via syscall. 93 // If it fails, we discard error and start looping for a 94 // loopback from index 0. 95 startIndex, err := getNextFreeLoopbackIndex() 96 if err != nil { 97 logrus.Debugf("Error retrieving the next available loopback: %s", err) 98 } 99 100 // OpenFile adds O_CLOEXEC 101 sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0644) 102 if err != nil { 103 logrus.Errorf("Error opening sparse file %s: %s", sparseName, err) 104 return nil, ErrAttachLoopbackDevice 105 } 106 defer sparseFile.Close() 107 108 loopFile, err := openNextAvailableLoopback(startIndex, sparseFile) 109 if err != nil { 110 return nil, err 111 } 112 113 // Set the status of the loopback device 114 loopInfo := &unix.LoopInfo64{ 115 File_name: stringToLoopName(loopFile.Name()), 116 Offset: 0, 117 Flags: unix.LO_FLAGS_AUTOCLEAR, 118 } 119 120 if err = unix.IoctlLoopSetStatus64(int(loopFile.Fd()), loopInfo); err != nil { 121 logrus.Errorf("Cannot set up loopback device info: %s", err) 122 123 // If the call failed, then free the loopback device 124 if err = unix.IoctlSetInt(int(loopFile.Fd()), unix.LOOP_CLR_FD, 0); err != nil { 125 logrus.Error("Error while cleaning up the loopback device") 126 } 127 loopFile.Close() 128 return nil, ErrAttachLoopbackDevice 129 } 130 131 return loopFile, nil 132 }