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>
... | ... |
@@ -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 |
+} |