]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/losetup.c
losetup: cleanup usage()
[thirdparty/util-linux.git] / sys-utils / losetup.c
1 /*
2 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
3 * Originally from Ted's losetup.c
4 *
5 * losetup.c - setup and control loop devices
6 */
7 #include <stdio.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/ioctl.h>
15 #include <sys/stat.h>
16 #include <sys/mman.h>
17 #include <sys/sysmacros.h>
18 #include <inttypes.h>
19 #include <dirent.h>
20 #include <getopt.h>
21 #include <stdarg.h>
22
23 #include "strutils.h"
24 #include "nls.h"
25 #include "pathnames.h"
26 #include "loopdev.h"
27 #include "xalloc.h"
28 #include "canonicalize.h"
29
30 enum {
31 A_CREATE = 1, /* setup a new device */
32 A_DELETE, /* delete given device(s) */
33 A_DELETE_ALL, /* delete all devices */
34 A_SHOW, /* list devices */
35 A_SHOW_ONE, /* print info about one device */
36 A_FIND_FREE, /* find first unused */
37 A_SET_CAPACITY, /* set device capacity */
38 };
39
40 static int verbose;
41
42 /*
43 * A function to read the passphrase either from the terminal or from
44 * an open file descriptor.
45 */
46 static char *xgetpass(int pfd, const char *prompt)
47 {
48 char *pass;
49 int buflen, i;
50
51 if (pfd < 0) /* terminal */
52 return getpass(prompt);
53
54 pass = NULL;
55 buflen = 0;
56 for (i=0; ; i++) {
57 if (i >= buflen-1) {
58 /* we're running out of space in the buffer.
59 * Make it bigger: */
60 char *tmppass = pass;
61 buflen += 128;
62 pass = realloc(tmppass, buflen);
63 if (pass == NULL) {
64 /* realloc failed. Stop reading. */
65 warn(_("Out of memory while reading passphrase"));
66 pass = tmppass; /* the old buffer hasn't changed */
67 break;
68 }
69 }
70 if (read(pfd, pass+i, 1) != 1 ||
71 pass[i] == '\n' || pass[i] == 0)
72 break;
73 }
74
75 if (pass == NULL)
76 return "";
77
78 pass[i] = 0;
79 return pass;
80 }
81
82 static int printf_loopdev(struct loopdev_cxt *lc)
83 {
84 uint64_t x;
85 dev_t dev = 0;
86 ino_t ino = 0;
87 char *fname = NULL;
88 int type;
89
90 fname = loopcxt_get_backing_file(lc);
91 if (!fname)
92 return -EINVAL;
93
94 if (loopcxt_get_backing_devno(lc, &dev) == 0)
95 loopcxt_get_backing_inode(lc, &ino);
96
97 if (!dev && !ino) {
98 /*
99 * Probably non-root user (no permissions to
100 * call LOOP_GET_STATUS ioctls).
101 */
102 printf("%s: []: (%s)",
103 loopcxt_get_device(lc), fname);
104
105 if (loopcxt_get_offset(lc, &x) == 0 && x)
106 printf(_(", offset %ju"), x);
107
108 if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
109 printf(_(", sizelimit %ju"), x);
110 printf("\n");
111 return 0;
112 }
113
114 printf("%s: [%04d]:%" PRIu64 " (%s)",
115 loopcxt_get_device(lc), dev, ino, fname);
116
117 if (loopcxt_get_offset(lc, &x) == 0 && x)
118 printf(_(", offset %ju"), x);
119
120 if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
121 printf(_(", sizelimit %ju"), x);
122
123 if (loopcxt_get_encrypt_type(lc, &type) == 0) {
124 const char *e = loopcxt_get_crypt_name(lc);
125
126 if ((!e || !*e) && type == 1)
127 e = "XOR";
128 if (e && *e)
129 printf(_(", encryption %s (type %ju)"), e, type);
130 }
131 printf("\n");
132 return 0;
133 }
134
135 static int show_all_loops(struct loopdev_cxt *lc, const char *file,
136 uint64_t offset, int flags)
137 {
138 struct stat sbuf, *st = &sbuf;
139
140 if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
141 return -1;
142
143 if (!file || stat(file, st))
144 st = NULL;
145
146 while (loopcxt_next(lc) == 0) {
147
148 if (file && !loopcxt_is_used(lc, st, file, offset, flags))
149 continue;
150
151 printf_loopdev(lc);
152 }
153 loopcxt_deinit_iterator(lc);
154 return 0;
155 }
156
157 static int set_capacity(struct loopdev_cxt *lc)
158 {
159 int fd = loopcxt_get_fd(lc);
160
161 if (fd < 0)
162 warn(_("%s: open failed"), loopcxt_get_device(lc));
163 else if (ioctl(fd, LOOP_SET_CAPACITY) != 0)
164 warnx(_("%s: set capacity failed"), loopcxt_get_device(lc));
165 else
166 return 0;
167
168 return -1;
169 }
170
171 static int delete_loop(struct loopdev_cxt *lc)
172 {
173 if (loopcxt_delete_device(lc))
174 warn(_("%s: detach failed"), loopcxt_get_device(lc));
175 else
176 return 0;
177
178 return -1;
179 }
180
181 static int delete_all_loops(struct loopdev_cxt *lc)
182 {
183 int res = 0;
184
185 if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
186 return -1;
187
188 while (loopcxt_next(lc) == 0)
189 res += delete_loop(lc);
190
191 loopcxt_deinit_iterator(lc);
192 return res;
193 }
194
195 static void usage(FILE *out)
196 {
197 fputs(USAGE_HEADER, out);
198
199 fprintf(out,
200 _(" %1$s [options] [<loopdev>]\n"
201 " %1$s [options] -f | <loopdev> <file>\n"),
202 program_invocation_short_name);
203
204 fputs(USAGE_OPTIONS, out);
205 fputs(_(" -a, --all list all used devices\n"
206 " -d, --detach <loopdev> [...] detach one or more devices\n"
207 " -D, --detach-all detach all used devices\n"
208 " -f, --find find first unused device\n"
209 " -c, --set-capacity <loopdev> resize device\n"
210 " -j, --associated <file> list all devices associated with <file>\n"), out);
211 fputs(USAGE_SEPARATOR, out);
212
213 fputs(_(" -e, --encryption <type> enable encryption with specified <name/num>\n"
214 " -o, --offset <num> start at offset <num> into file\n"
215 " --sizelimit <num> device limited to <num> bytes of the file\n"
216 " -p, --pass-fd <num> read passphrase from file descriptor <num>\n"
217 " -r, --read-only setup read-only loop device\n"
218 " --show print device name after setup (with -f)\n"
219 " -v, --verbose verbose mode\n"), out);
220
221 fputs(USAGE_SEPARATOR, out);
222 fputs(USAGE_HELP, out);
223 fputs(USAGE_VERSION, out);
224
225 fprintf(out, USAGE_MAN_TAIL("losetup(8)"));
226
227 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
228 }
229
230 int main(int argc, char **argv)
231 {
232 struct loopdev_cxt lc;
233 int act = 0, flags = 0, passfd = -1, c;
234 char *file = NULL, *encryption = NULL;
235 uint64_t offset = 0, sizelimit = 0;
236 int res = 0, showdev = 0, lo_flags = 0;
237
238 enum {
239 OPT_SIZELIMIT = CHAR_MAX + 1,
240 OPT_SHOW
241 };
242 static const struct option longopts[] = {
243 { "all", 0, 0, 'a' },
244 { "set-capacity", 1, 0, 'c' },
245 { "detach", 1, 0, 'd' },
246 { "detach-all", 0, 0, 'D' },
247 { "encryption", 1, 0, 'e' },
248 { "find", 0, 0, 'f' },
249 { "help", 0, 0, 'h' },
250 { "associated", 1, 0, 'j' },
251 { "offset", 1, 0, 'o' },
252 { "sizelimit", 1, 0, OPT_SIZELIMIT },
253 { "pass-fd", 1, 0, 'p' },
254 { "read-only", 0, 0, 'r' },
255 { "show", 0, 0, OPT_SHOW },
256 { "verbose", 0, 0, 'v' },
257 { "version", 0, 0, 'V' },
258 { NULL, 0, 0, 0 }
259 };
260
261 setlocale(LC_ALL, "");
262 bindtextdomain(PACKAGE, LOCALEDIR);
263 textdomain(PACKAGE);
264
265 loopcxt_init(&lc, 0);
266 loopcxt_enable_debug(&lc, getenv("LOOPDEV_DEBUG") ? TRUE : FALSE);
267
268 while ((c = getopt_long(argc, argv, "ac:d:De:E:fhj:o:p:rvV",
269 longopts, NULL)) != -1) {
270
271 if (act && strchr("acdDfj", c))
272 errx(EXIT_FAILURE,
273 _("the options %s are mutually exclusive"),
274 "--{all,associated,set-capacity,detach,detach-all,find}");
275
276 switch (c) {
277 case 'a':
278 act = A_SHOW;
279 break;
280 case 'c':
281 act = A_SET_CAPACITY;
282 loopcxt_set_device(&lc, optarg);
283 break;
284 case 'r':
285 lo_flags |= LO_FLAGS_READ_ONLY;
286 break;
287 case 'd':
288 act = A_DELETE;
289 loopcxt_set_device(&lc, optarg);
290 break;
291 case 'D':
292 act = A_DELETE_ALL;
293 break;
294 case 'E':
295 case 'e':
296 encryption = optarg;
297 break;
298 case 'f':
299 act = A_FIND_FREE;
300 break;
301 case 'h':
302 usage(stdout);
303 break;
304 case 'j':
305 act = A_SHOW;
306 file = optarg;
307 break;
308 case 'o':
309 if (strtosize(optarg, &offset))
310 errx(EXIT_FAILURE,
311 _("invalid offset '%s' specified"), optarg);
312 flags |= LOOPDEV_FL_OFFSET;
313 break;
314 case 'p':
315 passfd = strtol_or_err(optarg,
316 _("invalid passphrase file descriptor"));
317 break;
318 case OPT_SHOW:
319 showdev = 1;
320 break;
321 case 'v':
322 verbose = 1;
323 break;
324 case 'V':
325 printf(UTIL_LINUX_VERSION);
326 return EXIT_SUCCESS;
327 case OPT_SIZELIMIT: /* --sizelimit */
328 if (strtosize(optarg, &sizelimit))
329 errx(EXIT_FAILURE,
330 _("invalid size '%s' specified"), optarg);
331 flags |= LOOPDEV_FL_SIZELIMIT;
332 break;
333 default:
334 usage(stderr);
335 }
336 }
337
338 if (argc == 1)
339 usage(stderr);
340
341 if (act == A_FIND_FREE && optind < argc) {
342 /*
343 * losetup -f <backing_file>
344 */
345 act = A_CREATE;
346 file = argv[optind++];
347 }
348 if (!act && optind + 1 == argc) {
349 /*
350 * losetup <device>
351 */
352 act = A_SHOW_ONE;
353 loopcxt_set_device(&lc, argv[optind++]);
354 }
355 if (!act) {
356 /*
357 * losetup <loopdev> <backing_file>
358 */
359 act = A_CREATE;
360
361 if (optind >= argc)
362 errx(EXIT_FAILURE, _("no loop device specified"));
363 loopcxt_set_device(&lc, argv[optind++]);
364
365 if (optind >= argc)
366 errx(EXIT_FAILURE, _("no file specified"));
367 file = argv[optind++];
368 }
369
370 if (act != A_CREATE &&
371 (encryption || sizelimit || passfd != -1 || lo_flags || showdev))
372 errx(EXIT_FAILURE,
373 _("the options %s are allowed to loop device setup only"),
374 "--{encryption,sizelimit,pass-fd,read-only,show}");
375
376 if (act != A_CREATE && act != A_SHOW && (flags & LOOPDEV_FL_OFFSET))
377 errx(EXIT_FAILURE, _("the option --offset is not allowed in this context."));
378
379 switch (act) {
380 case A_CREATE:
381 {
382 char *pass = NULL;
383 int hasdev = loopcxt_has_device(&lc);
384
385 if (encryption) {
386 #ifdef MCL_FUTURE
387 if(mlockall(MCL_CURRENT | MCL_FUTURE))
388 err(EXIT_FAILURE, _("couldn't lock into memory"));
389 #endif
390 pass = xgetpass(passfd, _("Password: "));
391 }
392 do {
393 /* Note that loopcxt_{find_unused,set_device}() resets
394 * loopcxt struct.
395 */
396 if (!hasdev && (res = loopcxt_find_unused(&lc))) {
397 warnx(_("not found unused device"));
398 break;
399 }
400 if (encryption && pass)
401 loopcxt_set_encryption(&lc, encryption, pass);
402 if (flags & LOOPDEV_FL_OFFSET)
403 loopcxt_set_offset(&lc, offset);
404 if (flags & LOOPDEV_FL_SIZELIMIT)
405 loopcxt_set_offset(&lc, sizelimit);
406 if (lo_flags)
407 loopcxt_set_flags(&lc, lo_flags);
408 if ((res = loopcxt_set_backing_file(&lc, file))) {
409 warn(_("%s: failed to use backing file"), file);
410 break;
411 }
412 errno = 0;
413 res = loopcxt_setup_device(&lc);
414 if (res == 0)
415 break; /* success */
416 if (errno != EBUSY) {
417 warn(_("failed to setup loop device"));
418 break;
419 }
420 } while (hasdev == 0);
421
422 free(pass);
423
424 if (showdev && res == 0)
425 printf("%s\n", loopcxt_get_device(&lc));
426 break;
427 }
428 case A_DELETE:
429 res = delete_loop(&lc);
430 while (optind < argc) {
431 loopcxt_set_device(&lc, argv[optind++]);
432 res += delete_loop(&lc);
433 }
434 break;
435 case A_DELETE_ALL:
436 res = delete_all_loops(&lc);
437 break;
438 case A_FIND_FREE:
439 if (loopcxt_find_unused(&lc))
440 warn(_("find unused loop device failed"));
441 else
442 printf("%s\n", loopcxt_get_device(&lc));
443 break;
444 case A_SHOW:
445 res = show_all_loops(&lc, file, offset, flags);
446 break;
447 case A_SHOW_ONE:
448 res = printf_loopdev(&lc);
449 if (res)
450 warn(_("%s"), loopcxt_get_device(&lc));
451 break;
452 case A_SET_CAPACITY:
453 res = set_capacity(&lc);
454 break;
455 default:
456 usage(stderr);
457 break;
458 }
459
460 loopcxt_deinit(&lc);
461 return res ? EXIT_FAILURE : EXIT_SUCCESS;
462 }
463