Added
Link Here
|
1 |
/* Originally from Ted's losetup.c */ |
2 |
|
3 |
#define LOOPMAJOR 7 |
4 |
|
5 |
/* |
6 |
* losetup.c - setup and control loop devices |
7 |
*/ |
8 |
|
9 |
#include <stdio.h> |
10 |
#include <string.h> |
11 |
#include <ctype.h> |
12 |
#include <fcntl.h> |
13 |
#include <errno.h> |
14 |
#include <stdlib.h> |
15 |
#include <unistd.h> |
16 |
#include <sys/ioctl.h> |
17 |
#include <sys/stat.h> |
18 |
#include <sys/mman.h> |
19 |
#include <sys/sysmacros.h> |
20 |
#include <string.h> |
21 |
|
22 |
#include "loop.h" |
23 |
|
24 |
extern int verbose; |
25 |
extern char *progname; |
26 |
extern char *xstrdup (const char *s); /* not: #include "sundries.h" */ |
27 |
extern void error (const char *fmt, ...); /* idem */ |
28 |
|
29 |
/* caller guarantees n > 0 */ |
30 |
void |
31 |
xstrncpy(char *dest, const char *src, size_t n) { |
32 |
strncpy(dest, src, n-1); |
33 |
dest[n-1] = 0; |
34 |
} |
35 |
|
36 |
|
37 |
static int |
38 |
loop_info64_to_old(const struct loop_info64 *info64, struct loop_info *info) |
39 |
{ |
40 |
memset(info, 0, sizeof(*info)); |
41 |
info->lo_number = info64->lo_number; |
42 |
info->lo_device = info64->lo_device; |
43 |
info->lo_inode = info64->lo_inode; |
44 |
info->lo_rdevice = info64->lo_rdevice; |
45 |
info->lo_offset = info64->lo_offset; |
46 |
info->lo_encrypt_type = info64->lo_encrypt_type; |
47 |
info->lo_encrypt_key_size = info64->lo_encrypt_key_size; |
48 |
info->lo_flags = info64->lo_flags; |
49 |
info->lo_init[0] = info64->lo_init[0]; |
50 |
info->lo_init[1] = info64->lo_init[1]; |
51 |
if (info->lo_encrypt_type == LO_CRYPT_CRYPTOAPI) |
52 |
memcpy(info->lo_name, info64->lo_crypt_name, LO_NAME_SIZE); |
53 |
else |
54 |
memcpy(info->lo_name, info64->lo_file_name, LO_NAME_SIZE); |
55 |
memcpy(info->lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE); |
56 |
|
57 |
/* error in case values were truncated */ |
58 |
if (info->lo_device != info64->lo_device || |
59 |
info->lo_rdevice != info64->lo_rdevice || |
60 |
info->lo_inode != info64->lo_inode || |
61 |
info->lo_offset != info64->lo_offset) |
62 |
return -EOVERFLOW; |
63 |
|
64 |
return 0; |
65 |
} |
66 |
|
67 |
|
68 |
static int |
69 |
show_loop(char *device) { |
70 |
struct loop_info loopinfo; |
71 |
struct loop_info64 loopinfo64; |
72 |
int fd, errsv; |
73 |
|
74 |
if ((fd = open(device, O_RDONLY)) < 0) { |
75 |
int errsv = errno; |
76 |
fprintf(stderr, "loop: can't open device %s: %s\n", |
77 |
device, strerror (errsv)); |
78 |
return 2; |
79 |
} |
80 |
|
81 |
if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo64) == 0) { |
82 |
|
83 |
loopinfo64.lo_file_name[LO_NAME_SIZE-2] = '*'; |
84 |
loopinfo64.lo_file_name[LO_NAME_SIZE-1] = 0; |
85 |
loopinfo64.lo_crypt_name[LO_NAME_SIZE-1] = 0; |
86 |
|
87 |
printf("%s: [%04llx]:%llu (%s)", |
88 |
device, loopinfo64.lo_device, loopinfo64.lo_inode, |
89 |
loopinfo64.lo_file_name); |
90 |
|
91 |
if (loopinfo64.lo_offset) |
92 |
printf(", offset %lld", loopinfo64.lo_offset); |
93 |
|
94 |
if (loopinfo64.lo_sizelimit) |
95 |
printf(", sizelimit %lld", loopinfo64.lo_sizelimit); |
96 |
|
97 |
if (loopinfo64.lo_encrypt_type || |
98 |
loopinfo64.lo_crypt_name[0]) { |
99 |
char *e = loopinfo64.lo_crypt_name; |
100 |
|
101 |
if (*e == 0 && loopinfo64.lo_encrypt_type == 1) |
102 |
e = "XOR"; |
103 |
printf(", encryption %s (type %d)", |
104 |
e, loopinfo64.lo_encrypt_type); |
105 |
} |
106 |
printf("\n"); |
107 |
close (fd); |
108 |
return 0; |
109 |
} |
110 |
|
111 |
if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) == 0) { |
112 |
printf ("%s: [%04x]:%ld (%s)", |
113 |
device, loopinfo.lo_device, loopinfo.lo_inode, |
114 |
loopinfo.lo_name); |
115 |
|
116 |
if (loopinfo.lo_offset) |
117 |
printf(", offset %d", loopinfo.lo_offset); |
118 |
|
119 |
if (loopinfo.lo_encrypt_type) |
120 |
printf(", encryption type %d\n", |
121 |
loopinfo.lo_encrypt_type); |
122 |
|
123 |
printf("\n"); |
124 |
close (fd); |
125 |
return 0; |
126 |
} |
127 |
|
128 |
errsv = errno; |
129 |
fprintf(stderr, "loop: can't get info on device %s: %s\n", |
130 |
device, strerror (errsv)); |
131 |
close (fd); |
132 |
return 1; |
133 |
} |
134 |
|
135 |
int |
136 |
is_loop_device (const char *device) { |
137 |
struct stat statbuf; |
138 |
|
139 |
return (stat(device, &statbuf) == 0 && |
140 |
S_ISBLK(statbuf.st_mode) && |
141 |
major(statbuf.st_rdev) == LOOPMAJOR); |
142 |
} |
143 |
|
144 |
#define SIZE(a) (sizeof(a)/sizeof(a[0])) |
145 |
|
146 |
char * |
147 |
find_unused_loop_device (void) { |
148 |
/* Just creating a device, say in /tmp, is probably a bad idea - |
149 |
people might have problems with backup or so. |
150 |
So, we just try /dev/loop[0-7]. */ |
151 |
char dev[20]; |
152 |
char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" }; |
153 |
int i, j, fd, somedev = 0, someloop = 0, permission = 0; |
154 |
struct stat statbuf; |
155 |
struct loop_info loopinfo; |
156 |
|
157 |
for (j = 0; j < SIZE(loop_formats); j++) { |
158 |
for(i = 0; i < 256; i++) { |
159 |
sprintf(dev, loop_formats[j], i); |
160 |
if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) { |
161 |
somedev++; |
162 |
fd = open (dev, O_RDONLY); |
163 |
if (fd >= 0) { |
164 |
if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0) |
165 |
someloop++; /* in use */ |
166 |
else if (errno == ENXIO) { |
167 |
close (fd); |
168 |
return xstrdup(dev);/* probably free */ |
169 |
} |
170 |
close (fd); |
171 |
} else if (errno == EACCES) |
172 |
permission++; |
173 |
|
174 |
continue;/* continue trying as long as devices exist */ |
175 |
} |
176 |
break; |
177 |
} |
178 |
} |
179 |
|
180 |
if (!somedev) |
181 |
error("%s: could not find any device /dev/loop#", progname); |
182 |
else if (!someloop && permission) |
183 |
error("%s: no permission to look at /dev/loop#", progname); |
184 |
else if (!someloop) |
185 |
error( |
186 |
"%s: Could not find any loop device. Maybe this kernel " |
187 |
"does not know\n" |
188 |
" about the loop device? (If so, recompile or " |
189 |
"`modprobe loop'.)", progname); |
190 |
else |
191 |
error("%s: could not find any free loop device", progname); |
192 |
return 0; |
193 |
} |
194 |
|
195 |
/* |
196 |
* A function to read the passphrase either from the terminal or from |
197 |
* an open file descriptor. |
198 |
*/ |
199 |
static char * |
200 |
xgetpass(int pfd, const char *prompt) { |
201 |
char *pass; |
202 |
int buflen, i; |
203 |
|
204 |
pass = NULL; |
205 |
buflen = 0; |
206 |
for (i=0; ; i++) { |
207 |
if (i >= buflen-1) { |
208 |
/* we're running out of space in the buffer. |
209 |
* Make it bigger: */ |
210 |
char *tmppass = pass; |
211 |
buflen += 128; |
212 |
pass = realloc(tmppass, buflen); |
213 |
if (pass == NULL) { |
214 |
/* realloc failed. Stop reading. */ |
215 |
error("Out of memory while reading passphrase"); |
216 |
pass = tmppass; /* the old buffer hasn't changed */ |
217 |
break; |
218 |
} |
219 |
} |
220 |
if (read(pfd, pass+i, 1) != 1 || |
221 |
pass[i] == '\n' || pass[i] == 0) |
222 |
break; |
223 |
} |
224 |
|
225 |
if (pass == NULL) |
226 |
return ""; |
227 |
|
228 |
pass[i] = 0; |
229 |
return pass; |
230 |
} |
231 |
|
232 |
static int |
233 |
digits_only(const char *s) { |
234 |
while (*s) |
235 |
if (!isdigit(*s++)) |
236 |
return 0; |
237 |
return 1; |
238 |
} |
239 |
|
240 |
int |
241 |
set_loop(const char *device, const char *file, unsigned long long offset, |
242 |
const char *encryption, int pfd, int *loopro) { |
243 |
struct loop_info64 loopinfo64; |
244 |
int fd, ffd, mode, i; |
245 |
char *pass; |
246 |
|
247 |
mode = (*loopro ? O_RDONLY : O_RDWR); |
248 |
if ((ffd = open(file, mode)) < 0) { |
249 |
if (!*loopro && errno == EROFS) |
250 |
ffd = open(file, mode = O_RDONLY); |
251 |
if (ffd < 0) { |
252 |
perror(file); |
253 |
return 1; |
254 |
} |
255 |
} |
256 |
if ((fd = open(device, mode)) < 0) { |
257 |
perror (device); |
258 |
return 1; |
259 |
} |
260 |
*loopro = (mode == O_RDONLY); |
261 |
|
262 |
memset(&loopinfo64, 0, sizeof(loopinfo64)); |
263 |
|
264 |
xstrncpy(loopinfo64.lo_file_name, file, LO_NAME_SIZE); |
265 |
|
266 |
if (encryption && *encryption) { |
267 |
if (digits_only(encryption)) { |
268 |
loopinfo64.lo_encrypt_type = atoi(encryption); |
269 |
} else { |
270 |
loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI; |
271 |
snprintf(loopinfo64.lo_crypt_name, LO_NAME_SIZE, |
272 |
"%s", encryption); |
273 |
} |
274 |
} |
275 |
|
276 |
loopinfo64.lo_offset = offset; |
277 |
|
278 |
|
279 |
switch (loopinfo64.lo_encrypt_type) { |
280 |
case LO_CRYPT_NONE: |
281 |
loopinfo64.lo_encrypt_key_size = 0; |
282 |
break; |
283 |
case LO_CRYPT_XOR: |
284 |
pass = xgetpass(pfd, "Password: "); |
285 |
goto gotpass; |
286 |
default: |
287 |
pass = xgetpass(pfd, "Password: "); |
288 |
gotpass: |
289 |
memset(loopinfo64.lo_encrypt_key, 0, LO_KEY_SIZE); |
290 |
xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE); |
291 |
memset(pass, 0, strlen(pass)); |
292 |
loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE; |
293 |
} |
294 |
|
295 |
if (ioctl(fd, LOOP_SET_FD, ffd) < 0) { |
296 |
perror("ioctl: LOOP_SET_FD"); |
297 |
return 1; |
298 |
} |
299 |
close (ffd); |
300 |
|
301 |
i = ioctl(fd, LOOP_SET_STATUS64, &loopinfo64); |
302 |
if (i) { |
303 |
struct loop_info loopinfo; |
304 |
int errsv = errno; |
305 |
|
306 |
i = loop_info64_to_old(&loopinfo64, &loopinfo); |
307 |
if (i) { |
308 |
errno = errsv; |
309 |
perror("ioctl: LOOP_SET_STATUS64"); |
310 |
} else { |
311 |
i = ioctl(fd, LOOP_SET_STATUS, &loopinfo); |
312 |
if (i) |
313 |
perror("ioctl: LOOP_SET_STATUS"); |
314 |
} |
315 |
memset(&loopinfo, 0, sizeof(loopinfo)); |
316 |
} |
317 |
memset(&loopinfo64, 0, sizeof(loopinfo64)); |
318 |
|
319 |
if (i) { |
320 |
ioctl (fd, LOOP_CLR_FD, 0); |
321 |
close (fd); |
322 |
return 1; |
323 |
} |
324 |
close (fd); |
325 |
|
326 |
if (verbose > 1) |
327 |
printf("set_loop(%s,%s,%llu): success\n", |
328 |
device, file, offset); |
329 |
return 0; |
330 |
} |
331 |
|
332 |
int |
333 |
del_loop (const char *device) { |
334 |
int fd; |
335 |
|
336 |
if ((fd = open (device, O_RDONLY)) < 0) { |
337 |
int errsv = errno; |
338 |
fprintf(stderr, "loop: can't delete device %s: %s\n", |
339 |
device, strerror (errsv)); |
340 |
return 1; |
341 |
} |
342 |
if (ioctl (fd, LOOP_CLR_FD, 0) < 0) { |
343 |
perror ("ioctl: LOOP_CLR_FD"); |
344 |
return 1; |
345 |
} |
346 |
close (fd); |
347 |
if (verbose > 1) |
348 |
printf("del_loop(%s): success\n", device); |
349 |
return 0; |
350 |
} |
351 |
|
352 |
|
353 |
#include <getopt.h> |
354 |
#include <stdarg.h> |
355 |
|
356 |
int verbose = 0; |
357 |
char *progname; |
358 |
|
359 |
static void |
360 |
usage(void) { |
361 |
fprintf(stderr, "usage:\n\ |
362 |
%s loop_device # give info\n\ |
363 |
%s -d loop_device # delete\n\ |
364 |
%s -f # find unused\n\ |
365 |
%s [-e encryption] [-o offset] {-f|loop_device} file # setup\n", |
366 |
progname, progname, progname, progname); |
367 |
exit(1); |
368 |
} |
369 |
|
370 |
char * |
371 |
xstrdup (const char *s) { |
372 |
char *t; |
373 |
|
374 |
if (s == NULL) |
375 |
return NULL; |
376 |
|
377 |
t = strdup (s); |
378 |
|
379 |
if (t == NULL) { |
380 |
fprintf(stderr, "not enough memory"); |
381 |
exit(1); |
382 |
} |
383 |
|
384 |
return t; |
385 |
} |
386 |
|
387 |
void |
388 |
error (const char *fmt, ...) { |
389 |
va_list args; |
390 |
|
391 |
va_start (args, fmt); |
392 |
vfprintf (stderr, fmt, args); |
393 |
va_end (args); |
394 |
fprintf (stderr, "\n"); |
395 |
} |
396 |
|
397 |
int |
398 |
main(int argc, char **argv) { |
399 |
char *p, *offset, *encryption, *passfd, *device, *file; |
400 |
int delete, find, c; |
401 |
int res = 0; |
402 |
int ro = 0; |
403 |
int pfd = -1; |
404 |
unsigned long long off; |
405 |
|
406 |
|
407 |
delete = find = 0; |
408 |
off = 0; |
409 |
offset = encryption = passfd = NULL; |
410 |
|
411 |
progname = argv[0]; |
412 |
if ((p = strrchr(progname, '/')) != NULL) |
413 |
progname = p+1; |
414 |
|
415 |
while ((c = getopt(argc, argv, "de:E:fo:p:v")) != -1) { |
416 |
switch (c) { |
417 |
case 'd': |
418 |
delete = 1; |
419 |
break; |
420 |
case 'E': |
421 |
case 'e': |
422 |
encryption = optarg; |
423 |
break; |
424 |
case 'f': |
425 |
find = 1; |
426 |
break; |
427 |
case 'o': |
428 |
offset = optarg; |
429 |
break; |
430 |
case 'p': |
431 |
passfd = optarg; |
432 |
break; |
433 |
case 'v': |
434 |
verbose = 1; |
435 |
break; |
436 |
default: |
437 |
usage(); |
438 |
} |
439 |
} |
440 |
|
441 |
if (argc == 1) { |
442 |
usage(); |
443 |
} else if (delete) { |
444 |
if (argc != optind+1 || encryption || offset || find) |
445 |
usage(); |
446 |
} else if (find) { |
447 |
if (argc < optind || argc > optind+1) |
448 |
usage(); |
449 |
} else { |
450 |
if (argc < optind+1 || argc > optind+2) |
451 |
usage(); |
452 |
} |
453 |
|
454 |
if (find) { |
455 |
device = find_unused_loop_device(); |
456 |
if (device == NULL) |
457 |
return -1; |
458 |
if (verbose) |
459 |
printf("Loop device is %s\n", device); |
460 |
if (argc == optind) { |
461 |
printf("%s\n", device); |
462 |
return 0; |
463 |
} |
464 |
file = argv[optind]; |
465 |
} else { |
466 |
device = argv[optind]; |
467 |
if (argc == optind+1) |
468 |
file = NULL; |
469 |
else |
470 |
file = argv[optind+1]; |
471 |
} |
472 |
|
473 |
if (delete) |
474 |
res = del_loop(device); |
475 |
else if (file == NULL) |
476 |
res = show_loop(device); |
477 |
else { |
478 |
if (offset && sscanf(offset, "%llu", &off) != 1) |
479 |
usage(); |
480 |
if (passfd && sscanf(passfd, "%d", &pfd) != 1) |
481 |
usage(); |
482 |
res = set_loop(device, file, off, encryption, pfd, &ro); |
483 |
} |
484 |
return res; |
485 |
} |