/src/cryptsetup/lib/utils_loop.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * loopback block device utilities |
4 | | * |
5 | | * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. |
6 | | * Copyright (C) 2009-2025 Milan Broz |
7 | | */ |
8 | | |
9 | | #include <stdlib.h> |
10 | | #include <string.h> |
11 | | #include <stdio.h> |
12 | | #include <unistd.h> |
13 | | #include <fcntl.h> |
14 | | #include <errno.h> |
15 | | #include <limits.h> |
16 | | #include <sys/ioctl.h> |
17 | | #include <sys/stat.h> |
18 | | #if HAVE_SYS_SYSMACROS_H |
19 | | # include <sys/sysmacros.h> /* for major, minor */ |
20 | | #endif |
21 | | #include <linux/types.h> |
22 | | #include <linux/loop.h> |
23 | | |
24 | | #include "utils_loop.h" |
25 | | #include "libcryptsetup_macros.h" |
26 | | |
27 | 0 | #define LOOP_DEV_MAJOR 7 |
28 | | |
29 | | #ifndef LO_FLAGS_AUTOCLEAR |
30 | 0 | #define LO_FLAGS_AUTOCLEAR 4 |
31 | | #endif |
32 | | |
33 | | #ifndef LOOP_CTL_GET_FREE |
34 | | #define LOOP_CTL_GET_FREE 0x4C82 |
35 | | #endif |
36 | | |
37 | | #ifndef LOOP_SET_CAPACITY |
38 | | #define LOOP_SET_CAPACITY 0x4C07 |
39 | | #endif |
40 | | |
41 | | #ifndef LOOP_SET_BLOCK_SIZE |
42 | | #define LOOP_SET_BLOCK_SIZE 0x4C09 |
43 | | #endif |
44 | | |
45 | | #ifndef LOOP_CONFIGURE |
46 | 0 | #define LOOP_CONFIGURE 0x4C0A |
47 | | struct loop_config { |
48 | | __u32 fd; |
49 | | __u32 block_size; |
50 | | struct loop_info64 info; |
51 | | __u64 __reserved[8]; |
52 | | }; |
53 | | #endif |
54 | | |
55 | | static char *crypt_loop_get_device_old(void) |
56 | 0 | { |
57 | 0 | char dev[64]; |
58 | 0 | int i, loop_fd; |
59 | 0 | struct loop_info64 lo64 = {0}; |
60 | |
|
61 | 0 | for (i = 0; i < 256; i++) { |
62 | 0 | sprintf(dev, "/dev/loop%d", i); |
63 | |
|
64 | 0 | loop_fd = open(dev, O_RDONLY); |
65 | 0 | if (loop_fd < 0) |
66 | 0 | return NULL; |
67 | | |
68 | 0 | if (ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) && |
69 | 0 | errno == ENXIO) { |
70 | 0 | close(loop_fd); |
71 | 0 | return strdup(dev); |
72 | 0 | } |
73 | 0 | close(loop_fd); |
74 | 0 | } |
75 | | |
76 | 0 | return NULL; |
77 | 0 | } |
78 | | |
79 | | static char *crypt_loop_get_device(void) |
80 | 0 | { |
81 | 0 | char dev[64]; |
82 | 0 | int i, loop_fd; |
83 | 0 | struct stat st; |
84 | |
|
85 | 0 | loop_fd = open("/dev/loop-control", O_RDONLY); |
86 | 0 | if (loop_fd < 0) |
87 | 0 | return crypt_loop_get_device_old(); |
88 | | |
89 | 0 | i = ioctl(loop_fd, LOOP_CTL_GET_FREE); |
90 | 0 | if (i < 0) { |
91 | 0 | close(loop_fd); |
92 | 0 | return NULL; |
93 | 0 | } |
94 | 0 | close(loop_fd); |
95 | |
|
96 | 0 | if (sprintf(dev, "/dev/loop%d", i) < 0) |
97 | 0 | return NULL; |
98 | | |
99 | 0 | if (stat(dev, &st) || !S_ISBLK(st.st_mode)) |
100 | 0 | return NULL; |
101 | | |
102 | 0 | return strdup(dev); |
103 | 0 | } |
104 | | |
105 | | int crypt_loop_attach(char **loop, const char *file, int offset, |
106 | | int autoclear, int *readonly, size_t blocksize) |
107 | 0 | { |
108 | 0 | struct loop_config config = {0}; |
109 | 0 | char *lo_file_name; |
110 | 0 | int loop_fd = -1, file_fd = -1, r = 1; |
111 | 0 | int fallback = 0; |
112 | |
|
113 | 0 | *loop = NULL; |
114 | |
|
115 | 0 | file_fd = open(file, *readonly ? O_RDONLY : O_RDWR); |
116 | 0 | if (file_fd < 0 && (errno == EROFS || errno == EACCES) && !*readonly) { |
117 | 0 | *readonly = 1; |
118 | 0 | file_fd = open(file, O_RDONLY); |
119 | 0 | } |
120 | 0 | if (file_fd < 0) |
121 | 0 | goto out; |
122 | | |
123 | 0 | config.fd = file_fd; |
124 | |
|
125 | 0 | lo_file_name = (char*)config.info.lo_file_name; |
126 | 0 | lo_file_name[LO_NAME_SIZE-1] = '\0'; |
127 | 0 | strncpy(lo_file_name, file, LO_NAME_SIZE-1); |
128 | 0 | config.info.lo_offset = offset; |
129 | 0 | if (autoclear) |
130 | 0 | config.info.lo_flags |= LO_FLAGS_AUTOCLEAR; |
131 | 0 | if (blocksize > SECTOR_SIZE) |
132 | 0 | config.block_size = blocksize; |
133 | |
|
134 | 0 | while (loop_fd < 0) { |
135 | 0 | *loop = crypt_loop_get_device(); |
136 | 0 | if (!*loop) |
137 | 0 | goto out; |
138 | | |
139 | 0 | loop_fd = open(*loop, *readonly ? O_RDONLY : O_RDWR); |
140 | 0 | if (loop_fd < 0) |
141 | 0 | goto out; |
142 | 0 | if (ioctl(loop_fd, LOOP_CONFIGURE, &config) < 0) { |
143 | 0 | if (errno == EINVAL || errno == ENOTTY) { |
144 | 0 | free(*loop); |
145 | 0 | *loop = NULL; |
146 | |
|
147 | 0 | close(loop_fd); |
148 | 0 | loop_fd = -1; |
149 | | |
150 | | /* kernel doesn't support LOOP_CONFIGURE */ |
151 | 0 | fallback = 1; |
152 | 0 | break; |
153 | 0 | } |
154 | 0 | if (errno != EBUSY) |
155 | 0 | goto out; |
156 | 0 | free(*loop); |
157 | 0 | *loop = NULL; |
158 | |
|
159 | 0 | close(loop_fd); |
160 | 0 | loop_fd = -1; |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | 0 | if (fallback) { |
165 | 0 | while (loop_fd < 0) { |
166 | 0 | *loop = crypt_loop_get_device(); |
167 | 0 | if (!*loop) |
168 | 0 | goto out; |
169 | | |
170 | 0 | loop_fd = open(*loop, *readonly ? O_RDONLY : O_RDWR); |
171 | 0 | if (loop_fd < 0) |
172 | 0 | goto out; |
173 | 0 | if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) { |
174 | 0 | if (errno != EBUSY) |
175 | 0 | goto out; |
176 | 0 | free(*loop); |
177 | 0 | *loop = NULL; |
178 | |
|
179 | 0 | close(loop_fd); |
180 | 0 | loop_fd = -1; |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | 0 | if (blocksize > SECTOR_SIZE) |
185 | 0 | (void)ioctl(loop_fd, LOOP_SET_BLOCK_SIZE, (unsigned long)blocksize); |
186 | |
|
187 | 0 | if (ioctl(loop_fd, LOOP_SET_STATUS64, &config.info) < 0) { |
188 | 0 | (void)ioctl(loop_fd, LOOP_CLR_FD, 0); |
189 | 0 | goto out; |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | /* Verify that autoclear is really set */ |
194 | 0 | if (autoclear) { |
195 | 0 | memset(&config.info, 0, sizeof(config.info)); |
196 | 0 | if (ioctl(loop_fd, LOOP_GET_STATUS64, &config.info) < 0 || |
197 | 0 | !(config.info.lo_flags & LO_FLAGS_AUTOCLEAR)) { |
198 | 0 | (void)ioctl(loop_fd, LOOP_CLR_FD, 0); |
199 | 0 | goto out; |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | 0 | r = 0; |
204 | 0 | out: |
205 | 0 | if (r && loop_fd >= 0) |
206 | 0 | close(loop_fd); |
207 | 0 | if (file_fd >= 0) |
208 | 0 | close(file_fd); |
209 | 0 | if (r && *loop) { |
210 | 0 | free(*loop); |
211 | 0 | *loop = NULL; |
212 | 0 | } |
213 | 0 | return r ? -1 : loop_fd; |
214 | 0 | } |
215 | | |
216 | | int crypt_loop_detach(const char *loop) |
217 | 0 | { |
218 | 0 | int loop_fd = -1, r = 1; |
219 | |
|
220 | 0 | loop_fd = open(loop, O_RDONLY); |
221 | 0 | if (loop_fd < 0) |
222 | 0 | return 1; |
223 | | |
224 | 0 | if (!ioctl(loop_fd, LOOP_CLR_FD, 0)) |
225 | 0 | r = 0; |
226 | |
|
227 | 0 | close(loop_fd); |
228 | 0 | return r; |
229 | 0 | } |
230 | | |
231 | | int crypt_loop_resize(const char *loop) |
232 | 0 | { |
233 | 0 | int loop_fd = -1, r = 1; |
234 | |
|
235 | 0 | loop_fd = open(loop, O_RDONLY); |
236 | 0 | if (loop_fd < 0) |
237 | 0 | return 1; |
238 | | |
239 | 0 | if (!ioctl(loop_fd, LOOP_SET_CAPACITY, 0)) |
240 | 0 | r = 0; |
241 | |
|
242 | 0 | close(loop_fd); |
243 | 0 | return r; |
244 | 0 | } |
245 | | |
246 | | static char *_ioctl_backing_file(const char *loop) |
247 | 0 | { |
248 | 0 | struct loop_info64 lo64 = {0}; |
249 | 0 | int loop_fd; |
250 | |
|
251 | 0 | loop_fd = open(loop, O_RDONLY); |
252 | 0 | if (loop_fd < 0) |
253 | 0 | return NULL; |
254 | | |
255 | 0 | if (ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) < 0) { |
256 | 0 | close(loop_fd); |
257 | 0 | return NULL; |
258 | 0 | } |
259 | | |
260 | 0 | lo64.lo_file_name[LO_NAME_SIZE-2] = '*'; |
261 | 0 | lo64.lo_file_name[LO_NAME_SIZE-1] = 0; |
262 | |
|
263 | 0 | close(loop_fd); |
264 | |
|
265 | 0 | return strdup((char*)lo64.lo_file_name); |
266 | 0 | } |
267 | | |
268 | | static char *_sysfs_backing_file(const char *loop) |
269 | 0 | { |
270 | 0 | struct stat st; |
271 | 0 | char buf[PATH_MAX]; |
272 | 0 | ssize_t len; |
273 | 0 | int fd; |
274 | |
|
275 | 0 | if (stat(loop, &st) || !S_ISBLK(st.st_mode)) |
276 | 0 | return NULL; |
277 | | |
278 | 0 | if (snprintf(buf, sizeof(buf), "/sys/dev/block/%d:%d/loop/backing_file", |
279 | 0 | major(st.st_rdev), minor(st.st_rdev)) < 0) |
280 | 0 | return NULL; |
281 | | |
282 | 0 | fd = open(buf, O_RDONLY); |
283 | 0 | if (fd < 0) |
284 | 0 | return NULL; |
285 | | |
286 | 0 | len = read(fd, buf, PATH_MAX); |
287 | 0 | close(fd); |
288 | 0 | if (len < 2) |
289 | 0 | return NULL; |
290 | | |
291 | 0 | buf[len - 1] = '\0'; |
292 | 0 | return strdup(buf); |
293 | 0 | } |
294 | | |
295 | | char *crypt_loop_backing_file(const char *loop) |
296 | 0 | { |
297 | 0 | char *bf; |
298 | |
|
299 | 0 | if (!crypt_loop_device(loop)) |
300 | 0 | return NULL; |
301 | | |
302 | 0 | bf = _sysfs_backing_file(loop); |
303 | 0 | return bf ?: _ioctl_backing_file(loop); |
304 | 0 | } |
305 | | |
306 | | int crypt_loop_device(const char *loop) |
307 | 0 | { |
308 | 0 | struct stat st; |
309 | |
|
310 | 0 | if (!loop) |
311 | 0 | return 0; |
312 | | |
313 | 0 | if (stat(loop, &st) || !S_ISBLK(st.st_mode) || |
314 | 0 | major(st.st_rdev) != LOOP_DEV_MAJOR) |
315 | 0 | return 0; |
316 | | |
317 | 0 | return 1; |
318 | 0 | } |