Annotation of /trunk/mkinitrd-magellan/klibc/usr/utils/losetup.c
Parent Directory | Revision Log
Revision 1297 -
(hide annotations)
(download)
Fri May 27 15:12:11 2011 UTC (12 years, 11 months ago) by niro
File MIME type: text/plain
File size: 10670 byte(s)
Fri May 27 15:12:11 2011 UTC (12 years, 11 months ago) by niro
File MIME type: text/plain
File size: 10670 byte(s)
-updated to klibc-1.5.22 with mntproc definitions patch included
1 | niro | 1122 | /* 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 | niro | 1297 | #include <stdarg.h> |
21 | niro | 1122 | #include <string.h> |
22 | |||
23 | #include "loop.h" | ||
24 | |||
25 | extern int verbose; | ||
26 | extern char *progname; | ||
27 | extern char *xstrdup (const char *s); /* not: #include "sundries.h" */ | ||
28 | extern void error (const char *fmt, ...); /* idem */ | ||
29 | |||
30 | /* caller guarantees n > 0 */ | ||
31 | void xstrncpy(char *dest, const char *src, size_t n) | ||
32 | { | ||
33 | strncpy(dest, src, n-1); | ||
34 | dest[n-1] = 0; | ||
35 | } | ||
36 | |||
37 | |||
38 | static int 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 show_loop(char *device) | ||
69 | { | ||
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 * find_unused_loop_device (void) | ||
147 | { | ||
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 * xgetpass(int pfd, const char *prompt) | ||
200 | { | ||
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 digits_only(const char *s) | ||
233 | { | ||
234 | while (*s) | ||
235 | if (!isdigit(*s++)) | ||
236 | return 0; | ||
237 | return 1; | ||
238 | } | ||
239 | |||
240 | int set_loop(const char *device, const char *file, unsigned long long offset, | ||
241 | const char *encryption, int pfd, int *loopro) { | ||
242 | struct loop_info64 loopinfo64; | ||
243 | int fd, ffd, mode, i; | ||
244 | char *pass; | ||
245 | |||
246 | mode = (*loopro ? O_RDONLY : O_RDWR); | ||
247 | if ((ffd = open(file, mode)) < 0) { | ||
248 | if (!*loopro && errno == EROFS) | ||
249 | ffd = open(file, mode = O_RDONLY); | ||
250 | if (ffd < 0) { | ||
251 | perror(file); | ||
252 | return 1; | ||
253 | } | ||
254 | } | ||
255 | if ((fd = open(device, mode)) < 0) { | ||
256 | perror (device); | ||
257 | return 1; | ||
258 | } | ||
259 | *loopro = (mode == O_RDONLY); | ||
260 | |||
261 | memset(&loopinfo64, 0, sizeof(loopinfo64)); | ||
262 | |||
263 | xstrncpy(loopinfo64.lo_file_name, file, LO_NAME_SIZE); | ||
264 | |||
265 | if (encryption && *encryption) { | ||
266 | if (digits_only(encryption)) { | ||
267 | loopinfo64.lo_encrypt_type = atoi(encryption); | ||
268 | } else { | ||
269 | loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI; | ||
270 | snprintf(loopinfo64.lo_crypt_name, LO_NAME_SIZE, | ||
271 | "%s", encryption); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | loopinfo64.lo_offset = offset; | ||
276 | |||
277 | |||
278 | switch (loopinfo64.lo_encrypt_type) { | ||
279 | case LO_CRYPT_NONE: | ||
280 | loopinfo64.lo_encrypt_key_size = 0; | ||
281 | break; | ||
282 | case LO_CRYPT_XOR: | ||
283 | pass = xgetpass(pfd, "Password: "); | ||
284 | goto gotpass; | ||
285 | default: | ||
286 | pass = xgetpass(pfd, "Password: "); | ||
287 | gotpass: | ||
288 | memset(loopinfo64.lo_encrypt_key, 0, LO_KEY_SIZE); | ||
289 | xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE); | ||
290 | memset(pass, 0, strlen(pass)); | ||
291 | loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE; | ||
292 | } | ||
293 | |||
294 | if (ioctl(fd, LOOP_SET_FD, (void *)(size_t)ffd) < 0) { | ||
295 | perror("ioctl: LOOP_SET_FD"); | ||
296 | return 1; | ||
297 | } | ||
298 | close (ffd); | ||
299 | |||
300 | i = ioctl(fd, LOOP_SET_STATUS64, &loopinfo64); | ||
301 | if (i) { | ||
302 | struct loop_info loopinfo; | ||
303 | int errsv = errno; | ||
304 | |||
305 | i = loop_info64_to_old(&loopinfo64, &loopinfo); | ||
306 | if (i) { | ||
307 | errno = errsv; | ||
308 | perror("ioctl: LOOP_SET_STATUS64"); | ||
309 | } else { | ||
310 | i = ioctl(fd, LOOP_SET_STATUS, &loopinfo); | ||
311 | if (i) | ||
312 | perror("ioctl: LOOP_SET_STATUS"); | ||
313 | } | ||
314 | memset(&loopinfo, 0, sizeof(loopinfo)); | ||
315 | } | ||
316 | memset(&loopinfo64, 0, sizeof(loopinfo64)); | ||
317 | |||
318 | if (i) { | ||
319 | ioctl (fd, LOOP_CLR_FD, 0); | ||
320 | close (fd); | ||
321 | return 1; | ||
322 | } | ||
323 | close (fd); | ||
324 | |||
325 | if (verbose > 1) | ||
326 | printf("set_loop(%s,%s,%llu): success\n", | ||
327 | device, file, offset); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | int del_loop (const char *device) | ||
332 | { | ||
333 | int fd; | ||
334 | |||
335 | if ((fd = open (device, O_RDONLY)) < 0) { | ||
336 | int errsv = errno; | ||
337 | fprintf(stderr, "loop: can't delete device %s: %s\n", | ||
338 | device, strerror (errsv)); | ||
339 | return 1; | ||
340 | } | ||
341 | if (ioctl (fd, LOOP_CLR_FD, 0) < 0) { | ||
342 | perror ("ioctl: LOOP_CLR_FD"); | ||
343 | niro | 1297 | close (fd); |
344 | niro | 1122 | 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 | int verbose = 0; | ||
354 | char *progname; | ||
355 | |||
356 | static void usage(void) { | ||
357 | fprintf(stderr, "usage:\n\ | ||
358 | %s loop_device # give info\n\ | ||
359 | %s -d loop_device # delete\n\ | ||
360 | %s -f # find unused\n\ | ||
361 | %s [-e encryption] [-o offset] {-f|loop_device} file # setup\n", | ||
362 | progname, progname, progname, progname); | ||
363 | exit(1); | ||
364 | } | ||
365 | |||
366 | char * xstrdup (const char *s) { | ||
367 | char *t; | ||
368 | |||
369 | if (s == NULL) | ||
370 | return NULL; | ||
371 | |||
372 | t = strdup (s); | ||
373 | |||
374 | if (t == NULL) { | ||
375 | fprintf(stderr, "not enough memory"); | ||
376 | exit(1); | ||
377 | } | ||
378 | |||
379 | return t; | ||
380 | } | ||
381 | |||
382 | void error (const char *fmt, ...) | ||
383 | { | ||
384 | va_list args; | ||
385 | |||
386 | va_start (args, fmt); | ||
387 | vfprintf (stderr, fmt, args); | ||
388 | va_end (args); | ||
389 | fprintf (stderr, "\n"); | ||
390 | } | ||
391 | |||
392 | int main(int argc, char **argv) | ||
393 | { | ||
394 | char *p, *offset, *encryption, *passfd, *device, *file; | ||
395 | int delete, find, c; | ||
396 | int res = 0; | ||
397 | int ro = 0; | ||
398 | int pfd = -1; | ||
399 | unsigned long long off; | ||
400 | |||
401 | |||
402 | delete = find = 0; | ||
403 | off = 0; | ||
404 | offset = encryption = passfd = NULL; | ||
405 | |||
406 | progname = argv[0]; | ||
407 | if ((p = strrchr(progname, '/')) != NULL) | ||
408 | progname = p+1; | ||
409 | |||
410 | while ((c = getopt(argc, argv, "de:E:fo:p:v")) != -1) { | ||
411 | switch (c) { | ||
412 | case 'd': | ||
413 | delete = 1; | ||
414 | break; | ||
415 | case 'E': | ||
416 | case 'e': | ||
417 | encryption = optarg; | ||
418 | break; | ||
419 | case 'f': | ||
420 | find = 1; | ||
421 | break; | ||
422 | case 'o': | ||
423 | offset = optarg; | ||
424 | break; | ||
425 | case 'p': | ||
426 | passfd = optarg; | ||
427 | break; | ||
428 | case 'v': | ||
429 | verbose = 1; | ||
430 | break; | ||
431 | default: | ||
432 | usage(); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | if (argc == 1) { | ||
437 | usage(); | ||
438 | } else if (delete) { | ||
439 | if (argc != optind+1 || encryption || offset || find) | ||
440 | usage(); | ||
441 | } else if (find) { | ||
442 | if (argc < optind || argc > optind+1) | ||
443 | usage(); | ||
444 | } else { | ||
445 | if (argc < optind+1 || argc > optind+2) | ||
446 | usage(); | ||
447 | } | ||
448 | |||
449 | if (find) { | ||
450 | device = find_unused_loop_device(); | ||
451 | if (device == NULL) | ||
452 | return -1; | ||
453 | if (verbose) | ||
454 | printf("Loop device is %s\n", device); | ||
455 | if (argc == optind) { | ||
456 | printf("%s\n", device); | ||
457 | return 0; | ||
458 | } | ||
459 | file = argv[optind]; | ||
460 | } else { | ||
461 | device = argv[optind]; | ||
462 | if (argc == optind+1) | ||
463 | file = NULL; | ||
464 | else | ||
465 | file = argv[optind+1]; | ||
466 | } | ||
467 | |||
468 | if (delete) | ||
469 | res = del_loop(device); | ||
470 | else if (file == NULL) | ||
471 | res = show_loop(device); | ||
472 | else { | ||
473 | if (offset && sscanf(offset, "%llu", &off) != 1) | ||
474 | usage(); | ||
475 | if (passfd && sscanf(passfd, "%d", &pfd) != 1) | ||
476 | usage(); | ||
477 | res = set_loop(device, file, off, encryption, pfd, &ro); | ||
478 | } | ||
479 | return res; | ||
480 | } |