]>
Commit | Line | Data |
---|---|---|
10ee5932 KZ |
1 | /* |
2 | * Copyright (C) 2011 Karel Zak <kzak@redhat.com> | |
3 | * | |
4 | * -- based on mount/losetup.c | |
5 | * | |
6 | * Simple library for work with loop devices. | |
7 | * | |
8 | * - requires kernel 2.6.x | |
9 | * - reads info from /sys/block/loop<N>/loop/<attr> (new kernels) | |
10 | * - reads info by ioctl | |
11 | * - supports *unlimited* number of loop devices | |
12 | * - supports /dev/loop<N> as well as /dev/loop/<N> | |
13 | * - minimize overhead (fd, loopinfo, ... are shared for all operations) | |
14 | * - setup (associate device and backing file) | |
15 | * - delete (dis-associate file) | |
16 | * - old LOOP_{SET,GET}_STATUS (32bit) ioctls are unsupported | |
17 | * - extendible | |
18 | */ | |
19 | #include <stdio.h> | |
20 | #include <stdint.h> | |
21 | #include <string.h> | |
22 | #include <ctype.h> | |
23 | #include <fcntl.h> | |
10ee5932 KZ |
24 | #include <stdlib.h> |
25 | #include <unistd.h> | |
26 | #include <sys/ioctl.h> | |
27 | #include <sys/stat.h> | |
28 | #include <sys/mman.h> | |
29 | #include <sys/sysmacros.h> | |
30 | #include <inttypes.h> | |
31 | #include <dirent.h> | |
32 | #include <linux/posix_types.h> | |
33 | ||
34 | #include "linux_version.h" | |
35 | #include "c.h" | |
36 | #include "sysfs.h" | |
37 | #include "pathnames.h" | |
38 | #include "loopdev.h" | |
39 | #include "canonicalize.h" | |
40 | ||
aee31ddc KZ |
41 | #define CONFIG_LOOPDEV_DEBUG |
42 | ||
43 | #ifdef CONFIG_LOOPDEV_DEBUG | |
aee31ddc KZ |
44 | # include <stdarg.h> |
45 | ||
46 | # define DBG(l,x) do { \ | |
47 | if ((l)->debug) {\ | |
48 | fprintf(stderr, "loopdev: [%p]: ", (l)); \ | |
49 | x; \ | |
50 | } \ | |
51 | } while(0) | |
52 | ||
53 | static inline void __attribute__ ((__format__ (__printf__, 1, 2))) | |
54 | loopdev_debug(const char *mesg, ...) | |
55 | { | |
56 | va_list ap; | |
57 | va_start(ap, mesg); | |
58 | vfprintf(stderr, mesg, ap); | |
59 | va_end(ap); | |
60 | fputc('\n', stderr); | |
61 | } | |
62 | ||
63 | #else /* !CONFIG_LOOPDEV_DEBUG */ | |
64 | # define DBG(m,x) do { ; } while(0) | |
65 | #endif | |
66 | ||
67 | ||
10ee5932 KZ |
68 | #define loopcxt_ioctl_enabled(_lc) (!((_lc)->flags & LOOPDEV_FL_NOIOCTL)) |
69 | ||
70 | /* | |
71 | * @lc: context | |
72 | * @device: device name, absolute device path or NULL to reset the current setting | |
73 | * | |
74 | * Sets device, absolute paths (e.g. "/dev/loop<N>") are unchanged, device | |
75 | * names ("loop<N>") are converted to the path (/dev/loop<N> or to | |
76 | * /dev/loop/<N>) | |
77 | * | |
78 | * Returns: <0 on error, 0 on success | |
79 | */ | |
80 | int loopcxt_set_device(struct loopdev_cxt *lc, const char *device) | |
81 | { | |
82 | if (!lc) | |
83 | return -EINVAL; | |
84 | ||
85 | if (lc->fd >= 0) | |
86 | close(lc->fd); | |
87 | lc->fd = -1; | |
fd7f0718 | 88 | lc->mode = 0; |
10ee5932 | 89 | lc->has_info = 0; |
6c224de1 | 90 | lc->info_failed = 0; |
10ee5932 | 91 | *lc->device = '\0'; |
fd7f0718 | 92 | memset(&lc->info, 0, sizeof(lc->info)); |
10ee5932 KZ |
93 | |
94 | /* set new */ | |
95 | if (device) { | |
96 | if (*device != '/') { | |
97 | const char *dir = _PATH_DEV; | |
98 | ||
99 | /* compose device name for /dev/loop<n> or /dev/loop/<n> */ | |
100 | if (lc->flags & LOOPDEV_FL_DEVSUBDIR) { | |
101 | if (strlen(device) < 5) | |
102 | return -1; | |
103 | device += 4; | |
104 | dir = _PATH_DEV_LOOP "/"; /* _PATH_DEV uses tailing slash */ | |
105 | } | |
106 | snprintf(lc->device, sizeof(lc->device), "%s%s", | |
107 | dir, device); | |
108 | } else { | |
109 | strncpy(lc->device, device, sizeof(lc->device)); | |
110 | lc->device[sizeof(lc->device) - 1] = '\0'; | |
111 | } | |
d356c5d2 | 112 | DBG(lc, loopdev_debug("%s successfully assigned", device)); |
10ee5932 KZ |
113 | } |
114 | ||
115 | sysfs_deinit(&lc->sysfs); | |
116 | return 0; | |
117 | } | |
118 | ||
c7e0925d KZ |
119 | int loopcxt_has_device(struct loopdev_cxt *lc) |
120 | { | |
121 | return lc && *lc->device; | |
122 | } | |
123 | ||
10ee5932 KZ |
124 | /* |
125 | * @lc: context | |
126 | * @flags: LOOPDEV_FL_* flags | |
127 | * | |
128 | * Initilize loop handler. | |
129 | * | |
fd7f0718 KZ |
130 | * We have two sets of the flags: |
131 | * | |
132 | * * LOOPDEV_FL_* flags control loopcxt_* API behavior | |
133 | * | |
134 | * * LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls | |
135 | * | |
136 | * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2) | |
137 | * syscall to open loop device. By default is the device open read-only. | |
138 | * | |
139 | * The expection is loopcxt_setup_device(), where the device is open read-write | |
140 | * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()). | |
141 | * | |
10ee5932 KZ |
142 | * Returns: <0 on error, 0 on success. |
143 | */ | |
144 | int loopcxt_init(struct loopdev_cxt *lc, int flags) | |
145 | { | |
0b14bf7a KZ |
146 | struct stat st; |
147 | ||
10ee5932 KZ |
148 | if (!lc) |
149 | return -EINVAL; | |
150 | ||
151 | memset(lc, 0, sizeof(*lc)); | |
152 | lc->flags = flags; | |
153 | loopcxt_set_device(lc, NULL); | |
154 | ||
82b4082e | 155 | if (!(lc->flags & LOOPDEV_FL_NOSYSFS) && |
10ee5932 KZ |
156 | get_linux_version() >= KERNEL_VERSION(2,6,37)) |
157 | /* | |
158 | * Use only sysfs for basic information about loop devices | |
159 | */ | |
160 | lc->flags |= LOOPDEV_FL_NOIOCTL; | |
161 | ||
0b14bf7a KZ |
162 | if (!(lc->flags & LOOPDEV_FL_CONTROL) && !stat(_PATH_DEV_LOOPCTL, &st)) |
163 | lc->flags |= LOOPDEV_FL_CONTROL; | |
164 | ||
10ee5932 KZ |
165 | return 0; |
166 | } | |
167 | ||
168 | /* | |
169 | * @lc: context | |
170 | * | |
171 | * Deinitialize loop context | |
172 | */ | |
173 | void loopcxt_deinit(struct loopdev_cxt *lc) | |
174 | { | |
175 | if (!lc) | |
176 | return; | |
177 | ||
aee31ddc KZ |
178 | DBG(lc, loopdev_debug("de-initialize")); |
179 | ||
10ee5932 KZ |
180 | free(lc->filename); |
181 | lc->filename = NULL; | |
182 | ||
183 | loopcxt_set_device(lc, NULL); | |
184 | loopcxt_deinit_iterator(lc); | |
185 | } | |
186 | ||
aee31ddc KZ |
187 | /* |
188 | * @lc: context | |
189 | * @enable: TRUE/FALSE | |
190 | * | |
191 | * Enabled/disables debug messages | |
192 | */ | |
193 | void loopcxt_enable_debug(struct loopdev_cxt *lc, int enable) | |
194 | { | |
195 | if (lc) | |
196 | lc->debug = enable ? 1 : 0; | |
197 | } | |
198 | ||
10ee5932 KZ |
199 | /* |
200 | * @lc: context | |
201 | * | |
202 | * Returns newly allocated device path. | |
203 | */ | |
204 | char *loopcxt_strdup_device(struct loopdev_cxt *lc) | |
205 | { | |
206 | if (!lc || !*lc->device) | |
207 | return NULL; | |
208 | return strdup(lc->device); | |
209 | } | |
210 | ||
211 | /* | |
212 | * @lc: context | |
213 | * | |
214 | * Returns pointer device name in the @lc struct. | |
215 | */ | |
216 | const char *loopcxt_get_device(struct loopdev_cxt *lc) | |
217 | { | |
218 | return lc ? lc->device : NULL; | |
219 | } | |
220 | ||
221 | /* | |
222 | * @lc: context | |
223 | * | |
224 | * Returns pointer to the sysfs context (see lib/sysfs.c) | |
225 | */ | |
226 | struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc) | |
227 | { | |
228 | if (!lc || !*lc->device || (lc->flags & LOOPDEV_FL_NOSYSFS)) | |
229 | return NULL; | |
230 | ||
231 | if (!lc->sysfs.devno) { | |
232 | dev_t devno = sysfs_devname_to_devno(lc->device, NULL); | |
aee31ddc KZ |
233 | if (!devno) { |
234 | DBG(lc, loopdev_debug("sysfs: failed devname to devno")); | |
10ee5932 | 235 | return NULL; |
aee31ddc KZ |
236 | } |
237 | if (sysfs_init(&lc->sysfs, devno, NULL)) { | |
238 | DBG(lc, loopdev_debug("sysfs: init failed")); | |
10ee5932 | 239 | return NULL; |
aee31ddc | 240 | } |
10ee5932 | 241 | } |
aee31ddc | 242 | |
10ee5932 KZ |
243 | return &lc->sysfs; |
244 | } | |
245 | ||
246 | /* | |
247 | * @lc: context | |
248 | * | |
249 | * Returns: file descriptor to the open loop device or <0 on error. The mode | |
250 | * depends on LOOPDEV_FL_{RDWR,RDONLY} context flags. Default is | |
251 | * read-only. | |
252 | */ | |
253 | int loopcxt_get_fd(struct loopdev_cxt *lc) | |
254 | { | |
255 | if (!lc || !*lc->device) | |
fd7f0718 | 256 | return -EINVAL; |
10ee5932 | 257 | |
fd7f0718 KZ |
258 | if (lc->fd < 0) { |
259 | lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY; | |
260 | lc->fd = open(lc->device, lc->mode); | |
aee31ddc | 261 | DBG(lc, loopdev_debug("open %s", lc->fd < 0 ? "failed" : "ok")); |
fd7f0718 | 262 | } |
10ee5932 KZ |
263 | return lc->fd; |
264 | } | |
265 | ||
fd7f0718 KZ |
266 | int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode) |
267 | { | |
268 | if (!lc) | |
269 | return -EINVAL; | |
270 | ||
271 | lc->fd = fd; | |
272 | lc->mode = mode; | |
273 | return 0; | |
274 | } | |
275 | ||
10ee5932 KZ |
276 | /* |
277 | * @lc: context | |
278 | * @flags: LOOPITER_FL_* flags | |
279 | * | |
280 | * Iterator allows to scan list of the free or used loop devices. | |
281 | * | |
282 | * Returns: <0 on error, 0 on success | |
283 | */ | |
284 | int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags) | |
285 | { | |
286 | struct loopdev_iter *iter; | |
287 | struct stat st; | |
288 | ||
289 | if (!lc) | |
290 | return -EINVAL; | |
291 | ||
aee31ddc KZ |
292 | DBG(lc, loopdev_debug("iter: initialize")); |
293 | ||
10ee5932 KZ |
294 | iter = &lc->iter; |
295 | ||
296 | /* always zeroize | |
297 | */ | |
298 | memset(iter, 0, sizeof(*iter)); | |
299 | iter->ncur = -1; | |
300 | iter->flags = flags; | |
301 | iter->default_check = 1; | |
302 | ||
303 | if (!lc->extra_check) { | |
304 | /* | |
305 | * Check for /dev/loop/<N> subdirectory | |
306 | */ | |
82b4082e | 307 | if (!(lc->flags & LOOPDEV_FL_DEVSUBDIR) && |
10ee5932 KZ |
308 | stat(_PATH_DEV_LOOP, &st) == 0 && S_ISDIR(st.st_mode)) |
309 | lc->flags |= LOOPDEV_FL_DEVSUBDIR; | |
310 | ||
311 | lc->extra_check = 1; | |
312 | } | |
313 | return 0; | |
314 | } | |
315 | ||
316 | /* | |
317 | * @lc: context | |
318 | * | |
319 | * Returns: <0 on error, 0 on success | |
320 | */ | |
321 | int loopcxt_deinit_iterator(struct loopdev_cxt *lc) | |
322 | { | |
323 | struct loopdev_iter *iter = &lc->iter; | |
324 | ||
325 | if (!lc) | |
326 | return -EINVAL; | |
327 | ||
aee31ddc KZ |
328 | DBG(lc, loopdev_debug("iter: de-initialize")); |
329 | ||
10ee5932 KZ |
330 | iter = &lc->iter; |
331 | ||
332 | free(iter->minors); | |
333 | if (iter->proc) | |
334 | fclose(iter->proc); | |
335 | iter->minors = NULL; | |
336 | iter->proc = NULL; | |
337 | iter->done = 1; | |
338 | return 0; | |
339 | } | |
340 | ||
341 | /* | |
342 | * Same as loopcxt_set_device, but also checks if the device is | |
343 | * associeted with any file. | |
344 | * | |
345 | * Returns: <0 on error, 0 on success, 1 device does not match with | |
346 | * LOOPITER_FL_{USED,FREE} flags. | |
347 | */ | |
348 | static int loopiter_set_device(struct loopdev_cxt *lc, const char *device) | |
349 | { | |
350 | int rc = loopcxt_set_device(lc, device); | |
351 | int used; | |
352 | ||
353 | if (rc) | |
354 | return rc; | |
355 | ||
356 | if (!(lc->iter.flags & LOOPITER_FL_USED) && | |
357 | !(lc->iter.flags & LOOPITER_FL_FREE)) | |
358 | return 0; /* caller does not care about device status */ | |
359 | ||
360 | used = loopcxt_get_offset(lc, NULL) == 0; | |
361 | ||
362 | if ((lc->iter.flags & LOOPITER_FL_USED) && used) | |
363 | return 0; | |
364 | ||
365 | if ((lc->iter.flags & LOOPITER_FL_FREE) && !used) | |
366 | return 0; | |
367 | ||
aee31ddc | 368 | DBG(lc, loopdev_debug("iter: setting device")); |
10ee5932 KZ |
369 | loopcxt_set_device(lc, NULL); |
370 | return 1; | |
371 | } | |
372 | ||
373 | static int cmpnum(const void *p1, const void *p2) | |
374 | { | |
33487796 KZ |
375 | return (((* (int *) p1) > (* (int *) p2)) - |
376 | ((* (int *) p1) < (* (int *) p2))); | |
10ee5932 KZ |
377 | } |
378 | ||
379 | /* | |
380 | * The classic scandir() is more expensive and less portable. | |
381 | * We needn't full loop device names -- loop numbers (loop<N>) | |
382 | * are enough. | |
383 | */ | |
384 | static int loop_scandir(const char *dirname, int **ary, int hasprefix) | |
385 | { | |
386 | DIR *dir; | |
387 | struct dirent *d; | |
388 | unsigned int n, count = 0, arylen = 0; | |
389 | ||
390 | if (!dirname || !ary) | |
391 | return 0; | |
392 | dir = opendir(dirname); | |
393 | if (!dir) | |
394 | return 0; | |
10ee5932 KZ |
395 | free(*ary); |
396 | *ary = NULL; | |
397 | ||
398 | while((d = readdir(dir))) { | |
399 | #ifdef _DIRENT_HAVE_D_TYPE | |
400 | if (d->d_type != DT_BLK && d->d_type != DT_UNKNOWN && | |
401 | d->d_type != DT_LNK) | |
402 | continue; | |
403 | #endif | |
404 | if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) | |
405 | continue; | |
406 | ||
407 | if (hasprefix) { | |
408 | /* /dev/loop<N> */ | |
409 | if (sscanf(d->d_name, "loop%u", &n) != 1) | |
410 | continue; | |
411 | } else { | |
412 | /* /dev/loop/<N> */ | |
413 | char *end = NULL; | |
414 | ||
415 | n = strtol(d->d_name, &end, 10); | |
416 | if (d->d_name == end || (end && *end) || errno) | |
417 | continue; | |
418 | } | |
419 | if (n < LOOPDEV_DEFAULT_NNODES) | |
420 | continue; /* ignore loop<0..7> */ | |
421 | ||
422 | if (count + 1 > arylen) { | |
423 | int *tmp; | |
424 | ||
425 | arylen += 1; | |
426 | ||
427 | tmp = realloc(*ary, arylen * sizeof(int)); | |
428 | if (!tmp) { | |
429 | free(*ary); | |
430 | return -1; | |
431 | } | |
432 | *ary = tmp; | |
433 | } | |
fc8b1f36 KZ |
434 | if (*ary) |
435 | (*ary)[count++] = n; | |
10ee5932 | 436 | } |
fc8b1f36 | 437 | if (count && *ary) |
10ee5932 KZ |
438 | qsort(*ary, count, sizeof(int), cmpnum); |
439 | ||
440 | closedir(dir); | |
441 | return count; | |
442 | } | |
443 | ||
444 | /* | |
445 | * @lc: context, has to initialized by loopcxt_init_iterator() | |
446 | * | |
447 | * Returns: 0 on success, -1 on error, 1 at the end of scanning. The details | |
448 | * about the current loop device are available by | |
449 | * loopcxt_get_{fd,backing_file,device,offset, ...} functions. | |
450 | */ | |
451 | int loopcxt_next(struct loopdev_cxt *lc) | |
452 | { | |
453 | struct loopdev_iter *iter; | |
454 | ||
455 | if (!lc) | |
456 | return -EINVAL; | |
aee31ddc KZ |
457 | |
458 | DBG(lc, loopdev_debug("iter: next")); | |
459 | ||
10ee5932 KZ |
460 | iter = &lc->iter; |
461 | if (iter->done) | |
462 | return 1; | |
463 | ||
464 | /* A) Look for used loop devices in /proc/partitions ("losetup -a" only) | |
465 | */ | |
466 | if (iter->flags & LOOPITER_FL_USED) { | |
467 | char buf[BUFSIZ]; | |
468 | ||
469 | if (!iter->proc) | |
470 | iter->proc = fopen(_PATH_PROC_PARTITIONS, "r"); | |
471 | ||
472 | while (iter->proc && fgets(buf, sizeof(buf), iter->proc)) { | |
473 | unsigned int m; | |
474 | char name[128]; | |
475 | ||
476 | if (sscanf(buf, " %u %*s %*s %128[^\n ]", | |
477 | &m, name) != 2 || m != LOOPDEV_MAJOR) | |
478 | continue; | |
479 | ||
480 | if (loopiter_set_device(lc, name) == 0) | |
481 | return 0; | |
482 | } | |
483 | ||
484 | goto done; | |
485 | } | |
486 | ||
487 | /* B) Classic way, try first eight loop devices (default number | |
488 | * of loop devices). This is enough for 99% of all cases. | |
489 | */ | |
490 | if (iter->default_check) { | |
491 | for (++iter->ncur; iter->ncur < LOOPDEV_DEFAULT_NNODES; | |
492 | iter->ncur++) { | |
493 | char name[16]; | |
494 | snprintf(name, sizeof(name), "loop%d", iter->ncur); | |
495 | ||
496 | if (loopiter_set_device(lc, name) == 0) | |
497 | return 0; | |
498 | } | |
499 | iter->default_check = 0; | |
500 | } | |
501 | ||
502 | /* C) the worst possibility, scan whole /dev or /dev/loop/<N> | |
503 | */ | |
504 | if (!iter->minors) { | |
505 | iter->nminors = (lc->flags & LOOPDEV_FL_DEVSUBDIR) ? | |
506 | loop_scandir(_PATH_DEV_LOOP, &iter->minors, 0) : | |
507 | loop_scandir(_PATH_DEV, &iter->minors, 1); | |
508 | iter->ncur = -1; | |
509 | } | |
510 | for (++iter->ncur; iter->ncur < iter->nminors; iter->ncur++) { | |
511 | char name[16]; | |
512 | snprintf(name, sizeof(name), "loop%d", iter->minors[iter->ncur]); | |
513 | ||
514 | if (loopiter_set_device(lc, name) == 0) | |
515 | return 0; | |
516 | } | |
517 | done: | |
518 | loopcxt_deinit_iterator(lc); | |
519 | return 1; | |
520 | } | |
521 | ||
522 | /* | |
523 | * @device: path to device | |
524 | */ | |
525 | int is_loopdev(const char *device) | |
526 | { | |
527 | struct stat st; | |
528 | ||
529 | if (!device) | |
530 | return 0; | |
531 | ||
532 | return (stat(device, &st) == 0 && | |
533 | S_ISBLK(st.st_mode) && | |
534 | major(st.st_rdev) == LOOPDEV_MAJOR); | |
535 | } | |
536 | ||
537 | /* | |
538 | * @lc: context | |
539 | * | |
540 | * Returns result from LOOP_GET_STAT64 ioctl or NULL on error. | |
541 | */ | |
542 | struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc) | |
543 | { | |
544 | int fd; | |
545 | ||
6c224de1 | 546 | if (!lc || lc->info_failed) |
10ee5932 KZ |
547 | return NULL; |
548 | if (lc->has_info) | |
549 | return &lc->info; | |
550 | ||
551 | fd = loopcxt_get_fd(lc); | |
552 | if (fd < 0) | |
553 | return NULL; | |
554 | ||
555 | if (ioctl(fd, LOOP_GET_STATUS64, &lc->info) == 0) { | |
556 | lc->has_info = 1; | |
6c224de1 KZ |
557 | lc->info_failed = 0; |
558 | DBG(lc, loopdev_debug("reading loop_info64 OK")); | |
10ee5932 | 559 | return &lc->info; |
6c224de1 KZ |
560 | } else { |
561 | lc->info_failed = 1; | |
562 | DBG(lc, loopdev_debug("reading loop_info64 FAILED")); | |
10ee5932 KZ |
563 | } |
564 | ||
565 | return NULL; | |
566 | } | |
567 | ||
568 | /* | |
569 | * @lc: context | |
570 | * | |
571 | * Returns (allocated) string with path to the file assicieted | |
572 | * with the current loop device. | |
573 | */ | |
574 | char *loopcxt_get_backing_file(struct loopdev_cxt *lc) | |
575 | { | |
576 | struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc); | |
577 | char *res = NULL; | |
578 | ||
579 | if (sysfs) | |
fd7f0718 KZ |
580 | /* |
581 | * This is always preffered, the loop_info64 | |
582 | * has too small buffer for the filename. | |
583 | */ | |
10ee5932 KZ |
584 | res = sysfs_strdup(sysfs, "loop/backing_file"); |
585 | ||
586 | if (!res && loopcxt_ioctl_enabled(lc)) { | |
587 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
588 | ||
589 | if (lo) { | |
590 | lo->lo_file_name[LO_NAME_SIZE - 2] = '*'; | |
591 | lo->lo_file_name[LO_NAME_SIZE - 1] = '\0'; | |
592 | res = strdup((char *) lo->lo_file_name); | |
593 | } | |
594 | } | |
aee31ddc | 595 | |
6c224de1 | 596 | DBG(lc, loopdev_debug("get_backing_file [%s]", res)); |
10ee5932 KZ |
597 | return res; |
598 | } | |
599 | ||
600 | /* | |
601 | * @lc: context | |
602 | * @offset: returns offset number for the given device | |
603 | * | |
604 | * Returns: <0 on error, 0 on success | |
605 | */ | |
606 | int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset) | |
607 | { | |
608 | struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc); | |
609 | int rc = -EINVAL; | |
610 | ||
611 | if (sysfs) | |
612 | rc = sysfs_read_u64(sysfs, "loop/offset", offset); | |
613 | ||
614 | if (rc && loopcxt_ioctl_enabled(lc)) { | |
615 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
616 | if (lo) { | |
617 | if (offset) | |
618 | *offset = lo->lo_offset; | |
6c224de1 | 619 | rc = 0; |
10ee5932 KZ |
620 | } |
621 | } | |
622 | ||
6c224de1 | 623 | DBG(lc, loopdev_debug("get_offset [rc=%d]", rc)); |
10ee5932 KZ |
624 | return rc; |
625 | } | |
626 | ||
627 | /* | |
628 | * @lc: context | |
629 | * @sizelimit: returns size limit for the given device | |
630 | * | |
631 | * Returns: <0 on error, 0 on success | |
632 | */ | |
633 | int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size) | |
634 | { | |
635 | struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc); | |
636 | int rc = -EINVAL; | |
637 | ||
638 | if (sysfs) | |
639 | rc = sysfs_read_u64(sysfs, "loop/sizelimit", size); | |
640 | ||
641 | if (rc && loopcxt_ioctl_enabled(lc)) { | |
642 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
643 | if (lo) { | |
644 | if (size) | |
645 | *size = lo->lo_sizelimit; | |
6c224de1 | 646 | rc = 0; |
10ee5932 KZ |
647 | } |
648 | } | |
649 | ||
6c224de1 KZ |
650 | DBG(lc, loopdev_debug("get_sizelimit [rc=%d]", rc)); |
651 | return rc; | |
652 | } | |
653 | ||
654 | /* | |
655 | * @lc: context | |
656 | * @devno: returns encryption type | |
657 | * | |
658 | * Cryptoloop is DEPRECATED! | |
659 | * | |
660 | * Returns: <0 on error, 0 on success | |
661 | */ | |
662 | int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type) | |
663 | { | |
664 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
665 | int rc = -EINVAL; | |
666 | ||
667 | if (lo) { | |
668 | if (type) | |
669 | *type = lo->lo_encrypt_type; | |
670 | rc = 0; | |
671 | } | |
672 | DBG(lc, loopdev_debug("get_encrypt_type [rc=%d]", rc)); | |
673 | return rc; | |
674 | } | |
675 | ||
676 | /* | |
677 | * @lc: context | |
678 | * @devno: returns crypt name | |
679 | * | |
680 | * Cryptoloop is DEPRECATED! | |
681 | * | |
682 | * Returns: <0 on error, 0 on success | |
683 | */ | |
684 | const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc) | |
685 | { | |
686 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
687 | ||
688 | if (lo) | |
689 | return (char *) lo->lo_crypt_name; | |
690 | ||
691 | DBG(lc, loopdev_debug("get_crypt_name failed")); | |
692 | return NULL; | |
693 | } | |
694 | ||
695 | /* | |
696 | * @lc: context | |
697 | * @devno: returns backing file devno | |
698 | * | |
699 | * Returns: <0 on error, 0 on success | |
700 | */ | |
701 | int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno) | |
702 | { | |
703 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
704 | int rc = -EINVAL; | |
705 | ||
706 | if (lo) { | |
707 | if (devno) | |
708 | *devno = lo->lo_device; | |
709 | rc = 0; | |
710 | } | |
711 | DBG(lc, loopdev_debug("get_backing_devno [rc=%d]", rc)); | |
712 | return rc; | |
713 | } | |
714 | ||
715 | /* | |
716 | * @lc: context | |
717 | * @ino: returns backing file inode | |
718 | * | |
719 | * Returns: <0 on error, 0 on success | |
720 | */ | |
721 | int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino) | |
722 | { | |
723 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
724 | int rc = -EINVAL; | |
725 | ||
726 | if (lo) { | |
727 | if (ino) | |
728 | *ino = lo->lo_inode; | |
729 | rc = 0; | |
730 | } | |
731 | DBG(lc, loopdev_debug("get_backing_inode [rc=%d]", rc)); | |
10ee5932 KZ |
732 | return rc; |
733 | } | |
734 | ||
59d749c3 KZ |
735 | /* |
736 | * Check if the kernel supports partitioned loop devices. | |
737 | * | |
738 | * Notes: | |
739 | * - kernels < 3.2 support partitioned loop devices and PT scanning | |
740 | * only if max_part= module paremeter is non-zero | |
741 | * | |
742 | * - kernels >= 3.2 always support partitioned loop devices | |
743 | * | |
744 | * - kernels >= 3.2 always support BLKPG_{ADD,DEL}_PARTITION ioctls | |
745 | * | |
746 | * - kernels >= 3.2 enable PT scanner only if max_part= is non-zero or if the | |
747 | * LO_FLAGS_PARTSCAN flag is set for the device. The PT scanner is disabled | |
748 | * by default. | |
749 | * | |
750 | * See kernel commit e03c8dd14915fabc101aa495828d58598dc5af98. | |
751 | */ | |
752 | int loopmod_supports_partscan(void) | |
753 | { | |
754 | int rc, ret = 0; | |
755 | FILE *f; | |
756 | ||
757 | if (get_linux_version() >= KERNEL_VERSION(3,2,0)) | |
758 | return 1; | |
759 | ||
760 | f = fopen("/sys/module/loop/parameters/max_part", "r"); | |
761 | if (!f) | |
762 | return 0; | |
763 | rc = fscanf(f, "%d", &ret); | |
764 | fclose(f); | |
765 | return rc = 1 ? ret : 0; | |
766 | } | |
767 | ||
10ee5932 KZ |
768 | /* |
769 | * @lc: context | |
770 | * | |
59d749c3 KZ |
771 | * Returns: 1 if the partscan flags is set *or* (for old kernels) partitions |
772 | * scannig is enabled for all loop devices. | |
773 | */ | |
774 | int loopcxt_is_partscan(struct loopdev_cxt *lc) | |
775 | { | |
776 | struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc); | |
777 | ||
778 | if (sysfs) { | |
779 | /* kernel >= 3.2 */ | |
780 | int fl; | |
781 | if (sysfs_read_int(sysfs, "loop/partscan", &fl) == 0) | |
782 | return fl; | |
783 | } | |
784 | ||
785 | /* old kernels (including kernels without loopN/loop/<flags> directory */ | |
786 | return loopmod_supports_partscan(); | |
787 | } | |
788 | ||
789 | /* | |
790 | * @lc: context | |
791 | * | |
792 | * Returns: 1 if the autoclear flags is set. | |
10ee5932 KZ |
793 | */ |
794 | int loopcxt_is_autoclear(struct loopdev_cxt *lc) | |
795 | { | |
796 | struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc); | |
797 | ||
798 | if (sysfs) { | |
799 | int fl; | |
800 | if (sysfs_read_int(sysfs, "loop/autoclear", &fl) == 0) | |
801 | return fl; | |
802 | } | |
803 | ||
804 | if (loopcxt_ioctl_enabled(lc)) { | |
805 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
806 | if (lo) | |
807 | return lo->lo_flags & LO_FLAGS_AUTOCLEAR; | |
808 | } | |
809 | return 0; | |
810 | } | |
811 | ||
fd7f0718 KZ |
812 | /* |
813 | * @lc: context | |
814 | * | |
59d749c3 | 815 | * Returns: 1 if the readonly flags is set. |
fd7f0718 KZ |
816 | */ |
817 | int loopcxt_is_readonly(struct loopdev_cxt *lc) | |
818 | { | |
819 | struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc); | |
820 | ||
821 | if (sysfs) { | |
822 | int fl; | |
823 | if (sysfs_read_int(sysfs, "ro", &fl) == 0) | |
824 | return fl; | |
825 | } | |
826 | ||
827 | if (loopcxt_ioctl_enabled(lc)) { | |
828 | struct loop_info64 *lo = loopcxt_get_info(lc); | |
829 | if (lo) | |
830 | return lo->lo_flags & LO_FLAGS_READ_ONLY; | |
831 | } | |
832 | return 0; | |
833 | } | |
834 | ||
6c224de1 KZ |
835 | /* |
836 | * @lc: context | |
837 | * @st: backing file stat or NULL | |
838 | * @backing_file: filename | |
839 | * @offset: offset | |
840 | * @flags: LOOPDEV_FL_OFFSET if @offset should not be ignored | |
841 | * | |
842 | * Returns 1 if the current @lc loopdev is associated with the given backing | |
843 | * file. Note that the preferred way is to use devno and inode number rather | |
844 | * than filename. The @backing_file filename is poor solution usable in case | |
845 | * that you don't have rights to call stat(). | |
846 | * | |
847 | * Don't forget that old kernels provide very restricted (in size) backing | |
848 | * filename by LOOP_GET_STAT64 ioctl only. | |
849 | */ | |
850 | int loopcxt_is_used(struct loopdev_cxt *lc, | |
851 | struct stat *st, | |
852 | const char *backing_file, | |
853 | uint64_t offset, | |
854 | int flags) | |
855 | { | |
856 | ino_t ino; | |
857 | dev_t dev; | |
858 | ||
859 | if (!lc) | |
860 | return 0; | |
861 | ||
862 | DBG(lc, loopdev_debug("checking %s vs. %s", | |
863 | loopcxt_get_device(lc), | |
864 | backing_file)); | |
865 | ||
866 | if (st && loopcxt_get_backing_inode(lc, &ino) == 0 && | |
867 | loopcxt_get_backing_devno(lc, &dev) == 0) { | |
868 | ||
869 | if (ino == st->st_ino && dev == st->st_dev) | |
870 | goto found; | |
871 | ||
872 | /* don't use filename if we have devno and inode */ | |
873 | return 0; | |
874 | } | |
875 | ||
876 | /* poor man's solution */ | |
877 | if (backing_file) { | |
878 | char *name = loopcxt_get_backing_file(lc); | |
879 | int rc = name && strcmp(name, backing_file) == 0; | |
880 | ||
881 | free(name); | |
882 | if (rc) | |
883 | goto found; | |
884 | } | |
885 | ||
886 | return 0; | |
887 | found: | |
888 | if (flags & LOOPDEV_FL_OFFSET) { | |
889 | uint64_t off; | |
890 | ||
891 | return loopcxt_get_offset(lc, &off) == 0 && off == offset; | |
892 | } | |
893 | return 1; | |
894 | } | |
895 | ||
fd7f0718 KZ |
896 | /* |
897 | * The setting is removed by loopcxt_set_device() loopcxt_next()! | |
898 | */ | |
10ee5932 KZ |
899 | int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset) |
900 | { | |
901 | if (!lc) | |
902 | return -EINVAL; | |
903 | lc->info.lo_offset = offset; | |
aee31ddc KZ |
904 | |
905 | DBG(lc, loopdev_debug("set offset=%jd", offset)); | |
10ee5932 KZ |
906 | return 0; |
907 | } | |
908 | ||
fd7f0718 KZ |
909 | /* |
910 | * The setting is removed by loopcxt_set_device() loopcxt_next()! | |
911 | */ | |
10ee5932 KZ |
912 | int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit) |
913 | { | |
914 | if (!lc) | |
915 | return -EINVAL; | |
916 | lc->info.lo_sizelimit = sizelimit; | |
aee31ddc KZ |
917 | |
918 | DBG(lc, loopdev_debug("set sizelimit=%jd", sizelimit)); | |
10ee5932 KZ |
919 | return 0; |
920 | } | |
921 | ||
922 | /* | |
923 | * @lc: context | |
924 | * @flags: kernel LO_FLAGS_{READ_ONLY,USE_AOPS,AUTOCLEAR} flags | |
925 | * | |
fd7f0718 KZ |
926 | * The setting is removed by loopcxt_set_device() loopcxt_next()! |
927 | * | |
10ee5932 KZ |
928 | * Returns: 0 on success, <0 on error. |
929 | */ | |
930 | int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags) | |
931 | { | |
932 | if (!lc) | |
933 | return -EINVAL; | |
934 | lc->info.lo_flags = flags; | |
aee31ddc KZ |
935 | |
936 | DBG(lc, loopdev_debug("set flags=%u", (unsigned) flags)); | |
10ee5932 KZ |
937 | return 0; |
938 | } | |
939 | ||
940 | /* | |
941 | * @lc: context | |
942 | * @filename: backing file path (the path will be canonicalized) | |
943 | * | |
fd7f0718 KZ |
944 | * The setting is removed by loopcxt_set_device() loopcxt_next()! |
945 | * | |
10ee5932 KZ |
946 | * Returns: 0 on success, <0 on error. |
947 | */ | |
948 | int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename) | |
949 | { | |
950 | if (!lc) | |
951 | return -EINVAL; | |
952 | ||
953 | lc->filename = canonicalize_path(filename); | |
954 | if (!lc->filename) | |
955 | return -errno; | |
956 | ||
957 | strncpy((char *)lc->info.lo_file_name, lc->filename, LO_NAME_SIZE); | |
958 | lc->info.lo_file_name[LO_NAME_SIZE- 1] = '\0'; | |
959 | ||
aee31ddc | 960 | DBG(lc, loopdev_debug("set backing file=%s", lc->info.lo_file_name)); |
10ee5932 KZ |
961 | return 0; |
962 | } | |
963 | ||
964 | static int digits_only(const char *s) | |
965 | { | |
966 | while (*s) | |
967 | if (!isdigit(*s++)) | |
968 | return 0; | |
969 | return 1; | |
970 | } | |
971 | ||
972 | /* | |
973 | * @lc: context | |
974 | * @encryption: encryption name / type (see lopsetup man page) | |
975 | * @password | |
976 | * | |
fd7f0718 | 977 | * Note that the encryption functionality is deprecated an unmaintained. Use |
10ee5932 KZ |
978 | * cryptsetup (it also supports AES-loops). |
979 | * | |
fd7f0718 KZ |
980 | * The setting is removed by loopcxt_set_device() loopcxt_next()! |
981 | * | |
10ee5932 KZ |
982 | * Returns: 0 on success, <0 on error. |
983 | */ | |
984 | int loopcxt_set_encryption(struct loopdev_cxt *lc, | |
985 | const char *encryption, | |
986 | const char *password) | |
987 | { | |
988 | if (!lc) | |
989 | return -EINVAL; | |
990 | ||
aee31ddc KZ |
991 | DBG(lc, loopdev_debug("setting encryption '%s'", encryption)); |
992 | ||
10ee5932 KZ |
993 | if (encryption && *encryption) { |
994 | if (digits_only(encryption)) { | |
995 | lc->info.lo_encrypt_type = atoi(encryption); | |
996 | } else { | |
997 | lc->info.lo_encrypt_type = LO_CRYPT_CRYPTOAPI; | |
998 | snprintf((char *)lc->info.lo_crypt_name, LO_NAME_SIZE, | |
999 | "%s", encryption); | |
1000 | } | |
1001 | } | |
1002 | ||
1003 | switch (lc->info.lo_encrypt_type) { | |
1004 | case LO_CRYPT_NONE: | |
1005 | lc->info.lo_encrypt_key_size = 0; | |
1006 | break; | |
1007 | default: | |
c7e0925d | 1008 | DBG(lc, loopdev_debug("setting encryption key")); |
10ee5932 KZ |
1009 | memset(lc->info.lo_encrypt_key, 0, LO_KEY_SIZE); |
1010 | strncpy((char *)lc->info.lo_encrypt_key, password, LO_KEY_SIZE); | |
1011 | lc->info.lo_encrypt_key[LO_KEY_SIZE - 1] = '\0'; | |
1012 | lc->info.lo_encrypt_key_size = LO_KEY_SIZE; | |
1013 | break; | |
1014 | } | |
c7e0925d KZ |
1015 | |
1016 | DBG(lc, loopdev_debug("encryption successfully set")); | |
10ee5932 KZ |
1017 | return 0; |
1018 | } | |
1019 | ||
1020 | /* | |
1021 | * @cl: context | |
1022 | * | |
1023 | * Associate the current device (see loopcxt_{set,get}_device()) with | |
1024 | * a file (see loopcxt_set_backing_file()). | |
1025 | * | |
fd7f0718 KZ |
1026 | * The device is initialized read-write by default. If you want read-only |
1027 | * device then set LO_FLAGS_READ_ONLY by loopcxt_set_flags(). The LOOPDEV_FL_* | |
1028 | * flags are ignored and modified according to LO_FLAGS_*. | |
1029 | * | |
1030 | * If the device is already open by loopcxt_get_fd() then this setup device | |
1031 | * function will re-open the device to fix read/write mode. | |
1032 | * | |
1033 | * The device is also initialized read-only if the backing file is not | |
1034 | * possible to open read-write (e.g. read-only FS). | |
10ee5932 KZ |
1035 | * |
1036 | * Returns: <0 on error, 0 on success. | |
1037 | */ | |
1038 | int loopcxt_setup_device(struct loopdev_cxt *lc) | |
1039 | { | |
fd7f0718 | 1040 | int file_fd, dev_fd, mode = O_RDWR, rc = -1; |
10ee5932 KZ |
1041 | |
1042 | if (!lc || !*lc->device || !lc->filename) | |
1043 | return -EINVAL; | |
1044 | ||
aee31ddc KZ |
1045 | DBG(lc, loopdev_debug("device setup requested")); |
1046 | ||
10ee5932 KZ |
1047 | /* |
1048 | * Open backing file and device | |
1049 | */ | |
fd7f0718 KZ |
1050 | if (lc->info.lo_flags & LO_FLAGS_READ_ONLY) |
1051 | mode = O_RDONLY; | |
10ee5932 KZ |
1052 | |
1053 | if ((file_fd = open(lc->filename, mode)) < 0) { | |
1054 | if (mode != O_RDONLY && (errno == EROFS || errno == EACCES)) | |
1055 | file_fd = open(lc->filename, mode = O_RDONLY); | |
1056 | ||
aee31ddc KZ |
1057 | if (file_fd < 0) { |
1058 | DBG(lc, loopdev_debug("open backing file failed: %m")); | |
10ee5932 | 1059 | return -errno; |
aee31ddc | 1060 | } |
10ee5932 | 1061 | } |
d356c5d2 | 1062 | DBG(lc, loopdev_debug("setup: backing file open: OK")); |
10ee5932 | 1063 | |
fd7f0718 KZ |
1064 | if (lc->fd != -1 && lc->mode != mode) { |
1065 | close(lc->fd); | |
1066 | lc->fd = -1; | |
1067 | lc->mode = 0; | |
1068 | } | |
1069 | ||
10ee5932 | 1070 | if (mode == O_RDONLY) { |
fd7f0718 KZ |
1071 | lc->flags |= LOOPDEV_FL_RDONLY; /* open() mode */ |
1072 | lc->info.lo_flags |= LO_FLAGS_READ_ONLY; /* kernel loopdev mode */ | |
1073 | } else { | |
1074 | lc->flags |= LOOPDEV_FL_RDWR; /* open() mode */ | |
1075 | lc->info.lo_flags &= ~LO_FLAGS_READ_ONLY; | |
1076 | lc->flags &= ~LOOPDEV_FL_RDONLY; | |
1077 | } | |
10ee5932 KZ |
1078 | |
1079 | dev_fd = loopcxt_get_fd(lc); | |
1080 | if (dev_fd < 0) { | |
1081 | rc = -errno; | |
1082 | goto err; | |
1083 | } | |
1084 | ||
d356c5d2 KZ |
1085 | DBG(lc, loopdev_debug("setup: device open: OK")); |
1086 | ||
10ee5932 KZ |
1087 | /* |
1088 | * Set FD | |
1089 | */ | |
1090 | if (ioctl(dev_fd, LOOP_SET_FD, file_fd) < 0) { | |
1091 | rc = -errno; | |
aee31ddc | 1092 | DBG(lc, loopdev_debug("LOOP_SET_FD failed: %m")); |
10ee5932 KZ |
1093 | goto err; |
1094 | } | |
d356c5d2 KZ |
1095 | |
1096 | DBG(lc, loopdev_debug("setup: LOOP_SET_FD: OK")); | |
1097 | ||
10ee5932 KZ |
1098 | close(file_fd); |
1099 | file_fd = -1; | |
1100 | ||
aee31ddc KZ |
1101 | if (ioctl(dev_fd, LOOP_SET_STATUS64, &lc->info)) { |
1102 | DBG(lc, loopdev_debug("LOOP_SET_STATUS64 failed: %m")); | |
10ee5932 | 1103 | goto err; |
aee31ddc | 1104 | } |
d356c5d2 KZ |
1105 | |
1106 | DBG(lc, loopdev_debug("setup: LOOP_SET_STATUS64: OK")); | |
1107 | ||
10ee5932 KZ |
1108 | memset(&lc->info, 0, sizeof(lc->info)); |
1109 | lc->has_info = 0; | |
6c224de1 | 1110 | lc->info_failed = 0; |
10ee5932 | 1111 | |
8fbbe528 | 1112 | DBG(lc, loopdev_debug("setup success [rc=0]")); |
10ee5932 KZ |
1113 | return 0; |
1114 | err: | |
1115 | if (file_fd >= 0) | |
1116 | close(file_fd); | |
1117 | if (dev_fd >= 0) | |
1118 | ioctl(dev_fd, LOOP_CLR_FD, 0); | |
1119 | ||
8fbbe528 | 1120 | DBG(lc, loopdev_debug("setup failed [rc=%d]", rc)); |
10ee5932 KZ |
1121 | return rc; |
1122 | } | |
1123 | ||
1124 | int loopcxt_delete_device(struct loopdev_cxt *lc) | |
1125 | { | |
1126 | int fd = loopcxt_get_fd(lc); | |
1127 | ||
1128 | if (fd < 0) | |
1129 | return -EINVAL; | |
1130 | ||
aee31ddc KZ |
1131 | if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { |
1132 | DBG(lc, loopdev_debug("LOOP_CLR_FD failed: %m")); | |
10ee5932 | 1133 | return -errno; |
aee31ddc KZ |
1134 | } |
1135 | ||
1136 | DBG(lc, loopdev_debug("device removed")); | |
10ee5932 KZ |
1137 | return 0; |
1138 | } | |
1139 | ||
59d749c3 KZ |
1140 | /* |
1141 | * Note that LOOP_CTL_GET_FREE ioctl is supported since kernel 3.1. In older | |
1142 | * kernels we have to check all loop devices to found unused one. | |
1143 | * | |
1144 | * See kernel commit 770fe30a46a12b6fb6b63fbe1737654d28e8484. | |
1145 | */ | |
10ee5932 KZ |
1146 | int loopcxt_find_unused(struct loopdev_cxt *lc) |
1147 | { | |
0b14bf7a | 1148 | int rc = -1; |
10ee5932 | 1149 | |
aee31ddc KZ |
1150 | DBG(lc, loopdev_debug("find_unused requested")); |
1151 | ||
0b14bf7a KZ |
1152 | if (lc->flags & LOOPDEV_FL_CONTROL) { |
1153 | int ctl = open(_PATH_DEV_LOOPCTL, O_RDWR); | |
10ee5932 | 1154 | |
0b14bf7a KZ |
1155 | if (ctl >= 0) |
1156 | rc = ioctl(ctl, LOOP_CTL_GET_FREE); | |
1157 | if (rc >= 0) { | |
1158 | char name[16]; | |
1159 | snprintf(name, sizeof(name), "loop%d", rc); | |
1160 | ||
1161 | rc = loopiter_set_device(lc, name); | |
1162 | } | |
1163 | if (ctl >= 0) | |
1164 | close(ctl); | |
1165 | DBG(lc, loopdev_debug("find_unused by loop-control [rc=%d]", rc)); | |
1166 | } | |
1167 | ||
1168 | if (rc < 0) { | |
1169 | rc = loopcxt_init_iterator(lc, LOOPITER_FL_FREE); | |
1170 | if (rc) | |
1171 | return rc; | |
10ee5932 | 1172 | |
0b14bf7a KZ |
1173 | rc = loopcxt_next(lc); |
1174 | loopcxt_deinit_iterator(lc); | |
1175 | DBG(lc, loopdev_debug("find_unused by scan [rc=%d]", rc)); | |
1176 | } | |
10ee5932 KZ |
1177 | return rc; |
1178 | } | |
1179 | ||
1180 | ||
1181 | ||
1182 | /* | |
1183 | * Return: TRUE/FALSE | |
1184 | */ | |
1185 | int loopdev_is_autoclear(const char *device) | |
1186 | { | |
1187 | struct loopdev_cxt lc; | |
1188 | int rc; | |
1189 | ||
1190 | if (!device) | |
1191 | return 0; | |
1192 | ||
1193 | loopcxt_init(&lc, 0); | |
1194 | loopcxt_set_device(&lc, device); | |
1195 | rc = loopcxt_is_autoclear(&lc); | |
1196 | loopcxt_deinit(&lc); | |
1197 | ||
1198 | return rc; | |
1199 | } | |
1200 | ||
1201 | char *loopdev_get_backing_file(const char *device) | |
1202 | { | |
1203 | struct loopdev_cxt lc; | |
1204 | char *res; | |
1205 | ||
1206 | if (!device) | |
1207 | return NULL; | |
1208 | ||
1209 | loopcxt_init(&lc, 0); | |
1210 | loopcxt_set_device(&lc, device); | |
1211 | res = loopcxt_get_backing_file(&lc); | |
1212 | loopcxt_deinit(&lc); | |
1213 | ||
1214 | return res; | |
1215 | } | |
1216 | ||
1217 | /* | |
1218 | * Returns: TRUE/FALSE | |
1219 | */ | |
1220 | int loopdev_is_used(const char *device, const char *filename, | |
1221 | uint64_t offset, int flags) | |
1222 | { | |
1223 | struct loopdev_cxt lc; | |
6c224de1 | 1224 | struct stat st; |
10ee5932 KZ |
1225 | int rc = 0; |
1226 | ||
8b470b20 | 1227 | if (!device || !filename) |
10ee5932 KZ |
1228 | return 0; |
1229 | ||
1230 | loopcxt_init(&lc, 0); | |
1231 | loopcxt_set_device(&lc, device); | |
1232 | ||
6c224de1 KZ |
1233 | rc = !stat(filename, &st); |
1234 | rc = loopcxt_is_used(&lc, rc ? &st : NULL, filename, offset, flags); | |
10ee5932 | 1235 | |
10ee5932 | 1236 | loopcxt_deinit(&lc); |
10ee5932 KZ |
1237 | return rc; |
1238 | } | |
1239 | ||
1240 | int loopdev_delete(const char *device) | |
1241 | { | |
1242 | struct loopdev_cxt lc; | |
1243 | int rc; | |
1244 | ||
8b470b20 KZ |
1245 | if (!device) |
1246 | return -EINVAL; | |
1247 | ||
10ee5932 KZ |
1248 | loopcxt_init(&lc, 0); |
1249 | rc = loopcxt_set_device(&lc, device); | |
1250 | if (!rc) | |
1251 | rc = loopcxt_delete_device(&lc); | |
1252 | loopcxt_deinit(&lc); | |
1253 | return rc; | |
1254 | } | |
1255 | ||
1256 | /* | |
1257 | * Returns: 0 = success, < 0 error, 1 not found | |
1258 | */ | |
1259 | int loopcxt_find_by_backing_file(struct loopdev_cxt *lc, const char *filename, | |
1260 | uint64_t offset, int flags) | |
1261 | { | |
6c224de1 KZ |
1262 | int rc, hasst; |
1263 | struct stat st; | |
10ee5932 KZ |
1264 | |
1265 | if (!filename) | |
1266 | return -EINVAL; | |
1267 | ||
6c224de1 KZ |
1268 | hasst = !stat(filename, &st); |
1269 | ||
10ee5932 KZ |
1270 | rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED); |
1271 | if (rc) | |
1272 | return rc; | |
1273 | ||
6c224de1 | 1274 | while ((rc = loopcxt_next(lc)) == 0) { |
10ee5932 | 1275 | |
6c224de1 KZ |
1276 | if (loopcxt_is_used(lc, hasst ? &st : NULL, |
1277 | filename, offset, flags)) | |
1278 | break; | |
10ee5932 KZ |
1279 | } |
1280 | ||
1281 | loopcxt_deinit_iterator(lc); | |
1282 | return rc; | |
1283 | } | |
1284 | ||
1285 | /* | |
1286 | * Returns allocated string with device name | |
1287 | */ | |
1288 | char *loopdev_find_by_backing_file(const char *filename, uint64_t offset, int flags) | |
1289 | { | |
1290 | struct loopdev_cxt lc; | |
1291 | char *res = NULL; | |
1292 | ||
1293 | if (!filename) | |
1294 | return NULL; | |
1295 | ||
1296 | loopcxt_init(&lc, 0); | |
1297 | if (loopcxt_find_by_backing_file(&lc, filename, offset, flags)) | |
1298 | res = loopcxt_strdup_device(&lc); | |
1299 | loopcxt_deinit(&lc); | |
1300 | ||
1301 | return res; | |
1302 | } | |
1303 | ||
d5688130 KZ |
1304 | /* |
1305 | * Returns number of loop devices associated with @file, if only one loop | |
1306 | * device is associeted with the given @filename and @loopdev is not NULL then | |
1307 | * @loopdev returns name of the device. | |
1308 | */ | |
1309 | int loopdev_count_by_backing_file(const char *filename, char **loopdev) | |
1310 | { | |
1311 | struct loopdev_cxt lc; | |
1312 | int count = 0; | |
1313 | ||
1314 | if (!filename) | |
1315 | return -1; | |
1316 | ||
1317 | loopcxt_init(&lc, 0); | |
1318 | if (loopcxt_init_iterator(&lc, LOOPITER_FL_USED)) | |
1319 | return -1; | |
1320 | ||
1321 | while(loopcxt_next(&lc) == 0) { | |
1322 | char *backing = loopcxt_get_backing_file(&lc); | |
1323 | ||
1324 | if (!backing || strcmp(backing, filename)) { | |
1325 | free(backing); | |
1326 | continue; | |
1327 | } | |
1328 | ||
1329 | free(backing); | |
1330 | if (loopdev && count == 0) | |
1331 | *loopdev = loopcxt_strdup_device(&lc); | |
1332 | count++; | |
1333 | } | |
1334 | ||
1335 | loopcxt_deinit(&lc); | |
1336 | ||
1337 | if (loopdev && count > 1) { | |
1338 | free(*loopdev); | |
1339 | *loopdev = NULL; | |
1340 | } | |
1341 | return count; | |
1342 | } | |
1343 | ||
10ee5932 KZ |
1344 | |
1345 | #ifdef TEST_PROGRAM_LOOPDEV | |
1346 | #include <errno.h> | |
1347 | #include <err.h> | |
10ee5932 | 1348 | |
aee31ddc | 1349 | static void test_loop_info(const char *device, int flags, int debug) |
10ee5932 KZ |
1350 | { |
1351 | struct loopdev_cxt lc; | |
1352 | char *p; | |
1353 | uint64_t u64; | |
1354 | ||
1355 | loopcxt_init(&lc, flags); | |
aee31ddc KZ |
1356 | loopcxt_enable_debug(&lc, debug); |
1357 | ||
10ee5932 KZ |
1358 | if (loopcxt_set_device(&lc, device)) |
1359 | err(EXIT_FAILURE, "failed to set device"); | |
1360 | ||
1361 | p = loopcxt_get_backing_file(&lc); | |
1362 | printf("\tBACKING FILE: %s\n", p); | |
1363 | free(p); | |
1364 | ||
1365 | if (loopcxt_get_offset(&lc, &u64) == 0) | |
1366 | printf("\tOFFSET: %jd\n", u64); | |
1367 | ||
1368 | if (loopcxt_get_sizelimit(&lc, &u64) == 0) | |
1369 | printf("\tSIZE LIMIT: %jd\n", u64); | |
1370 | ||
1371 | printf("\tAUTOCLEAR: %s\n", loopcxt_is_autoclear(&lc) ? "YES" : "NOT"); | |
1372 | ||
1373 | loopcxt_deinit(&lc); | |
1374 | } | |
1375 | ||
aee31ddc | 1376 | static void test_loop_scan(int flags, int debug) |
10ee5932 KZ |
1377 | { |
1378 | struct loopdev_cxt lc; | |
1379 | int rc; | |
1380 | ||
1381 | loopcxt_init(&lc, 0); | |
aee31ddc | 1382 | loopcxt_enable_debug(&lc, debug); |
10ee5932 KZ |
1383 | |
1384 | if (loopcxt_init_iterator(&lc, flags)) | |
1385 | err(EXIT_FAILURE, "iterator initlization failed"); | |
1386 | ||
1387 | while((rc = loopcxt_next(&lc)) == 0) { | |
1388 | const char *device = loopcxt_get_device(&lc); | |
1389 | ||
1390 | if (flags & LOOPITER_FL_USED) { | |
1391 | char *backing = loopcxt_get_backing_file(&lc); | |
1392 | printf("\t%s: %s\n", device, backing); | |
1393 | free(backing); | |
1394 | } else | |
1395 | printf("\t%s\n", device); | |
1396 | } | |
1397 | ||
1398 | if (rc < 0) | |
1399 | err(EXIT_FAILURE, "loopdevs scanning failed"); | |
1400 | ||
1401 | loopcxt_deinit(&lc); | |
1402 | } | |
1403 | ||
aee31ddc | 1404 | static int test_loop_setup(const char *filename, const char *device, int debug) |
10ee5932 KZ |
1405 | { |
1406 | struct loopdev_cxt lc; | |
1407 | int rc = 0; | |
1408 | ||
1409 | loopcxt_init(&lc, 0); | |
aee31ddc | 1410 | loopcxt_enable_debug(&lc, debug); |
10ee5932 | 1411 | |
10ee5932 KZ |
1412 | if (device) { |
1413 | rc = loopcxt_set_device(&lc, device); | |
1414 | if (rc) | |
1415 | err(EXIT_FAILURE, "failed to set device: %s", device); | |
1416 | } | |
1417 | ||
1418 | do { | |
1419 | if (!device) { | |
1420 | rc = loopcxt_find_unused(&lc); | |
1421 | if (rc) | |
ad2d0d85 | 1422 | err(EXIT_FAILURE, "failed to find unused device"); |
10ee5932 KZ |
1423 | printf("Trying to use '%s'\n", loopcxt_get_device(&lc)); |
1424 | } | |
1425 | ||
fd7f0718 KZ |
1426 | if (loopcxt_set_backing_file(&lc, filename)) |
1427 | err(EXIT_FAILURE, "failed to set backing file"); | |
1428 | ||
10ee5932 KZ |
1429 | rc = loopcxt_setup_device(&lc); |
1430 | if (rc == 0) | |
1431 | break; /* success */ | |
1432 | ||
1433 | if (device || rc != -EBUSY) | |
1434 | err(EXIT_FAILURE, "failed to setup device for %s", | |
1435 | lc.filename); | |
1436 | ||
1437 | printf("device stolen...trying again\n"); | |
1438 | } while (1); | |
1439 | ||
1440 | loopcxt_deinit(&lc); | |
1441 | ||
1442 | return 0; | |
1443 | } | |
1444 | ||
1445 | int main(int argc, char *argv[]) | |
1446 | { | |
aee31ddc KZ |
1447 | int dbg; |
1448 | ||
10ee5932 KZ |
1449 | if (argc < 2) |
1450 | goto usage; | |
1451 | ||
aee31ddc KZ |
1452 | dbg = getenv("LOOPDEV_DEBUG") == NULL ? 0 : 1; |
1453 | ||
10ee5932 KZ |
1454 | if (argc == 3 && strcmp(argv[1], "--info") == 0) { |
1455 | printf("---sysfs & ioctl:---\n"); | |
aee31ddc | 1456 | test_loop_info(argv[2], 0, dbg); |
10ee5932 | 1457 | printf("---sysfs only:---\n"); |
aee31ddc | 1458 | test_loop_info(argv[2], LOOPDEV_FL_NOIOCTL, dbg); |
10ee5932 | 1459 | printf("---ioctl only:---\n"); |
aee31ddc | 1460 | test_loop_info(argv[2], LOOPDEV_FL_NOSYSFS, dbg); |
10ee5932 KZ |
1461 | |
1462 | } else if (argc == 2 && strcmp(argv[1], "--used") == 0) { | |
1463 | printf("---all used devices---\n"); | |
aee31ddc | 1464 | test_loop_scan(LOOPITER_FL_USED, dbg); |
10ee5932 KZ |
1465 | |
1466 | } else if (argc == 2 && strcmp(argv[1], "--free") == 0) { | |
1467 | printf("---all free devices---\n"); | |
aee31ddc | 1468 | test_loop_scan(LOOPITER_FL_FREE, dbg); |
10ee5932 KZ |
1469 | |
1470 | } else if (argc >= 3 && strcmp(argv[1], "--setup") == 0) { | |
aee31ddc | 1471 | test_loop_setup(argv[2], argv[3], dbg); |
10ee5932 KZ |
1472 | |
1473 | } else if (argc == 3 && strcmp(argv[1], "--delete") == 0) { | |
1474 | if (loopdev_delete(argv[2])) | |
1475 | errx(EXIT_FAILURE, "failed to deinitialize device %s", argv[2]); | |
1476 | } else | |
1477 | goto usage; | |
1478 | ||
1479 | return EXIT_SUCCESS; | |
1480 | ||
1481 | usage: | |
1482 | errx(EXIT_FAILURE, "usage: \n" | |
1483 | " %1$s --info <device>\n" | |
1484 | " %1$s --free\n" | |
1485 | " %1$s --used\n" | |
1486 | " %1$s --setup <filename> [<device>]\n" | |
1487 | " %1$s --delete\n", | |
1488 | argv[0]); | |
1489 | } | |
1490 | ||
1491 | #endif /* TEST_PROGRAM */ |