Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <fcntl.h>
5 : : #include <linux/loop.h>
6 : : #include <sys/ioctl.h>
7 : : #include <sys/stat.h>
8 : :
9 : : #include "alloc-util.h"
10 : : #include "fd-util.h"
11 : : #include "loop-util.h"
12 : : #include "stat-util.h"
13 : :
14 : 0 : int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
15 : 0 : const struct loop_info64 info = {
16 [ # # ]: 0 : .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0),
17 : : };
18 : :
19 : 0 : _cleanup_close_ int control = -1, loop = -1;
20 : 0 : _cleanup_free_ char *loopdev = NULL;
21 : 0 : unsigned n_attempts = 0;
22 : : struct stat st;
23 : : LoopDevice *d;
24 : : int nr, r;
25 : :
26 [ # # ]: 0 : assert(fd >= 0);
27 [ # # ]: 0 : assert(ret);
28 [ # # # # ]: 0 : assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
29 : :
30 [ # # ]: 0 : if (fstat(fd, &st) < 0)
31 : 0 : return -errno;
32 : :
33 [ # # ]: 0 : if (S_ISBLK(st.st_mode)) {
34 : : int copy;
35 : :
36 : : /* If this is already a block device, store a copy of the fd as it is */
37 : :
38 : 0 : copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
39 [ # # ]: 0 : if (copy < 0)
40 : 0 : return -errno;
41 : :
42 : 0 : d = new0(LoopDevice, 1);
43 [ # # ]: 0 : if (!d)
44 : 0 : return -ENOMEM;
45 : :
46 : 0 : *d = (LoopDevice) {
47 : : .fd = copy,
48 : : .nr = -1,
49 : : .relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */
50 : : };
51 : :
52 : 0 : *ret = d;
53 : 0 : return d->fd;
54 : : }
55 : :
56 : 0 : r = stat_verify_regular(&st);
57 [ # # ]: 0 : if (r < 0)
58 : 0 : return r;
59 : :
60 : 0 : control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
61 [ # # ]: 0 : if (control < 0)
62 : 0 : return -errno;
63 : :
64 : : /* Loop around LOOP_CTL_GET_FREE, since at the moment we attempt to open the returned device it might
65 : : * be gone already, taken by somebody else racing against us. */
66 : : for (;;) {
67 : 0 : nr = ioctl(control, LOOP_CTL_GET_FREE);
68 [ # # ]: 0 : if (nr < 0)
69 : 0 : return -errno;
70 : :
71 [ # # ]: 0 : if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
72 : 0 : return -ENOMEM;
73 : :
74 : 0 : loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
75 [ # # ]: 0 : if (loop < 0)
76 : 0 : return -errno;
77 [ # # ]: 0 : if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
78 [ # # ]: 0 : if (errno != EBUSY)
79 : 0 : return -errno;
80 : :
81 [ # # ]: 0 : if (++n_attempts >= 64) /* Give up eventually */
82 : 0 : return -EBUSY;
83 : : } else
84 : 0 : break;
85 : :
86 : 0 : loopdev = mfree(loopdev);
87 : 0 : loop = safe_close(loop);
88 : : }
89 : :
90 [ # # ]: 0 : if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
91 : 0 : return -errno;
92 : :
93 : 0 : d = new(LoopDevice, 1);
94 [ # # ]: 0 : if (!d)
95 : 0 : return -ENOMEM;
96 : :
97 : 0 : *d = (LoopDevice) {
98 : 0 : .fd = TAKE_FD(loop),
99 : 0 : .node = TAKE_PTR(loopdev),
100 : : .nr = nr,
101 : : };
102 : :
103 : 0 : *ret = d;
104 : 0 : return d->fd;
105 : : }
106 : :
107 : 0 : int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) {
108 : 0 : _cleanup_close_ int fd = -1;
109 : :
110 [ # # ]: 0 : assert(path);
111 [ # # ]: 0 : assert(ret);
112 [ # # # # ]: 0 : assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
113 : :
114 : 0 : fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
115 [ # # ]: 0 : if (fd < 0)
116 : 0 : return -errno;
117 : :
118 : 0 : return loop_device_make(fd, open_flags, ret);
119 : : }
120 : :
121 : 0 : LoopDevice* loop_device_unref(LoopDevice *d) {
122 [ # # ]: 0 : if (!d)
123 : 0 : return NULL;
124 : :
125 [ # # ]: 0 : if (d->fd >= 0) {
126 : :
127 [ # # # # ]: 0 : if (d->nr >= 0 && !d->relinquished) {
128 [ # # ]: 0 : if (ioctl(d->fd, LOOP_CLR_FD) < 0)
129 [ # # ]: 0 : log_debug_errno(errno, "Failed to clear loop device: %m");
130 : :
131 : : }
132 : :
133 : 0 : safe_close(d->fd);
134 : : }
135 : :
136 [ # # # # ]: 0 : if (d->nr >= 0 && !d->relinquished) {
137 : 0 : _cleanup_close_ int control = -1;
138 : :
139 : 0 : control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
140 [ # # ]: 0 : if (control < 0)
141 [ # # ]: 0 : log_debug_errno(errno, "Failed to open loop control device: %m");
142 : : else {
143 [ # # ]: 0 : if (ioctl(control, LOOP_CTL_REMOVE, d->nr) < 0)
144 [ # # ]: 0 : log_debug_errno(errno, "Failed to remove loop device: %m");
145 : : }
146 : : }
147 : :
148 : 0 : free(d->node);
149 : 0 : return mfree(d);
150 : : }
151 : :
152 : 0 : void loop_device_relinquish(LoopDevice *d) {
153 [ # # ]: 0 : assert(d);
154 : :
155 : : /* Don't attempt to clean up the loop device anymore from this point on. Leave the clean-ing up to the kernel
156 : : * itself, using the loop device "auto-clear" logic we already turned on when creating the device. */
157 : :
158 : 0 : d->relinquished = true;
159 : 0 : }
|