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