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 : }
|