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