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