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