]>
Commit | Line | Data |
---|---|---|
d162fcb5 | 1 | /* Originally from Ted's losetup.c */ |
6dbe3af9 KZ |
2 | /* |
3 | * losetup.c - setup and control loop devices | |
4 | */ | |
6dbe3af9 KZ |
5 | #include <stdio.h> |
6 | #include <string.h> | |
7 | #include <ctype.h> | |
8 | #include <fcntl.h> | |
fd6b7a7f | 9 | #include <errno.h> |
6dbe3af9 KZ |
10 | #include <stdlib.h> |
11 | #include <unistd.h> | |
12 | #include <sys/ioctl.h> | |
fd6b7a7f | 13 | #include <sys/stat.h> |
22853e4a | 14 | #include <sys/mman.h> |
95f1bdee | 15 | #include <sys/sysmacros.h> |
7711bc17 | 16 | #include <inttypes.h> |
2b326e7c | 17 | #include <dirent.h> |
65329058 KZ |
18 | #include <getopt.h> |
19 | #include <stdarg.h> | |
6dbe3af9 | 20 | |
fd6b7a7f KZ |
21 | #include "loop.h" |
22 | #include "lomount.h" | |
8abcf290 | 23 | #include "strutils.h" |
7eda085c | 24 | #include "nls.h" |
0b994f0e | 25 | #include "sundries.h" |
edb68d0c | 26 | #include "pathnames.h" |
6dbe3af9 | 27 | |
259fcc57 KZ |
28 | static int is_associated(int dev, struct stat *file, unsigned long long offset, int isoff); |
29 | ||
2b326e7c KZ |
30 | #define LOOPMAJOR 7 |
31 | #define NLOOPS_DEFAULT 8 /* /dev/loop[0-7] */ | |
32 | ||
33 | struct looplist { | |
34 | int flag; /* scanning options */ | |
edb68d0c | 35 | FILE *proc; /* /proc/partitions */ |
b642d0e0 | 36 | int ncur; /* current position */ |
edb68d0c KZ |
37 | int *minors; /* ary of minor numbers (when scan whole /dev) */ |
38 | int nminors; /* number of items in *minors */ | |
39 | char name[128]; /* device name */ | |
2b326e7c KZ |
40 | int ct_perm; /* count permission problems */ |
41 | int ct_succ; /* count number of successfully | |
42 | detected devices */ | |
43 | }; | |
44 | ||
45 | #define LLFLG_USEDONLY (1 << 1) /* return used devices only */ | |
46 | #define LLFLG_FREEONLY (1 << 2) /* return non-used devices */ | |
47 | #define LLFLG_DONE (1 << 3) /* all is done */ | |
edb68d0c | 48 | #define LLFLG_PROCFS (1 << 4) /* try to found used devices in /proc/partitions */ |
2b326e7c KZ |
49 | #define LLFLG_SUBDIR (1 << 5) /* /dev/loop/N */ |
50 | #define LLFLG_DFLT (1 << 6) /* directly try to check default loops */ | |
51 | ||
b61e9390 KZ |
52 | /* TODO: move to lib/sysfs.c */ |
53 | static char *loopfile_from_sysfs(const char *device) | |
54 | { | |
55 | FILE *f; | |
56 | struct stat st; | |
57 | char buf[PATH_MAX], *res = NULL; | |
58 | ||
59 | if (stat(device, &st) || !S_ISBLK(st.st_mode)) | |
60 | return NULL; | |
61 | ||
62 | snprintf(buf, sizeof(buf), _PATH_SYS_DEVBLOCK "/%d:%d/loop/backing_file", | |
63 | major(st.st_rdev), minor(st.st_rdev)); | |
64 | ||
65 | f = fopen(buf, "r"); | |
66 | if (!f) | |
67 | return NULL; | |
68 | ||
69 | if (fgets(buf, sizeof(buf), f)) { | |
70 | size_t sz = strlen(buf); | |
71 | if (sz) { | |
72 | buf[sz - 1] = '\0'; | |
73 | res = xstrdup(buf); | |
74 | } | |
75 | } | |
76 | ||
77 | fclose(f); | |
78 | return res; | |
79 | } | |
80 | ||
db3b5b76 KZ |
81 | char *loopdev_get_loopfile(const char *device) |
82 | { | |
83 | char *res = loopfile_from_sysfs(device); | |
84 | ||
85 | if (!res) { | |
db3b5b76 KZ |
86 | struct loop_info64 lo64; |
87 | int fd; | |
88 | ||
89 | if ((fd = open(device, O_RDONLY)) < 0) | |
90 | return NULL; | |
91 | ||
92 | if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0) { | |
93 | lo64.lo_file_name[LO_NAME_SIZE-2] = '*'; | |
94 | lo64.lo_file_name[LO_NAME_SIZE-1] = 0; | |
95 | res = xstrdup((char *) lo64.lo_file_name); | |
96 | ||
db3b5b76 KZ |
97 | } |
98 | close(fd); | |
99 | } | |
100 | return res; | |
101 | } | |
102 | ||
2b326e7c KZ |
103 | int |
104 | is_loop_device (const char *device) { | |
105 | struct stat st; | |
106 | ||
107 | return (stat(device, &st) == 0 && | |
108 | S_ISBLK(st.st_mode) && | |
109 | major(st.st_rdev) == LOOPMAJOR); | |
110 | } | |
111 | ||
112 | static int | |
113 | is_loop_used(int fd) | |
114 | { | |
846985f7 | 115 | struct loop_info64 li; |
fc08b4bb KZ |
116 | |
117 | errno = 0; | |
846985f7 | 118 | if (ioctl (fd, LOOP_GET_STATUS64, &li) < 0 && errno == ENXIO) |
fc08b4bb KZ |
119 | return 0; |
120 | return 1; | |
2b326e7c KZ |
121 | } |
122 | ||
b4cbb7b8 KZ |
123 | static int |
124 | is_loopfd_autoclear(int fd) | |
e84feaec | 125 | { |
e84feaec | 126 | struct loop_info64 lo64; |
e84feaec KZ |
127 | |
128 | if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0) { | |
129 | if (lo64.lo_flags & LO_FLAGS_AUTOCLEAR) | |
b4cbb7b8 | 130 | return 1; |
e84feaec | 131 | |
e84feaec | 132 | } |
b4cbb7b8 KZ |
133 | return 0; |
134 | } | |
135 | ||
136 | int | |
137 | is_loop_autoclear(const char *device) | |
138 | { | |
139 | int fd, rc; | |
140 | ||
141 | if ((fd = open(device, O_RDONLY)) < 0) | |
142 | return 0; | |
143 | rc = is_loopfd_autoclear(fd); | |
e84feaec KZ |
144 | |
145 | close(fd); | |
146 | return rc; | |
147 | } | |
148 | ||
2b326e7c KZ |
149 | static int |
150 | looplist_open(struct looplist *ll, int flag) | |
151 | { | |
152 | struct stat st; | |
153 | ||
154 | memset(ll, 0, sizeof(*ll)); | |
155 | ll->flag = flag; | |
2b326e7c KZ |
156 | ll->ncur = -1; |
157 | ||
c74c0daf | 158 | if (stat(_PATH_DEV, &st) == -1 || (!S_ISDIR(st.st_mode))) |
2b326e7c KZ |
159 | return -1; /* /dev doesn't exist */ |
160 | ||
c74c0daf | 161 | if (stat(_PATH_DEV_LOOP, &st) == 0 && S_ISDIR(st.st_mode)) |
2b326e7c KZ |
162 | ll->flag |= LLFLG_SUBDIR; /* /dev/loop/ exists */ |
163 | ||
164 | if ((ll->flag & LLFLG_USEDONLY) && | |
edb68d0c KZ |
165 | stat(_PATH_PROC_PARTITIONS, &st) == 0) |
166 | ll->flag |= LLFLG_PROCFS; /* try /proc/partitions */ | |
2b326e7c KZ |
167 | |
168 | ll->flag |= LLFLG_DFLT; /* required! */ | |
169 | return 0; | |
170 | } | |
171 | ||
172 | static void | |
173 | looplist_close(struct looplist *ll) | |
174 | { | |
63754e71 | 175 | free(ll->minors); |
edb68d0c KZ |
176 | if (ll->proc) |
177 | fclose(ll->proc); | |
178 | ll->minors = NULL; | |
179 | ll->proc = NULL; | |
2b326e7c KZ |
180 | ll->ncur = -1; |
181 | ll->flag |= LLFLG_DONE; | |
182 | } | |
183 | ||
184 | static int | |
edb68d0c | 185 | looplist_open_dev(struct looplist *ll, int lnum) |
2b326e7c | 186 | { |
edb68d0c KZ |
187 | struct stat st; |
188 | int used; | |
189 | int fd; | |
190 | ||
191 | /* create a full device path */ | |
192 | snprintf(ll->name, sizeof(ll->name), | |
193 | ll->flag & LLFLG_SUBDIR ? | |
c74c0daf | 194 | _PATH_DEV_LOOP "/%d" : |
14b613f3 | 195 | _PATH_DEV "loop%d", |
edb68d0c | 196 | lnum); |
2b326e7c | 197 | |
edb68d0c KZ |
198 | fd = open(ll->name, O_RDONLY); |
199 | if (fd == -1) { | |
200 | if (errno == EACCES) | |
201 | ll->ct_perm++; | |
202 | return -1; | |
203 | } | |
204 | if (fstat(fd, &st) == -1) | |
205 | goto error; | |
206 | if (!S_ISBLK(st.st_mode) || major(st.st_rdev) != LOOPMAJOR) | |
207 | goto error; | |
208 | ||
209 | ll->ct_succ++; | |
210 | ||
211 | /* check if the device is wanted */ | |
2b326e7c | 212 | if (!(ll->flag & (LLFLG_USEDONLY | LLFLG_FREEONLY))) |
edb68d0c | 213 | return fd; |
2b326e7c | 214 | |
edb68d0c | 215 | used = is_loop_used(fd); |
2b326e7c | 216 | |
edb68d0c KZ |
217 | if ((ll->flag & LLFLG_USEDONLY) && used) |
218 | return fd; | |
219 | if ((ll->flag & LLFLG_FREEONLY) && !used) | |
220 | return fd; | |
221 | error: | |
222 | close(fd); | |
223 | return -1; | |
224 | } | |
225 | ||
226 | /* returns <N> from "loop<N>" */ | |
227 | static int | |
228 | name2minor(int hasprefix, const char *name) | |
229 | { | |
230 | int n; | |
231 | char *end; | |
232 | ||
233 | if (hasprefix) { | |
234 | if (strncmp(name, "loop", 4)) | |
235 | return -1; | |
236 | name += 4; | |
237 | } | |
238 | n = strtol(name, &end, 10); | |
239 | if (end && end != name && *end == '\0' && n >= 0) | |
240 | return n; | |
241 | return -1; | |
242 | } | |
243 | ||
244 | static int | |
245 | cmpnum(const void *p1, const void *p2) | |
246 | { | |
3c55d914 | 247 | return (*(int *) p1 > *(int *) p2) - (*(int *) p1 < *(int *) p2); |
edb68d0c KZ |
248 | } |
249 | ||
250 | /* | |
251 | * The classic scandir() is more expensive and less portable. | |
b642d0e0 | 252 | * We needn't full loop device names -- minor numbers (loop<N>) |
edb68d0c KZ |
253 | * are enough. |
254 | */ | |
255 | static int | |
256 | loop_scandir(const char *dirname, int **ary, int hasprefix) | |
257 | { | |
258 | DIR *dir; | |
259 | struct dirent *d; | |
260 | int n, count = 0, arylen = 0; | |
261 | ||
262 | if (!dirname || !ary) | |
263 | return -1; | |
264 | dir = opendir(dirname); | |
265 | if (!dir) | |
266 | return -1; | |
267 | ||
268 | *ary = NULL; | |
269 | ||
270 | while((d = readdir(dir))) { | |
72f6902e | 271 | if (d->d_type != DT_BLK && d->d_type != DT_UNKNOWN && d->d_type != DT_LNK) |
edb68d0c KZ |
272 | continue; |
273 | n = name2minor(hasprefix, d->d_name); | |
274 | if (n == -1 || n < NLOOPS_DEFAULT) | |
275 | continue; | |
276 | if (count + 1 > arylen) { | |
9695a7c6 TK |
277 | int *tmp; |
278 | ||
edb68d0c | 279 | arylen += 1; |
9695a7c6 TK |
280 | |
281 | tmp = realloc(*ary, arylen * sizeof(int)); | |
282 | if (!tmp) { | |
283 | free(*ary); | |
edb68d0c | 284 | return -1; |
9695a7c6 TK |
285 | } |
286 | *ary = tmp; | |
edb68d0c KZ |
287 | } |
288 | (*ary)[count++] = n; | |
289 | } | |
290 | if (count) | |
291 | qsort(*ary, count, sizeof(int), cmpnum); | |
292 | ||
293 | closedir(dir); | |
294 | return count; | |
2b326e7c KZ |
295 | } |
296 | ||
297 | static int | |
298 | looplist_next(struct looplist *ll) | |
299 | { | |
edb68d0c | 300 | int fd, n; |
2b326e7c KZ |
301 | |
302 | if (ll->flag & LLFLG_DONE) | |
303 | return -1; | |
304 | ||
edb68d0c | 305 | /* A) Look for used loop devices in /proc/partitions ("losetup -a" only) |
2b326e7c | 306 | */ |
edb68d0c KZ |
307 | if (ll->flag & LLFLG_PROCFS) { |
308 | char buf[BUFSIZ]; | |
2b326e7c | 309 | |
edb68d0c KZ |
310 | if (!ll->proc) |
311 | ll->proc = fopen(_PATH_PROC_PARTITIONS, "r"); | |
312 | ||
313 | while (ll->proc && fgets(buf, sizeof(buf), ll->proc)) { | |
314 | int m; | |
315 | unsigned long long sz; | |
316 | char name[128]; | |
317 | ||
318 | if (sscanf(buf, " %d %d %llu %128[^\n ]", | |
319 | &m, &n, &sz, name) != 4) | |
2b326e7c | 320 | continue; |
edb68d0c KZ |
321 | if (m != LOOPMAJOR) |
322 | continue; | |
65158b10 KZ |
323 | /* unfortunately, real minor numbers needn't to match |
324 | * loop<N> device name. We have to follow device name. | |
325 | */ | |
326 | n = name2minor(1, name); | |
edb68d0c KZ |
327 | fd = looplist_open_dev(ll, n); |
328 | if (fd != -1) | |
329 | return fd; | |
2b326e7c | 330 | } |
2b326e7c KZ |
331 | goto done; |
332 | } | |
333 | ||
edb68d0c | 334 | |
2b326e7c KZ |
335 | /* B) Classic way, try first eight loop devices (default number |
336 | * of loop devices). This is enough for 99% of all cases. | |
337 | */ | |
338 | if (ll->flag & LLFLG_DFLT) { | |
339 | for (++ll->ncur; ll->ncur < NLOOPS_DEFAULT; ll->ncur++) { | |
edb68d0c KZ |
340 | fd = looplist_open_dev(ll, ll->ncur); |
341 | if (fd != -1) | |
342 | return fd; | |
2b326e7c KZ |
343 | } |
344 | ll->flag &= ~LLFLG_DFLT; | |
2b326e7c KZ |
345 | } |
346 | ||
b642d0e0 | 347 | /* C) the worst possibility, scan all /dev or /dev/loop |
2b326e7c | 348 | */ |
edb68d0c KZ |
349 | if (!ll->minors) { |
350 | ll->nminors = (ll->flag & LLFLG_SUBDIR) ? | |
c74c0daf KZ |
351 | loop_scandir(_PATH_DEV_LOOP, &ll->minors, 0) : |
352 | loop_scandir(_PATH_DEV, &ll->minors, 1); | |
2b326e7c KZ |
353 | ll->ncur = -1; |
354 | } | |
edb68d0c KZ |
355 | for (++ll->ncur; ll->ncur < ll->nminors; ll->ncur++) { |
356 | fd = looplist_open_dev(ll, ll->minors[ll->ncur]); | |
357 | if (fd != -1) | |
358 | return fd; | |
2b326e7c | 359 | } |
edb68d0c | 360 | |
2b326e7c KZ |
361 | done: |
362 | looplist_close(ll); | |
363 | return -1; | |
364 | } | |
365 | ||
f4612577 PU |
366 | /* Find loop device associated with given @filename. Used for unmounting loop |
367 | * device specified by associated backing file. | |
368 | * | |
369 | * returns: 1 no such device/error | |
370 | * 2 more than one loop device associated with @filename | |
371 | * 0 exactly one loop device associated with @filename | |
372 | * (@loopdev points to string containing full device name) | |
373 | */ | |
374 | int | |
375 | find_loopdev_by_backing_file(const char *filename, char **loopdev) | |
376 | { | |
377 | struct looplist ll; | |
378 | struct stat filestat; | |
379 | int fd; | |
380 | int devs_n = 0; /* number of loop devices found */ | |
381 | char* devname = NULL; | |
382 | ||
383 | if (stat(filename, &filestat) == -1) { | |
384 | perror(filename); | |
385 | return 1; | |
386 | } | |
387 | ||
388 | if (looplist_open(&ll, LLFLG_USEDONLY) == -1) { | |
389 | error(_("%s: /dev directory does not exist."), progname); | |
390 | return 1; | |
391 | } | |
392 | ||
393 | while((devs_n < 2) && (fd = looplist_next(&ll)) != -1) { | |
394 | if (is_associated(fd, &filestat, 0, 0) == 1) { | |
395 | if (!devname) | |
396 | devname = xstrdup(ll.name); | |
397 | devs_n++; | |
398 | } | |
399 | close(fd); | |
400 | } | |
401 | looplist_close(&ll); | |
402 | ||
403 | if (devs_n == 1) { | |
404 | *loopdev = devname; | |
405 | return 0; /* exactly one loopdev */ | |
406 | } | |
407 | free(devname); | |
408 | return devs_n ? 2 : 1; /* more loopdevs or error */ | |
409 | } | |
410 | ||
d34ac93a KZ |
411 | static int |
412 | set_capacity(const char *device) | |
413 | { | |
414 | int errsv; | |
415 | int fd = open(device, O_RDONLY); | |
416 | ||
417 | if (fd == -1) | |
418 | goto err; | |
419 | ||
420 | if (ioctl(fd, LOOP_SET_CAPACITY) != 0) | |
421 | goto err; | |
422 | ||
423 | return 0; | |
424 | err: | |
425 | errsv = errno; | |
426 | fprintf(stderr, _("loop: can't set capacity on device %s: %s\n"), | |
427 | device, strerror (errsv)); | |
428 | if (fd != -1) | |
429 | close(fd); | |
430 | return 2; | |
431 | } | |
432 | ||
66ee8158 | 433 | static int |
2b326e7c | 434 | show_loop_fd(int fd, char *device) { |
d03dd608 | 435 | struct loop_info64 loopinfo64; |
2b326e7c | 436 | int errsv; |
d03dd608 KZ |
437 | |
438 | if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo64) == 0) { | |
439 | ||
b61e9390 KZ |
440 | char *lofile = NULL; |
441 | ||
d03dd608 KZ |
442 | loopinfo64.lo_file_name[LO_NAME_SIZE-2] = '*'; |
443 | loopinfo64.lo_file_name[LO_NAME_SIZE-1] = 0; | |
444 | loopinfo64.lo_crypt_name[LO_NAME_SIZE-1] = 0; | |
445 | ||
b61e9390 KZ |
446 | /* ioctl has limited buffer for backing file name, since |
447 | * kernel 2.6.37 the filename is available in sysfs too | |
448 | */ | |
449 | if (strlen((char *) loopinfo64.lo_file_name) == LO_NAME_SIZE - 1) | |
450 | lofile = loopfile_from_sysfs(device); | |
451 | if (!lofile) | |
452 | lofile = (char *) loopinfo64.lo_file_name; | |
453 | ||
7711bc17 | 454 | printf("%s: [%04" PRIx64 "]:%" PRIu64 " (%s)", |
d03dd608 | 455 | device, loopinfo64.lo_device, loopinfo64.lo_inode, |
b61e9390 KZ |
456 | lofile); |
457 | ||
458 | if (lofile != (char *) loopinfo64.lo_file_name) | |
459 | free(lofile); | |
d03dd608 KZ |
460 | |
461 | if (loopinfo64.lo_offset) | |
7711bc17 | 462 | printf(_(", offset %" PRIu64 ), loopinfo64.lo_offset); |
d03dd608 KZ |
463 | |
464 | if (loopinfo64.lo_sizelimit) | |
7711bc17 | 465 | printf(_(", sizelimit %" PRIu64 ), loopinfo64.lo_sizelimit); |
d03dd608 KZ |
466 | |
467 | if (loopinfo64.lo_encrypt_type || | |
468 | loopinfo64.lo_crypt_name[0]) { | |
830d6af0 | 469 | char *e = (char *)loopinfo64.lo_crypt_name; |
d03dd608 KZ |
470 | |
471 | if (*e == 0 && loopinfo64.lo_encrypt_type == 1) | |
472 | e = "XOR"; | |
7711bc17 | 473 | printf(_(", encryption %s (type %" PRIu32 ")"), |
d03dd608 KZ |
474 | e, loopinfo64.lo_encrypt_type); |
475 | } | |
476 | printf("\n"); | |
d03dd608 | 477 | return 0; |
df1dddf9 | 478 | } |
a21409f5 | 479 | |
d03dd608 KZ |
480 | errsv = errno; |
481 | fprintf(stderr, _("loop: can't get info on device %s: %s\n"), | |
482 | device, strerror (errsv)); | |
d03dd608 | 483 | return 1; |
6dbe3af9 | 484 | } |
8b125fae | 485 | |
2b326e7c KZ |
486 | static int |
487 | show_loop(char *device) { | |
488 | int ret, fd; | |
489 | ||
490 | if ((fd = open(device, O_RDONLY)) < 0) { | |
491 | int errsv = errno; | |
492 | fprintf(stderr, _("loop: can't open device %s: %s\n"), | |
493 | device, strerror (errsv)); | |
494 | return 2; | |
495 | } | |
496 | ret = show_loop_fd(fd, device); | |
497 | close(fd); | |
498 | return ret; | |
499 | } | |
500 | ||
501 | ||
8b125fae KZ |
502 | static int |
503 | show_used_loop_devices (void) { | |
2b326e7c KZ |
504 | struct looplist ll; |
505 | int fd; | |
8b125fae | 506 | |
2b326e7c KZ |
507 | if (looplist_open(&ll, LLFLG_USEDONLY) == -1) { |
508 | error(_("%s: /dev directory does not exist."), progname); | |
509 | return 1; | |
510 | } | |
511 | ||
512 | while((fd = looplist_next(&ll)) != -1) { | |
513 | show_loop_fd(fd, ll.name); | |
514 | close(fd); | |
8b125fae | 515 | } |
2b326e7c | 516 | looplist_close(&ll); |
8b125fae | 517 | |
edb68d0c KZ |
518 | if (!ll.ct_succ && ll.ct_perm) { |
519 | error(_("%s: no permission to look at /dev/loop%s<N>"), progname, | |
520 | (ll.flag & LLFLG_SUBDIR) ? "/" : ""); | |
8b125fae KZ |
521 | return 1; |
522 | } | |
523 | return 0; | |
524 | } | |
525 | ||
259fcc57 KZ |
526 | /* list all associated loop devices */ |
527 | static int | |
528 | show_associated_loop_devices(char *filename, unsigned long long offset, int isoff) | |
529 | { | |
530 | struct looplist ll; | |
531 | struct stat filestat; | |
532 | int fd; | |
533 | ||
534 | if (stat(filename, &filestat) == -1) { | |
535 | perror(filename); | |
536 | return 1; | |
537 | } | |
538 | ||
539 | if (looplist_open(&ll, LLFLG_USEDONLY) == -1) { | |
540 | error(_("%s: /dev directory does not exist."), progname); | |
541 | return 1; | |
542 | } | |
543 | ||
544 | while((fd = looplist_next(&ll)) != -1) { | |
545 | if (is_associated(fd, &filestat, offset, isoff) == 1) | |
546 | show_loop_fd(fd, ll.name); | |
547 | close(fd); | |
548 | } | |
549 | looplist_close(&ll); | |
550 | ||
551 | return 0; | |
552 | } | |
553 | ||
23680772 KZ |
554 | /* check if the loopfile is already associated with the same given |
555 | * parameters. | |
556 | * | |
26d51308 | 557 | * returns: 0 unused / error |
23680772 KZ |
558 | * 1 loop device already used |
559 | */ | |
560 | static int | |
259fcc57 | 561 | is_associated(int dev, struct stat *file, unsigned long long offset, int isoff) |
23680772 KZ |
562 | { |
563 | struct loop_info64 linfo64; | |
23680772 KZ |
564 | int ret = 0; |
565 | ||
566 | if (ioctl(dev, LOOP_GET_STATUS64, &linfo64) == 0) { | |
567 | if (file->st_dev == linfo64.lo_device && | |
568 | file->st_ino == linfo64.lo_inode && | |
259fcc57 | 569 | (isoff == 0 || offset == linfo64.lo_offset)) |
23680772 | 570 | ret = 1; |
26d51308 | 571 | |
23680772 KZ |
572 | } |
573 | ||
26d51308 | 574 | return ret; |
23680772 KZ |
575 | } |
576 | ||
577 | /* check if the loop file is already used with the same given | |
578 | * parameters. We check for device no, inode and offset. | |
579 | * returns: associated devname or NULL | |
580 | */ | |
581 | char * | |
582 | loopfile_used (const char *filename, unsigned long long offset) { | |
2b326e7c KZ |
583 | struct looplist ll; |
584 | char *devname = NULL; | |
585 | struct stat filestat; | |
586 | int fd; | |
23680772 KZ |
587 | |
588 | if (stat(filename, &filestat) == -1) { | |
589 | perror(filename); | |
590 | return NULL; | |
591 | } | |
592 | ||
2b326e7c KZ |
593 | if (looplist_open(&ll, LLFLG_USEDONLY) == -1) { |
594 | error(_("%s: /dev directory does not exist."), progname); | |
595 | return NULL; | |
596 | } | |
23680772 | 597 | |
2b326e7c | 598 | while((fd = looplist_next(&ll)) != -1) { |
259fcc57 | 599 | int res = is_associated(fd, &filestat, offset, 1); |
2b326e7c KZ |
600 | close(fd); |
601 | if (res == 1) { | |
602 | devname = xstrdup(ll.name); | |
603 | break; | |
23680772 | 604 | } |
23680772 | 605 | } |
2b326e7c KZ |
606 | looplist_close(&ll); |
607 | ||
608 | return devname; | |
23680772 KZ |
609 | } |
610 | ||
611 | int | |
612 | loopfile_used_with(char *devname, const char *filename, unsigned long long offset) | |
613 | { | |
614 | struct stat statbuf; | |
615 | int fd, ret; | |
616 | ||
617 | if (!is_loop_device(devname)) | |
618 | return 0; | |
619 | ||
26d51308 KZ |
620 | if (stat(filename, &statbuf) == -1) |
621 | return 0; | |
23680772 KZ |
622 | |
623 | fd = open(devname, O_RDONLY); | |
26d51308 KZ |
624 | if (fd == -1) |
625 | return 0; | |
23680772 | 626 | |
26d51308 | 627 | ret = is_associated(fd, &statbuf, offset, 1); |
23680772 KZ |
628 | close(fd); |
629 | return ret; | |
630 | } | |
631 | ||
fd6b7a7f | 632 | char * |
22853e4a | 633 | find_unused_loop_device (void) { |
2b326e7c KZ |
634 | struct looplist ll; |
635 | char *devname = NULL; | |
636 | int fd; | |
22853e4a | 637 | |
2b326e7c KZ |
638 | if (looplist_open(&ll, LLFLG_FREEONLY) == -1) { |
639 | error(_("%s: /dev directory does not exist."), progname); | |
640 | return NULL; | |
641 | } | |
b22550fa | 642 | |
2b326e7c KZ |
643 | if ((fd = looplist_next(&ll)) != -1) { |
644 | close(fd); | |
645 | devname = xstrdup(ll.name); | |
fd6b7a7f | 646 | } |
2b326e7c KZ |
647 | looplist_close(&ll); |
648 | if (devname) | |
649 | return devname; | |
22853e4a | 650 | |
edb68d0c KZ |
651 | if (!ll.ct_succ && ll.ct_perm) |
652 | error(_("%s: no permission to look at /dev/loop%s<N>"), progname, | |
653 | (ll.flag & LLFLG_SUBDIR) ? "/" : ""); | |
2b326e7c KZ |
654 | else if (ll.ct_succ) |
655 | error(_("%s: could not find any free loop device"), progname); | |
656 | else | |
22853e4a | 657 | error(_( |
d162fcb5 | 658 | "%s: Could not find any loop device. Maybe this kernel " |
d03dd608 KZ |
659 | "does not know\n" |
660 | " about the loop device? (If so, recompile or " | |
d162fcb5 | 661 | "`modprobe loop'.)"), progname); |
2b326e7c | 662 | return NULL; |
fd6b7a7f | 663 | } |
6dbe3af9 | 664 | |
d03dd608 KZ |
665 | /* |
666 | * A function to read the passphrase either from the terminal or from | |
667 | * an open file descriptor. | |
668 | */ | |
669 | static char * | |
670 | xgetpass(int pfd, const char *prompt) { | |
671 | char *pass; | |
672 | int buflen, i; | |
673 | ||
674 | if (pfd < 0) /* terminal */ | |
675 | return getpass(prompt); | |
676 | ||
677 | pass = NULL; | |
678 | buflen = 0; | |
679 | for (i=0; ; i++) { | |
680 | if (i >= buflen-1) { | |
681 | /* we're running out of space in the buffer. | |
682 | * Make it bigger: */ | |
683 | char *tmppass = pass; | |
684 | buflen += 128; | |
685 | pass = realloc(tmppass, buflen); | |
686 | if (pass == NULL) { | |
687 | /* realloc failed. Stop reading. */ | |
5bbba4a5 | 688 | error(_("Out of memory while reading passphrase")); |
d03dd608 KZ |
689 | pass = tmppass; /* the old buffer hasn't changed */ |
690 | break; | |
691 | } | |
692 | } | |
d162fcb5 KZ |
693 | if (read(pfd, pass+i, 1) != 1 || |
694 | pass[i] == '\n' || pass[i] == 0) | |
d03dd608 KZ |
695 | break; |
696 | } | |
d162fcb5 | 697 | |
d03dd608 KZ |
698 | if (pass == NULL) |
699 | return ""; | |
d162fcb5 KZ |
700 | |
701 | pass[i] = 0; | |
702 | return pass; | |
d03dd608 KZ |
703 | } |
704 | ||
705 | static int | |
706 | digits_only(const char *s) { | |
707 | while (*s) | |
708 | if (!isdigit(*s++)) | |
709 | return 0; | |
710 | return 1; | |
711 | } | |
712 | ||
af17e0d1 KZ |
713 | /* |
714 | * return codes: | |
715 | * 0 - success | |
716 | * 1 - error | |
717 | * 2 - error (EBUSY) | |
718 | */ | |
6dbe3af9 | 719 | int |
c129767e | 720 | set_loop(const char *device, const char *file, unsigned long long offset, |
7bcefc7f | 721 | unsigned long long sizelimit, const char *encryption, int pfd, int *options) { |
d03dd608 | 722 | struct loop_info64 loopinfo64; |
756bfd01 | 723 | int fd, ffd, mode, i; |
22853e4a | 724 | char *pass; |
bfdb8be5 | 725 | char *filename; |
22853e4a | 726 | |
23680772 KZ |
727 | if (verbose) { |
728 | char *xdev = loopfile_used(file, offset); | |
729 | ||
730 | if (xdev) { | |
731 | printf(_("warning: %s is already associated with %s\n"), | |
732 | file, xdev); | |
733 | free(xdev); | |
734 | } | |
735 | } | |
736 | ||
024e1a4f | 737 | mode = (*options & SETLOOP_RDONLY) ? O_RDONLY : O_RDWR; |
d03dd608 | 738 | if ((ffd = open(file, mode)) < 0) { |
bc984ef9 MK |
739 | if (!(*options & SETLOOP_RDONLY) && |
740 | (errno == EROFS || errno == EACCES)) | |
d03dd608 | 741 | ffd = open(file, mode = O_RDONLY); |
22853e4a | 742 | if (ffd < 0) { |
d03dd608 | 743 | perror(file); |
22853e4a KZ |
744 | return 1; |
745 | } | |
c268327e KZ |
746 | if (verbose) |
747 | printf(_("warning: %s: is write-protected, using read-only.\n"), | |
748 | file); | |
024e1a4f | 749 | *options |= SETLOOP_RDONLY; |
22853e4a | 750 | } |
d03dd608 | 751 | if ((fd = open(device, mode)) < 0) { |
22853e4a | 752 | perror (device); |
f7858f66 | 753 | close(ffd); |
22853e4a KZ |
754 | return 1; |
755 | } | |
d03dd608 KZ |
756 | memset(&loopinfo64, 0, sizeof(loopinfo64)); |
757 | ||
bfdb8be5 KZ |
758 | if (!(filename = canonicalize(file))) |
759 | filename = (char *) file; | |
760 | xstrncpy((char *)loopinfo64.lo_file_name, filename, LO_NAME_SIZE); | |
d03dd608 KZ |
761 | |
762 | if (encryption && *encryption) { | |
763 | if (digits_only(encryption)) { | |
764 | loopinfo64.lo_encrypt_type = atoi(encryption); | |
765 | } else { | |
766 | loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI; | |
830d6af0 | 767 | snprintf((char *)loopinfo64.lo_crypt_name, LO_NAME_SIZE, |
d03dd608 KZ |
768 | "%s", encryption); |
769 | } | |
22853e4a | 770 | } |
d03dd608 KZ |
771 | |
772 | loopinfo64.lo_offset = offset; | |
7bcefc7f | 773 | loopinfo64.lo_sizelimit = sizelimit; |
22853e4a | 774 | |
d33279c2 | 775 | #ifdef MCL_FUTURE |
22853e4a KZ |
776 | /* |
777 | * Oh-oh, sensitive data coming up. Better lock into memory to prevent | |
778 | * passwd etc being swapped out and left somewhere on disk. | |
779 | */ | |
d33279c2 MY |
780 | if (loopinfo64.lo_encrypt_type != LO_CRYPT_NONE) { |
781 | if(mlockall(MCL_CURRENT | MCL_FUTURE)) { | |
782 | perror("memlock"); | |
783 | fprintf(stderr, _("Couldn't lock into memory, exiting.\n")); | |
784 | exit(1); | |
785 | } | |
22853e4a KZ |
786 | } |
787 | #endif | |
788 | ||
d03dd608 | 789 | switch (loopinfo64.lo_encrypt_type) { |
22853e4a | 790 | case LO_CRYPT_NONE: |
d03dd608 | 791 | loopinfo64.lo_encrypt_key_size = 0; |
22853e4a KZ |
792 | break; |
793 | case LO_CRYPT_XOR: | |
d03dd608 | 794 | pass = getpass(_("Password: ")); |
d162fcb5 | 795 | goto gotpass; |
22853e4a | 796 | default: |
d03dd608 | 797 | pass = xgetpass(pfd, _("Password: ")); |
d162fcb5 | 798 | gotpass: |
756bfd01 | 799 | memset(loopinfo64.lo_encrypt_key, 0, LO_KEY_SIZE); |
830d6af0 | 800 | xstrncpy((char *)loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE); |
756bfd01 KZ |
801 | memset(pass, 0, strlen(pass)); |
802 | loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE; | |
22853e4a | 803 | } |
d03dd608 KZ |
804 | |
805 | if (ioctl(fd, LOOP_SET_FD, ffd) < 0) { | |
0e7cd33e KZ |
806 | int rc = 1; |
807 | ||
95ba33f7 MK |
808 | if (errno == EBUSY) { |
809 | if (verbose) | |
960cf573 | 810 | printf(_("ioctl LOOP_SET_FD failed: %m\n")); |
0e7cd33e KZ |
811 | rc = 2; |
812 | } else | |
95ba33f7 | 813 | perror("ioctl: LOOP_SET_FD"); |
0e7cd33e KZ |
814 | |
815 | close(fd); | |
816 | close(ffd); | |
bfdb8be5 KZ |
817 | if (file != filename) |
818 | free(filename); | |
0e7cd33e | 819 | return rc; |
22853e4a | 820 | } |
d03dd608 KZ |
821 | close (ffd); |
822 | ||
024e1a4f BI |
823 | if (*options & SETLOOP_AUTOCLEAR) |
824 | loopinfo64.lo_flags = LO_FLAGS_AUTOCLEAR; | |
825 | ||
d26aa358 | 826 | i = ioctl(fd, LOOP_SET_STATUS64, &loopinfo64); |
846985f7 KZ |
827 | if (i) |
828 | perror("ioctl: LOOP_SET_STATUS64"); | |
d03dd608 | 829 | |
b4cbb7b8 KZ |
830 | if ((*options & SETLOOP_AUTOCLEAR) && !is_loopfd_autoclear(fd)) |
831 | /* kernel doesn't support loop auto-destruction */ | |
832 | *options &= ~SETLOOP_AUTOCLEAR; | |
833 | ||
834 | memset(&loopinfo64, 0, sizeof(loopinfo64)); | |
024e1a4f | 835 | |
d26aa358 KZ |
836 | if (i) { |
837 | ioctl (fd, LOOP_CLR_FD, 0); | |
838 | close (fd); | |
bfdb8be5 KZ |
839 | if (file != filename) |
840 | free(filename); | |
d26aa358 KZ |
841 | return 1; |
842 | } | |
024e1a4f BI |
843 | |
844 | /* | |
b642d0e0 | 845 | * HACK: here we're leaking a file descriptor, |
024e1a4f BI |
846 | * but mount is a short-lived process anyway. |
847 | */ | |
848 | if (!(*options & SETLOOP_AUTOCLEAR)) | |
849 | close (fd); | |
d26aa358 | 850 | |
c6455a94 | 851 | if (verbose) |
7bcefc7f SS |
852 | printf(_("set_loop(%s,%s,%llu,%llu): success\n"), |
853 | device, filename, offset, sizelimit); | |
bfdb8be5 KZ |
854 | if (file != filename) |
855 | free(filename); | |
22853e4a | 856 | return 0; |
6dbe3af9 KZ |
857 | } |
858 | ||
34f9b684 PU |
859 | static int |
860 | delete_all_devices (void) | |
861 | { | |
862 | struct looplist ll; | |
863 | int fd; | |
864 | int ok = 0; | |
865 | ||
866 | if (looplist_open(&ll, LLFLG_USEDONLY) == -1) { | |
867 | error(_("%s: /dev directory does not exist."), progname); | |
868 | return 1; | |
869 | } | |
870 | ||
871 | while((fd = looplist_next(&ll)) != -1) { | |
872 | close(fd); | |
873 | ok |= del_loop(ll.name); | |
874 | } | |
875 | looplist_close(&ll); | |
876 | ||
877 | if (!ll.ct_succ && ll.ct_perm) { | |
878 | error(_("%s: no permission to look at /dev/loop%s<N>"), progname, | |
879 | (ll.flag & LLFLG_SUBDIR) ? "/" : ""); | |
880 | return 1; | |
881 | } | |
882 | return ok; | |
883 | } | |
884 | ||
0e7cd33e | 885 | int |
22853e4a | 886 | del_loop (const char *device) { |
27d320d6 | 887 | int fd, errsv; |
22853e4a KZ |
888 | |
889 | if ((fd = open (device, O_RDONLY)) < 0) { | |
27d320d6 KZ |
890 | errsv = errno; |
891 | goto error; | |
22853e4a KZ |
892 | } |
893 | if (ioctl (fd, LOOP_CLR_FD, 0) < 0) { | |
27d320d6 KZ |
894 | errsv = errno; |
895 | goto error; | |
22853e4a KZ |
896 | } |
897 | close (fd); | |
c6455a94 | 898 | if (verbose) |
22853e4a KZ |
899 | printf(_("del_loop(%s): success\n"), device); |
900 | return 0; | |
27d320d6 KZ |
901 | |
902 | error: | |
903 | fprintf(stderr, _("loop: can't delete device %s: %s\n"), | |
904 | device, strerror(errsv)); | |
905 | if (fd >= 0) | |
906 | close(fd); | |
907 | return 1; | |
6dbe3af9 KZ |
908 | } |
909 | ||
22853e4a | 910 | static void |
6997468e KZ |
911 | usage(FILE *out) { |
912 | ||
913 | fputs(_("\nUsage:\n"), out); | |
914 | fprintf(out, | |
915 | _(" %1$s loop_device give info\n" | |
916 | " %1$s -a | --all list all used\n" | |
917 | " %1$s -d | --detach <loopdev> [<loopdev> ...] delete\n" | |
34f9b684 | 918 | " %1$s -D | --detach-all delete all used\n" |
6997468e KZ |
919 | " %1$s -f | --find find unused\n" |
920 | " %1$s -c | --set-capacity <loopdev> resize\n" | |
921 | " %1$s -j | --associated <file> [-o <num>] list all associated with <file>\n" | |
922 | " %1$s [options] {-f|--find|loopdev} <file> setup\n"), | |
923 | progname); | |
924 | ||
925 | fputs(_("\nOptions:\n"), out); | |
926 | fputs(_(" -e, --encryption <type> enable data encryption with specified <name/num>\n" | |
927 | " -h, --help this help\n" | |
928 | " -o, --offset <num> start at offset <num> into file\n" | |
929 | " --sizelimit <num> loop limited to only <num> bytes of the file\n" | |
930 | " -p, --pass-fd <num> read passphrase from file descriptor <num>\n" | |
931 | " -r, --read-only setup read-only loop device\n" | |
932 | " --show print device name (with -f <file>)\n" | |
933 | " -v, --verbose verbose mode\n\n"), out); | |
934 | ||
935 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
d99f0140 | 936 | } |
22853e4a | 937 | |
22853e4a KZ |
938 | int |
939 | main(int argc, char **argv) { | |
7bcefc7f | 940 | char *p, *offset, *sizelimit, *encryption, *passfd, *device, *file, *assoc; |
34f9b684 | 941 | int delete, delete_all, find, c, all, capacity; |
22853e4a | 942 | int res = 0; |
ba3809b0 | 943 | int showdev = 0; |
22853e4a | 944 | int ro = 0; |
d03dd608 | 945 | int pfd = -1; |
ca1e1363 | 946 | uintmax_t off = 0, slimit = 0; |
6c7d5ae9 KZ |
947 | |
948 | static const struct option longopts[] = { | |
d99f0140 | 949 | { "all", 0, 0, 'a' }, |
d34ac93a | 950 | { "set-capacity", 0, 0, 'c' }, |
d99f0140 | 951 | { "detach", 0, 0, 'd' }, |
34f9b684 | 952 | { "detach-all", 0, 0, 'D' }, |
d99f0140 KZ |
953 | { "encryption", 1, 0, 'e' }, |
954 | { "find", 0, 0, 'f' }, | |
955 | { "help", 0, 0, 'h' }, | |
259fcc57 | 956 | { "associated", 1, 0, 'j' }, |
d99f0140 | 957 | { "offset", 1, 0, 'o' }, |
7bcefc7f | 958 | { "sizelimit", 1, 0, 128 }, |
d99f0140 KZ |
959 | { "pass-fd", 1, 0, 'p' }, |
960 | { "read-only", 0, 0, 'r' }, | |
961 | { "show", 0, 0, 's' }, | |
962 | { "verbose", 0, 0, 'v' }, | |
963 | { NULL, 0, 0, 0 } | |
964 | }; | |
22853e4a KZ |
965 | |
966 | setlocale(LC_ALL, ""); | |
967 | bindtextdomain(PACKAGE, LOCALEDIR); | |
968 | textdomain(PACKAGE); | |
969 | ||
34f9b684 | 970 | capacity = delete = delete_all = find = all = 0; |
7bcefc7f | 971 | assoc = offset = sizelimit = encryption = passfd = NULL; |
d162fcb5 | 972 | |
22853e4a | 973 | progname = argv[0]; |
d162fcb5 KZ |
974 | if ((p = strrchr(progname, '/')) != NULL) |
975 | progname = p+1; | |
976 | ||
34f9b684 | 977 | while ((c = getopt_long(argc, argv, "acdDe:E:fhj:o:p:rsv", |
d99f0140 | 978 | longopts, NULL)) != -1) { |
22853e4a | 979 | switch (c) { |
8b125fae KZ |
980 | case 'a': |
981 | all = 1; | |
982 | break; | |
d34ac93a KZ |
983 | case 'c': |
984 | capacity = 1; | |
985 | break; | |
faf142b6 KZ |
986 | case 'r': |
987 | ro = 1; | |
988 | break; | |
22853e4a KZ |
989 | case 'd': |
990 | delete = 1; | |
991 | break; | |
34f9b684 PU |
992 | case 'D': |
993 | delete_all = 1; | |
994 | break; | |
d03dd608 | 995 | case 'E': |
22853e4a KZ |
996 | case 'e': |
997 | encryption = optarg; | |
998 | break; | |
d162fcb5 KZ |
999 | case 'f': |
1000 | find = 1; | |
1001 | break; | |
108591d2 KZ |
1002 | case 'h': |
1003 | usage(stdout); | |
1004 | break; | |
259fcc57 KZ |
1005 | case 'j': |
1006 | assoc = optarg; | |
1007 | break; | |
22853e4a KZ |
1008 | case 'o': |
1009 | offset = optarg; | |
1010 | break; | |
d03dd608 KZ |
1011 | case 'p': |
1012 | passfd = optarg; | |
1013 | break; | |
ba3809b0 KZ |
1014 | case 's': |
1015 | showdev = 1; | |
1016 | break; | |
22853e4a KZ |
1017 | case 'v': |
1018 | verbose = 1; | |
1019 | break; | |
7bcefc7f SS |
1020 | |
1021 | case 128: /* --sizelimit */ | |
1022 | sizelimit = optarg; | |
1023 | break; | |
1024 | ||
22853e4a | 1025 | default: |
108591d2 | 1026 | usage(stderr); |
22853e4a KZ |
1027 | } |
1028 | } | |
d162fcb5 KZ |
1029 | |
1030 | if (argc == 1) { | |
108591d2 | 1031 | usage(stderr); |
d162fcb5 | 1032 | } else if (delete) { |
27d320d6 | 1033 | if (argc < optind+1 || encryption || offset || sizelimit || |
d34ac93a | 1034 | capacity || find || all || showdev || assoc || ro) |
108591d2 | 1035 | usage(stderr); |
34f9b684 PU |
1036 | } else if (delete_all) { |
1037 | if (argc > optind || encryption || offset || sizelimit || | |
1038 | capacity || find || all || showdev || assoc || ro) | |
1039 | usage(stderr); | |
d162fcb5 | 1040 | } else if (find) { |
d34ac93a | 1041 | if (capacity || all || assoc || argc < optind || argc > optind+1) |
108591d2 | 1042 | usage(stderr); |
34f9b684 | 1043 | } else if (all || delete_all) { |
b71e4c30 KZ |
1044 | /* only -v is allowed */ |
1045 | if ((argc == 3 && verbose == 0) || argc > 3) | |
108591d2 | 1046 | usage(stderr); |
259fcc57 | 1047 | } else if (assoc) { |
d34ac93a | 1048 | if (capacity || encryption || showdev || passfd || ro) |
108591d2 | 1049 | usage(stderr); |
d34ac93a KZ |
1050 | } else if (capacity) { |
1051 | if (argc != optind + 1 || encryption || offset || sizelimit || | |
1052 | showdev || ro) | |
108591d2 | 1053 | usage(stderr); |
d162fcb5 KZ |
1054 | } else { |
1055 | if (argc < optind+1 || argc > optind+2) | |
108591d2 | 1056 | usage(stderr); |
d162fcb5 KZ |
1057 | } |
1058 | ||
ca1e1363 KZ |
1059 | if (offset && strtosize(offset, &off)) { |
1060 | error(_("%s: invalid offset '%s' specified"), progname, offset); | |
108591d2 | 1061 | usage(stderr); |
ca1e1363 KZ |
1062 | } |
1063 | if (sizelimit && strtosize(sizelimit, &slimit)) { | |
1064 | error(_("%s: invalid sizelimit '%s' specified"), | |
1065 | progname, sizelimit); | |
108591d2 | 1066 | usage(stderr); |
ca1e1363 | 1067 | } |
7bcefc7f | 1068 | |
8b125fae KZ |
1069 | if (all) |
1070 | return show_used_loop_devices(); | |
34f9b684 PU |
1071 | else if (delete_all) |
1072 | return delete_all_devices(); | |
259fcc57 KZ |
1073 | else if (assoc) |
1074 | return show_associated_loop_devices(assoc, off, offset ? 1 : 0); | |
8b125fae | 1075 | else if (find) { |
d162fcb5 KZ |
1076 | device = find_unused_loop_device(); |
1077 | if (device == NULL) | |
1078 | return -1; | |
d162fcb5 | 1079 | if (argc == optind) { |
6ffa7c93 | 1080 | if (verbose) |
5bbba4a5 | 1081 | printf(_("Loop device is %s\n"), device); |
d162fcb5 KZ |
1082 | printf("%s\n", device); |
1083 | return 0; | |
1084 | } | |
1085 | file = argv[optind]; | |
27d320d6 | 1086 | } else if (!delete) { |
d162fcb5 KZ |
1087 | device = argv[optind]; |
1088 | if (argc == optind+1) | |
1089 | file = NULL; | |
1090 | else | |
1091 | file = argv[optind+1]; | |
1092 | } | |
1093 | ||
27d320d6 KZ |
1094 | if (delete) { |
1095 | while (optind < argc) | |
1096 | res += del_loop(argv[optind++]); | |
d34ac93a KZ |
1097 | } else if (capacity) { |
1098 | res = set_capacity(device); | |
27d320d6 | 1099 | } else if (file == NULL) |
d162fcb5 KZ |
1100 | res = show_loop(device); |
1101 | else { | |
c129767e | 1102 | if (passfd && sscanf(passfd, "%d", &pfd) != 1) |
108591d2 | 1103 | usage(stderr); |
6ffa7c93 | 1104 | do { |
7bcefc7f | 1105 | res = set_loop(device, file, off, slimit, encryption, pfd, &ro); |
6ffa7c93 MK |
1106 | if (res == 2 && find) { |
1107 | if (verbose) | |
5bbba4a5 | 1108 | printf(_("stolen loop=%s...trying again\n"), |
6ffa7c93 MK |
1109 | device); |
1110 | free(device); | |
1111 | if (!(device = find_unused_loop_device())) | |
1112 | return -1; | |
1113 | } | |
1114 | } while (find && res == 2); | |
1115 | ||
af17e0d1 KZ |
1116 | if (device) { |
1117 | if (res == 2) | |
1118 | error(_("%s: %s: device is busy"), progname, device); | |
1119 | else if (res == 0) { | |
1120 | if (verbose) | |
5bbba4a5 | 1121 | printf(_("Loop device is %s\n"), device); |
af17e0d1 KZ |
1122 | if (showdev && find) |
1123 | printf("%s\n", device); | |
1124 | } | |
1125 | } | |
22853e4a KZ |
1126 | } |
1127 | return res; | |
1128 | } | |
1129 |