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