Browse code

loopback: separate loop logic from devicemapper

The loopback logic is not technically exclusive to the devicemapper
driver. This reorganizes the code such that the loopback code is usable
outside of the devicemapper package and driver.

Signed-off-by: Vincent Batts <vbatts@redhat.com>

Vincent Batts authored on 2015/12/15 07:16:34
Showing 9 changed files
... ...
@@ -24,6 +24,7 @@ import (
24 24
 	"github.com/docker/docker/daemon/graphdriver"
25 25
 	"github.com/docker/docker/pkg/devicemapper"
26 26
 	"github.com/docker/docker/pkg/idtools"
27
+	"github.com/docker/docker/pkg/loopback"
27 28
 	"github.com/docker/docker/pkg/mount"
28 29
 	"github.com/docker/docker/pkg/parsers"
29 30
 	"github.com/docker/go-units"
... ...
@@ -1170,7 +1171,7 @@ func (devices *DeviceSet) ResizePool(size int64) error {
1170 1170
 		return fmt.Errorf("devmapper: Can't shrink file")
1171 1171
 	}
1172 1172
 
1173
-	dataloopback := devicemapper.FindLoopDeviceFor(datafile)
1173
+	dataloopback := loopback.FindLoopDeviceFor(datafile)
1174 1174
 	if dataloopback == nil {
1175 1175
 		return fmt.Errorf("devmapper: Unable to find loopback mount for: %s", datafilename)
1176 1176
 	}
... ...
@@ -1182,7 +1183,7 @@ func (devices *DeviceSet) ResizePool(size int64) error {
1182 1182
 	}
1183 1183
 	defer metadatafile.Close()
1184 1184
 
1185
-	metadataloopback := devicemapper.FindLoopDeviceFor(metadatafile)
1185
+	metadataloopback := loopback.FindLoopDeviceFor(metadatafile)
1186 1186
 	if metadataloopback == nil {
1187 1187
 		return fmt.Errorf("devmapper: Unable to find loopback mount for: %s", metadatafilename)
1188 1188
 	}
... ...
@@ -1194,8 +1195,8 @@ func (devices *DeviceSet) ResizePool(size int64) error {
1194 1194
 	}
1195 1195
 
1196 1196
 	// Reload size for loopback device
1197
-	if err := devicemapper.LoopbackSetCapacity(dataloopback); err != nil {
1198
-		return fmt.Errorf("devmapper: Unable to update loopback capacity: %s", err)
1197
+	if err := loopback.SetCapacity(dataloopback); err != nil {
1198
+		return fmt.Errorf("Unable to update loopback capacity: %s", err)
1199 1199
 	}
1200 1200
 
1201 1201
 	// Suspend the pool
... ...
@@ -1414,7 +1415,7 @@ func getLoopFileDeviceMajMin(filename string) (string, uint64, uint64, error) {
1414 1414
 	}
1415 1415
 
1416 1416
 	defer file.Close()
1417
-	loopbackDevice := devicemapper.FindLoopDeviceFor(file)
1417
+	loopbackDevice := loopback.FindLoopDeviceFor(file)
1418 1418
 	if loopbackDevice == nil {
1419 1419
 		return "", 0, 0, fmt.Errorf("devmapper: Unable to find loopback mount for: %s", filename)
1420 1420
 	}
... ...
@@ -1622,7 +1623,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
1622 1622
 				return err
1623 1623
 			}
1624 1624
 
1625
-			dataFile, err = devicemapper.AttachLoopDevice(data)
1625
+			dataFile, err = loopback.AttachLoopDevice(data)
1626 1626
 			if err != nil {
1627 1627
 				return err
1628 1628
 			}
... ...
@@ -1655,7 +1656,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
1655 1655
 				return err
1656 1656
 			}
1657 1657
 
1658
-			metadataFile, err = devicemapper.AttachLoopDevice(metadata)
1658
+			metadataFile, err = loopback.AttachLoopDevice(metadata)
1659 1659
 			if err != nil {
1660 1660
 				return err
1661 1661
 			}
1662 1662
deleted file mode 100644
... ...
@@ -1,129 +0,0 @@
1
-// +build linux
2
-
3
-package devicemapper
4
-
5
-import (
6
-	"fmt"
7
-	"os"
8
-	"syscall"
9
-
10
-	"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
-				logrus.Errorf("There are no more loopback devices available.")
43
-			}
44
-			return nil, ErrAttachLoopbackDevice
45
-		}
46
-
47
-		if fi.Mode()&os.ModeDevice != os.ModeDevice {
48
-			logrus.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
-			logrus.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
-				logrus.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
-		logrus.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 looping for a
91
-	// loopback from index 0.
92
-	startIndex, err := getNextFreeLoopbackIndex()
93
-	if err != nil {
94
-		logrus.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
-		logrus.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
-		logrus.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
-			logrus.Errorf("Error while cleaning up the loopback device")
123
-		}
124
-		loopFile.Close()
125
-		return nil, ErrAttachLoopbackDevice
126
-	}
127
-
128
-	return loopFile, nil
129
-}
... ...
@@ -47,32 +47,29 @@ const (
47 47
 
48 48
 // List of errors returned when using devicemapper.
49 49
 var (
50
-	ErrTaskRun                = errors.New("dm_task_run failed")
51
-	ErrTaskSetName            = errors.New("dm_task_set_name failed")
52
-	ErrTaskSetMessage         = errors.New("dm_task_set_message failed")
53
-	ErrTaskSetAddNode         = errors.New("dm_task_set_add_node failed")
54
-	ErrTaskSetRo              = errors.New("dm_task_set_ro failed")
55
-	ErrTaskAddTarget          = errors.New("dm_task_add_target failed")
56
-	ErrTaskSetSector          = errors.New("dm_task_set_sector failed")
57
-	ErrTaskGetDeps            = errors.New("dm_task_get_deps failed")
58
-	ErrTaskGetInfo            = errors.New("dm_task_get_info failed")
59
-	ErrTaskGetDriverVersion   = errors.New("dm_task_get_driver_version failed")
60
-	ErrTaskDeferredRemove     = errors.New("dm_task_deferred_remove failed")
61
-	ErrTaskSetCookie          = errors.New("dm_task_set_cookie failed")
62
-	ErrNilCookie              = errors.New("cookie ptr can't be nil")
63
-	ErrAttachLoopbackDevice   = errors.New("loopback mounting failed")
64
-	ErrGetBlockSize           = errors.New("Can't get block size")
65
-	ErrUdevWait               = errors.New("wait on udev cookie failed")
66
-	ErrSetDevDir              = errors.New("dm_set_dev_dir failed")
67
-	ErrGetLibraryVersion      = errors.New("dm_get_library_version failed")
68
-	ErrCreateRemoveTask       = errors.New("Can't create task of type deviceRemove")
69
-	ErrRunRemoveDevice        = errors.New("running RemoveDevice failed")
70
-	ErrInvalidAddNode         = errors.New("Invalid AddNode type")
71
-	ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
72
-	ErrLoopbackSetCapacity    = errors.New("Unable set loopback capacity")
73
-	ErrBusy                   = errors.New("Device is Busy")
74
-	ErrDeviceIDExists         = errors.New("Device Id Exists")
75
-	ErrEnxio                  = errors.New("No such device or address")
50
+	ErrTaskRun              = errors.New("dm_task_run failed")
51
+	ErrTaskSetName          = errors.New("dm_task_set_name failed")
52
+	ErrTaskSetMessage       = errors.New("dm_task_set_message failed")
53
+	ErrTaskSetAddNode       = errors.New("dm_task_set_add_node failed")
54
+	ErrTaskSetRo            = errors.New("dm_task_set_ro failed")
55
+	ErrTaskAddTarget        = errors.New("dm_task_add_target failed")
56
+	ErrTaskSetSector        = errors.New("dm_task_set_sector failed")
57
+	ErrTaskGetDeps          = errors.New("dm_task_get_deps failed")
58
+	ErrTaskGetInfo          = errors.New("dm_task_get_info failed")
59
+	ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
60
+	ErrTaskDeferredRemove   = errors.New("dm_task_deferred_remove failed")
61
+	ErrTaskSetCookie        = errors.New("dm_task_set_cookie failed")
62
+	ErrNilCookie            = errors.New("cookie ptr can't be nil")
63
+	ErrGetBlockSize         = errors.New("Can't get block size")
64
+	ErrUdevWait             = errors.New("wait on udev cookie failed")
65
+	ErrSetDevDir            = errors.New("dm_set_dev_dir failed")
66
+	ErrGetLibraryVersion    = errors.New("dm_get_library_version failed")
67
+	ErrCreateRemoveTask     = errors.New("Can't create task of type deviceRemove")
68
+	ErrRunRemoveDevice      = errors.New("running RemoveDevice failed")
69
+	ErrInvalidAddNode       = errors.New("Invalid AddNode type")
70
+	ErrBusy                 = errors.New("Device is Busy")
71
+	ErrDeviceIDExists       = errors.New("Device Id Exists")
72
+	ErrEnxio                = errors.New("No such device or address")
76 73
 )
77 74
 
78 75
 var (
... ...
@@ -257,58 +254,6 @@ func (t *Task) getNextTarget(next unsafe.Pointer) (nextPtr unsafe.Pointer, start
257 257
 		start, length, targetType, params
258 258
 }
259 259
 
260
-func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
261
-	loopInfo, err := ioctlLoopGetStatus64(file.Fd())
262
-	if err != nil {
263
-		logrus.Errorf("devicemapper: Error get loopback backing file: %s", err)
264
-		return 0, 0, ErrGetLoopbackBackingFile
265
-	}
266
-	return loopInfo.loDevice, loopInfo.loInode, nil
267
-}
268
-
269
-// LoopbackSetCapacity reloads the size for the loopback device.
270
-func LoopbackSetCapacity(file *os.File) error {
271
-	if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
272
-		logrus.Errorf("devicemapper: Error loopbackSetCapacity: %s", err)
273
-		return ErrLoopbackSetCapacity
274
-	}
275
-	return nil
276
-}
277
-
278
-// FindLoopDeviceFor returns a loopback device file for the specified file which
279
-// is backing file of a loop back device.
280
-func FindLoopDeviceFor(file *os.File) *os.File {
281
-	stat, err := file.Stat()
282
-	if err != nil {
283
-		return nil
284
-	}
285
-	targetInode := stat.Sys().(*syscall.Stat_t).Ino
286
-	targetDevice := stat.Sys().(*syscall.Stat_t).Dev
287
-
288
-	for i := 0; true; i++ {
289
-		path := fmt.Sprintf("/dev/loop%d", i)
290
-
291
-		file, err := os.OpenFile(path, os.O_RDWR, 0)
292
-		if err != nil {
293
-			if os.IsNotExist(err) {
294
-				return nil
295
-			}
296
-
297
-			// Ignore all errors until the first not-exist
298
-			// we want to continue looking for the file
299
-			continue
300
-		}
301
-
302
-		dev, inode, err := getLoopbackBackingFile(file)
303
-		if err == nil && dev == targetDevice && inode == targetInode {
304
-			return file
305
-		}
306
-		file.Close()
307
-	}
308
-
309
-	return nil
310
-}
311
-
312 260
 // UdevWait waits for any processes that are waiting for udev to complete the specified cookie.
313 261
 func UdevWait(cookie *uint) error {
314 262
 	if res := DmUdevWait(*cookie); res != 1 {
... ...
@@ -5,17 +5,8 @@ package devicemapper
5 5
 /*
6 6
 #cgo LDFLAGS: -L. -ldevmapper
7 7
 #include <libdevmapper.h>
8
-#include <linux/loop.h> // FIXME: present only for defines, maybe we can remove it?
9 8
 #include <linux/fs.h>   // FIXME: present only for BLKGETSIZE64, maybe we can remove it?
10 9
 
11
-#ifndef LOOP_CTL_GET_FREE
12
-  #define LOOP_CTL_GET_FREE 0x4C82
13
-#endif
14
-
15
-#ifndef LO_FLAGS_PARTSCAN
16
-  #define LO_FLAGS_PARTSCAN 8
17
-#endif
18
-
19 10
 // FIXME: Can't we find a way to do the logging in pure Go?
20 11
 extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str);
21 12
 
... ...
@@ -45,44 +36,12 @@ import (
45 45
 
46 46
 type (
47 47
 	cdmTask C.struct_dm_task
48
-
49
-	loopInfo64 struct {
50
-		loDevice         uint64 /* ioctl r/o */
51
-		loInode          uint64 /* ioctl r/o */
52
-		loRdevice        uint64 /* ioctl r/o */
53
-		loOffset         uint64
54
-		loSizelimit      uint64 /* bytes, 0 == max available */
55
-		loNumber         uint32 /* ioctl r/o */
56
-		loEncryptType    uint32
57
-		loEncryptKeySize uint32 /* ioctl w/o */
58
-		loFlags          uint32 /* ioctl r/o */
59
-		loFileName       [LoNameSize]uint8
60
-		loCryptName      [LoNameSize]uint8
61
-		loEncryptKey     [LoKeySize]uint8 /* ioctl w/o */
62
-		loInit           [2]uint64
63
-	}
64 48
 )
65 49
 
66 50
 // IOCTL consts
67 51
 const (
68 52
 	BlkGetSize64 = C.BLKGETSIZE64
69 53
 	BlkDiscard   = C.BLKDISCARD
70
-
71
-	LoopSetFd       = C.LOOP_SET_FD
72
-	LoopCtlGetFree  = C.LOOP_CTL_GET_FREE
73
-	LoopGetStatus64 = C.LOOP_GET_STATUS64
74
-	LoopSetStatus64 = C.LOOP_SET_STATUS64
75
-	LoopClrFd       = C.LOOP_CLR_FD
76
-	LoopSetCapacity = C.LOOP_SET_CAPACITY
77
-)
78
-
79
-// LOOP consts.
80
-const (
81
-	LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR
82
-	LoFlagsReadOnly  = C.LO_FLAGS_READ_ONLY
83
-	LoFlagsPartScan  = C.LO_FLAGS_PARTSCAN
84
-	LoKeySize        = C.LO_KEY_SIZE
85
-	LoNameSize       = C.LO_NAME_SIZE
86 54
 )
87 55
 
88 56
 // Devicemapper cookie flags.
... ...
@@ -7,51 +7,6 @@ import (
7 7
 	"unsafe"
8 8
 )
9 9
 
10
-func ioctlLoopCtlGetFree(fd uintptr) (int, error) {
11
-	index, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, LoopCtlGetFree, 0)
12
-	if err != 0 {
13
-		return 0, err
14
-	}
15
-	return int(index), nil
16
-}
17
-
18
-func ioctlLoopSetFd(loopFd, sparseFd uintptr) error {
19
-	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetFd, sparseFd); err != 0 {
20
-		return err
21
-	}
22
-	return nil
23
-}
24
-
25
-func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *loopInfo64) error {
26
-	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
27
-		return err
28
-	}
29
-	return nil
30
-}
31
-
32
-func ioctlLoopClrFd(loopFd uintptr) error {
33
-	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopClrFd, 0); err != 0 {
34
-		return err
35
-	}
36
-	return nil
37
-}
38
-
39
-func ioctlLoopGetStatus64(loopFd uintptr) (*loopInfo64, error) {
40
-	loopInfo := &loopInfo64{}
41
-
42
-	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
43
-		return nil, err
44
-	}
45
-	return loopInfo, nil
46
-}
47
-
48
-func ioctlLoopSetCapacity(loopFd uintptr, value int) error {
49
-	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetCapacity, uintptr(value)); err != 0 {
50
-		return err
51
-	}
52
-	return nil
53
-}
54
-
55 10
 func ioctlBlkGetSize64(fd uintptr) (int64, error) {
56 11
 	var size int64
57 12
 	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 {
58 13
new file mode 100644
... ...
@@ -0,0 +1,137 @@
0
+// +build linux
1
+
2
+package loopback
3
+
4
+import (
5
+	"errors"
6
+	"fmt"
7
+	"os"
8
+	"syscall"
9
+
10
+	"github.com/Sirupsen/logrus"
11
+)
12
+
13
+// Loopback related errors
14
+var (
15
+	ErrAttachLoopbackDevice   = errors.New("loopback attach failed")
16
+	ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
17
+	ErrSetCapacity            = errors.New("Unable set loopback capacity")
18
+)
19
+
20
+func stringToLoopName(src string) [LoNameSize]uint8 {
21
+	var dst [LoNameSize]uint8
22
+	copy(dst[:], src[:])
23
+	return dst
24
+}
25
+
26
+func getNextFreeLoopbackIndex() (int, error) {
27
+	f, err := os.OpenFile("/dev/loop-control", os.O_RDONLY, 0644)
28
+	if err != nil {
29
+		return 0, err
30
+	}
31
+	defer f.Close()
32
+
33
+	index, err := ioctlLoopCtlGetFree(f.Fd())
34
+	if index < 0 {
35
+		index = 0
36
+	}
37
+	return index, err
38
+}
39
+
40
+func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.File, err error) {
41
+	// Start looking for a free /dev/loop
42
+	for {
43
+		target := fmt.Sprintf("/dev/loop%d", index)
44
+		index++
45
+
46
+		fi, err := os.Stat(target)
47
+		if err != nil {
48
+			if os.IsNotExist(err) {
49
+				logrus.Errorf("There are no more loopback devices available.")
50
+			}
51
+			return nil, ErrAttachLoopbackDevice
52
+		}
53
+
54
+		if fi.Mode()&os.ModeDevice != os.ModeDevice {
55
+			logrus.Errorf("Loopback device %s is not a block device.", target)
56
+			continue
57
+		}
58
+
59
+		// OpenFile adds O_CLOEXEC
60
+		loopFile, err = os.OpenFile(target, os.O_RDWR, 0644)
61
+		if err != nil {
62
+			logrus.Errorf("Error opening loopback device: %s", err)
63
+			return nil, ErrAttachLoopbackDevice
64
+		}
65
+
66
+		// Try to attach to the loop file
67
+		if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil {
68
+			loopFile.Close()
69
+
70
+			// If the error is EBUSY, then try the next loopback
71
+			if err != syscall.EBUSY {
72
+				logrus.Errorf("Cannot set up loopback device %s: %s", target, err)
73
+				return nil, ErrAttachLoopbackDevice
74
+			}
75
+
76
+			// Otherwise, we keep going with the loop
77
+			continue
78
+		}
79
+		// In case of success, we finished. Break the loop.
80
+		break
81
+	}
82
+
83
+	// This can't happen, but let's be sure
84
+	if loopFile == nil {
85
+		logrus.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
86
+		return nil, ErrAttachLoopbackDevice
87
+	}
88
+
89
+	return loopFile, nil
90
+}
91
+
92
+// AttachLoopDevice attaches the given sparse file to the next
93
+// available loopback device. It returns an opened *os.File.
94
+func AttachLoopDevice(sparseName string) (loop *os.File, err error) {
95
+
96
+	// Try to retrieve the next available loopback device via syscall.
97
+	// If it fails, we discard error and start looping for a
98
+	// loopback from index 0.
99
+	startIndex, err := getNextFreeLoopbackIndex()
100
+	if err != nil {
101
+		logrus.Debugf("Error retrieving the next available loopback: %s", err)
102
+	}
103
+
104
+	// OpenFile adds O_CLOEXEC
105
+	sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0644)
106
+	if err != nil {
107
+		logrus.Errorf("Error opening sparse file %s: %s", sparseName, err)
108
+		return nil, ErrAttachLoopbackDevice
109
+	}
110
+	defer sparseFile.Close()
111
+
112
+	loopFile, err := openNextAvailableLoopback(startIndex, sparseFile)
113
+	if err != nil {
114
+		return nil, err
115
+	}
116
+
117
+	// Set the status of the loopback device
118
+	loopInfo := &loopInfo64{
119
+		loFileName: stringToLoopName(loopFile.Name()),
120
+		loOffset:   0,
121
+		loFlags:    LoFlagsAutoClear,
122
+	}
123
+
124
+	if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil {
125
+		logrus.Errorf("Cannot set up loopback device info: %s", err)
126
+
127
+		// If the call failed, then free the loopback device
128
+		if err := ioctlLoopClrFd(loopFile.Fd()); err != nil {
129
+			logrus.Errorf("Error while cleaning up the loopback device")
130
+		}
131
+		loopFile.Close()
132
+		return nil, ErrAttachLoopbackDevice
133
+	}
134
+
135
+	return loopFile, nil
136
+}
0 137
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+// +build linux
1
+
2
+package loopback
3
+
4
+import (
5
+	"syscall"
6
+	"unsafe"
7
+)
8
+
9
+func ioctlLoopCtlGetFree(fd uintptr) (int, error) {
10
+	index, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, LoopCtlGetFree, 0)
11
+	if err != 0 {
12
+		return 0, err
13
+	}
14
+	return int(index), nil
15
+}
16
+
17
+func ioctlLoopSetFd(loopFd, sparseFd uintptr) error {
18
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetFd, sparseFd); err != 0 {
19
+		return err
20
+	}
21
+	return nil
22
+}
23
+
24
+func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *loopInfo64) error {
25
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
26
+		return err
27
+	}
28
+	return nil
29
+}
30
+
31
+func ioctlLoopClrFd(loopFd uintptr) error {
32
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopClrFd, 0); err != 0 {
33
+		return err
34
+	}
35
+	return nil
36
+}
37
+
38
+func ioctlLoopGetStatus64(loopFd uintptr) (*loopInfo64, error) {
39
+	loopInfo := &loopInfo64{}
40
+
41
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
42
+		return nil, err
43
+	}
44
+	return loopInfo, nil
45
+}
46
+
47
+func ioctlLoopSetCapacity(loopFd uintptr, value int) error {
48
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, loopFd, LoopSetCapacity, uintptr(value)); err != 0 {
49
+		return err
50
+	}
51
+	return nil
52
+}
0 53
new file mode 100644
... ...
@@ -0,0 +1,52 @@
0
+// +build linux
1
+
2
+package loopback
3
+
4
+/*
5
+#include <linux/loop.h> // FIXME: present only for defines, maybe we can remove it?
6
+
7
+#ifndef LOOP_CTL_GET_FREE
8
+  #define LOOP_CTL_GET_FREE 0x4C82
9
+#endif
10
+
11
+#ifndef LO_FLAGS_PARTSCAN
12
+  #define LO_FLAGS_PARTSCAN 8
13
+#endif
14
+
15
+*/
16
+import "C"
17
+
18
+type loopInfo64 struct {
19
+	loDevice         uint64 /* ioctl r/o */
20
+	loInode          uint64 /* ioctl r/o */
21
+	loRdevice        uint64 /* ioctl r/o */
22
+	loOffset         uint64
23
+	loSizelimit      uint64 /* bytes, 0 == max available */
24
+	loNumber         uint32 /* ioctl r/o */
25
+	loEncryptType    uint32
26
+	loEncryptKeySize uint32 /* ioctl w/o */
27
+	loFlags          uint32 /* ioctl r/o */
28
+	loFileName       [LoNameSize]uint8
29
+	loCryptName      [LoNameSize]uint8
30
+	loEncryptKey     [LoKeySize]uint8 /* ioctl w/o */
31
+	loInit           [2]uint64
32
+}
33
+
34
+// IOCTL consts
35
+const (
36
+	LoopSetFd       = C.LOOP_SET_FD
37
+	LoopCtlGetFree  = C.LOOP_CTL_GET_FREE
38
+	LoopGetStatus64 = C.LOOP_GET_STATUS64
39
+	LoopSetStatus64 = C.LOOP_SET_STATUS64
40
+	LoopClrFd       = C.LOOP_CLR_FD
41
+	LoopSetCapacity = C.LOOP_SET_CAPACITY
42
+)
43
+
44
+// LOOP consts.
45
+const (
46
+	LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR
47
+	LoFlagsReadOnly  = C.LO_FLAGS_READ_ONLY
48
+	LoFlagsPartScan  = C.LO_FLAGS_PARTSCAN
49
+	LoKeySize        = C.LO_KEY_SIZE
50
+	LoNameSize       = C.LO_NAME_SIZE
51
+)
0 52
new file mode 100644
... ...
@@ -0,0 +1,63 @@
0
+// +build linux
1
+
2
+package loopback
3
+
4
+import (
5
+	"fmt"
6
+	"os"
7
+	"syscall"
8
+
9
+	"github.com/Sirupsen/logrus"
10
+)
11
+
12
+func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
13
+	loopInfo, err := ioctlLoopGetStatus64(file.Fd())
14
+	if err != nil {
15
+		logrus.Errorf("Error get loopback backing file: %s", err)
16
+		return 0, 0, ErrGetLoopbackBackingFile
17
+	}
18
+	return loopInfo.loDevice, loopInfo.loInode, nil
19
+}
20
+
21
+// SetCapacity reloads the size for the loopback device.
22
+func SetCapacity(file *os.File) error {
23
+	if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
24
+		logrus.Errorf("Error loopbackSetCapacity: %s", err)
25
+		return ErrSetCapacity
26
+	}
27
+	return nil
28
+}
29
+
30
+// FindLoopDeviceFor returns a loopback device file for the specified file which
31
+// is backing file of a loop back device.
32
+func FindLoopDeviceFor(file *os.File) *os.File {
33
+	stat, err := file.Stat()
34
+	if err != nil {
35
+		return nil
36
+	}
37
+	targetInode := stat.Sys().(*syscall.Stat_t).Ino
38
+	targetDevice := stat.Sys().(*syscall.Stat_t).Dev
39
+
40
+	for i := 0; true; i++ {
41
+		path := fmt.Sprintf("/dev/loop%d", i)
42
+
43
+		file, err := os.OpenFile(path, os.O_RDWR, 0)
44
+		if err != nil {
45
+			if os.IsNotExist(err) {
46
+				return nil
47
+			}
48
+
49
+			// Ignore all errors until the first not-exist
50
+			// we want to continue looking for the file
51
+			continue
52
+		}
53
+
54
+		dev, inode, err := getLoopbackBackingFile(file)
55
+		if err == nil && dev == targetDevice && inode == targetInode {
56
+			return file
57
+		}
58
+		file.Close()
59
+	}
60
+
61
+	return nil
62
+}