]>
Commit | Line | Data |
---|---|---|
6dbe3af9 | 1 | /* |
39fde137 KZ |
2 | * Copyright (C) 2011 Karel Zak <kzak@redhat.com> |
3 | * Originally from Ted's losetup.c | |
4 | * | |
6dbe3af9 KZ |
5 | * losetup.c - setup and control loop devices |
6 | */ | |
896352d3 | 7 | #include <assert.h> |
6dbe3af9 KZ |
8 | #include <stdio.h> |
9 | #include <string.h> | |
fd6b7a7f | 10 | #include <errno.h> |
6dbe3af9 KZ |
11 | #include <stdlib.h> |
12 | #include <unistd.h> | |
13 | #include <sys/ioctl.h> | |
fd6b7a7f | 14 | #include <sys/stat.h> |
7711bc17 | 15 | #include <inttypes.h> |
65329058 | 16 | #include <getopt.h> |
6dbe3af9 | 17 | |
7e86cd54 OO |
18 | #include <libsmartcols.h> |
19 | ||
934df30d | 20 | #include "c.h" |
7eda085c | 21 | #include "nls.h" |
934df30d | 22 | #include "strutils.h" |
de4acb05 | 23 | #include "loopdev.h" |
efb8854f | 24 | #include "closestream.h" |
96801c48 | 25 | #include "optutils.h" |
896352d3 | 26 | #include "xalloc.h" |
114ade3d | 27 | #include "canonicalize.h" |
362f5d20 | 28 | #include "pathnames.h" |
96801c48 | 29 | |
39fde137 | 30 | enum { |
c7e0925d | 31 | A_CREATE = 1, /* setup a new device */ |
c654c4f0 KZ |
32 | A_DELETE, /* delete given device(s) */ |
33 | A_DELETE_ALL, /* delete all devices */ | |
39fde137 | 34 | A_SHOW, /* list devices */ |
09ec0c0a | 35 | A_SHOW_ONE, /* print info about one device */ |
39fde137 KZ |
36 | A_FIND_FREE, /* find first unused */ |
37 | A_SET_CAPACITY, /* set device capacity */ | |
64c3bb3c | 38 | A_SET_DIRECT_IO, /* set accessing backing file by direct io */ |
39fde137 KZ |
39 | }; |
40 | ||
896352d3 OO |
41 | enum { |
42 | COL_NAME = 0, | |
43 | COL_AUTOCLR, | |
44 | COL_BACK_FILE, | |
45 | COL_BACK_INO, | |
46 | COL_BACK_MAJMIN, | |
47 | COL_MAJMIN, | |
48 | COL_OFFSET, | |
49 | COL_PARTSCAN, | |
50 | COL_RO, | |
4ad996d7 | 51 | COL_SIZELIMIT, |
faeef4d2 | 52 | COL_DIO, |
896352d3 OO |
53 | }; |
54 | ||
7e86cd54 OO |
55 | /* basic output flags */ |
56 | static int no_headings; | |
57 | static int raw; | |
4827093d | 58 | static int json; |
896352d3 OO |
59 | |
60 | struct colinfo { | |
61 | const char *name; | |
62 | double whint; | |
63 | int flags; | |
64 | const char *help; | |
65 | }; | |
66 | ||
67 | static struct colinfo infos[] = { | |
7e86cd54 | 68 | [COL_AUTOCLR] = { "AUTOCLEAR", 1, SCOLS_FL_RIGHT, N_("autoclear flag set")}, |
896352d3 | 69 | [COL_BACK_FILE] = { "BACK-FILE", 0.3, 0, N_("device backing file")}, |
7e86cd54 | 70 | [COL_BACK_INO] = { "BACK-INO", 4, SCOLS_FL_RIGHT, N_("backing file inode number")}, |
896352d3 OO |
71 | [COL_BACK_MAJMIN] = { "BACK-MAJ:MIN", 6, 0, N_("backing file major:minor device number")}, |
72 | [COL_NAME] = { "NAME", 0.25, 0, N_("loop device name")}, | |
7e86cd54 OO |
73 | [COL_OFFSET] = { "OFFSET", 5, SCOLS_FL_RIGHT, N_("offset from the beginning")}, |
74 | [COL_PARTSCAN] = { "PARTSCAN", 1, SCOLS_FL_RIGHT, N_("partscan flag set")}, | |
75 | [COL_RO] = { "RO", 1, SCOLS_FL_RIGHT, N_("read-only device")}, | |
76 | [COL_SIZELIMIT] = { "SIZELIMIT", 5, SCOLS_FL_RIGHT, N_("size limit of the file in bytes")}, | |
896352d3 | 77 | [COL_MAJMIN] = { "MAJ:MIN", 3, 0, N_("loop device major:minor number")}, |
faeef4d2 | 78 | [COL_DIO] = { "DIO", 1, SCOLS_FL_RIGHT, N_("access backing file with direct-io")}, |
896352d3 OO |
79 | }; |
80 | ||
d78deddc | 81 | static int columns[ARRAY_SIZE(infos) * 2] = {-1}; |
40b17508 | 82 | static size_t ncolumns; |
6dbe3af9 | 83 | |
896352d3 OO |
84 | static int get_column_id(int num) |
85 | { | |
40b17508 KZ |
86 | assert(num >= 0); |
87 | assert((size_t) num < ncolumns); | |
d78deddc | 88 | assert(columns[num] < (int) ARRAY_SIZE(infos)); |
896352d3 OO |
89 | return columns[num]; |
90 | } | |
91 | ||
92 | static struct colinfo *get_column_info(int num) | |
93 | { | |
94 | return &infos[ get_column_id(num) ]; | |
95 | } | |
96 | ||
97 | static int column_name_to_id(const char *name, size_t namesz) | |
98 | { | |
99 | size_t i; | |
100 | ||
d78deddc | 101 | for (i = 0; i < ARRAY_SIZE(infos); i++) { |
896352d3 OO |
102 | const char *cn = infos[i].name; |
103 | ||
104 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
105 | return i; | |
106 | } | |
107 | warnx(_("unknown column: %s"), name); | |
108 | return -1; | |
109 | } | |
d03dd608 | 110 | |
39fde137 KZ |
111 | static int printf_loopdev(struct loopdev_cxt *lc) |
112 | { | |
113 | uint64_t x; | |
114 | dev_t dev = 0; | |
115 | ino_t ino = 0; | |
116 | char *fname = NULL; | |
a3b421df | 117 | uint32_t type; |
39fde137 KZ |
118 | |
119 | fname = loopcxt_get_backing_file(lc); | |
120 | if (!fname) | |
121 | return -EINVAL; | |
122 | ||
123 | if (loopcxt_get_backing_devno(lc, &dev) == 0) | |
124 | loopcxt_get_backing_inode(lc, &ino); | |
125 | ||
126 | if (!dev && !ino) { | |
127 | /* | |
128 | * Probably non-root user (no permissions to | |
129 | * call LOOP_GET_STATUS ioctls). | |
130 | */ | |
131 | printf("%s: []: (%s)", | |
132 | loopcxt_get_device(lc), fname); | |
133 | ||
134 | if (loopcxt_get_offset(lc, &x) == 0 && x) | |
135 | printf(_(", offset %ju"), x); | |
136 | ||
137 | if (loopcxt_get_sizelimit(lc, &x) == 0 && x) | |
138 | printf(_(", sizelimit %ju"), x); | |
139 | printf("\n"); | |
140 | return 0; | |
141 | } | |
142 | ||
143 | printf("%s: [%04d]:%" PRIu64 " (%s)", | |
a3b421df | 144 | loopcxt_get_device(lc), (int) dev, ino, fname); |
39fde137 KZ |
145 | |
146 | if (loopcxt_get_offset(lc, &x) == 0 && x) | |
147 | printf(_(", offset %ju"), x); | |
148 | ||
149 | if (loopcxt_get_sizelimit(lc, &x) == 0 && x) | |
150 | printf(_(", sizelimit %ju"), x); | |
151 | ||
152 | if (loopcxt_get_encrypt_type(lc, &type) == 0) { | |
153 | const char *e = loopcxt_get_crypt_name(lc); | |
154 | ||
155 | if ((!e || !*e) && type == 1) | |
156 | e = "XOR"; | |
157 | if (e && *e) | |
a3b421df | 158 | printf(_(", encryption %s (type %u)"), e, type); |
39fde137 KZ |
159 | } |
160 | printf("\n"); | |
161 | return 0; | |
162 | } | |
163 | ||
bc0ac075 KZ |
164 | static int show_all_loops(struct loopdev_cxt *lc, const char *file, |
165 | uint64_t offset, int flags) | |
39fde137 | 166 | { |
bc0ac075 | 167 | struct stat sbuf, *st = &sbuf; |
6d62bc0f | 168 | char *cn_file = NULL; |
bc0ac075 | 169 | |
39fde137 | 170 | if (loopcxt_init_iterator(lc, LOOPITER_FL_USED)) |
6e90a44c | 171 | return -1; |
39fde137 | 172 | |
bc0ac075 KZ |
173 | if (!file || stat(file, st)) |
174 | st = NULL; | |
175 | ||
176 | while (loopcxt_next(lc) == 0) { | |
6d62bc0f KZ |
177 | if (file) { |
178 | int used; | |
179 | const char *bf = cn_file ? cn_file : file; | |
180 | ||
74a4705a | 181 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
6d62bc0f KZ |
182 | if (!used && !cn_file) { |
183 | bf = cn_file = canonicalize_path(file); | |
74a4705a | 184 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
6d62bc0f KZ |
185 | } |
186 | if (!used) | |
114ade3d SK |
187 | continue; |
188 | } | |
bc0ac075 KZ |
189 | printf_loopdev(lc); |
190 | } | |
c654c4f0 | 191 | loopcxt_deinit_iterator(lc); |
f614b73c | 192 | free(cn_file); |
6e90a44c KZ |
193 | return 0; |
194 | } | |
195 | ||
c654c4f0 KZ |
196 | static int delete_loop(struct loopdev_cxt *lc) |
197 | { | |
198 | if (loopcxt_delete_device(lc)) | |
199 | warn(_("%s: detach failed"), loopcxt_get_device(lc)); | |
200 | else | |
201 | return 0; | |
202 | ||
203 | return -1; | |
204 | } | |
205 | ||
206 | static int delete_all_loops(struct loopdev_cxt *lc) | |
207 | { | |
208 | int res = 0; | |
209 | ||
210 | if (loopcxt_init_iterator(lc, LOOPITER_FL_USED)) | |
211 | return -1; | |
212 | ||
213 | while (loopcxt_next(lc) == 0) | |
214 | res += delete_loop(lc); | |
215 | ||
216 | loopcxt_deinit_iterator(lc); | |
217 | return res; | |
39fde137 KZ |
218 | } |
219 | ||
7e86cd54 | 220 | static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln) |
896352d3 | 221 | { |
40b17508 | 222 | size_t i; |
896352d3 OO |
223 | |
224 | for (i = 0; i < ncolumns; i++) { | |
48f1f38b KZ |
225 | const char *p = NULL; /* external data */ |
226 | char *np = NULL; /* allocated here */ | |
896352d3 OO |
227 | uint64_t x = 0; |
228 | ||
229 | switch(get_column_id(i)) { | |
230 | case COL_NAME: | |
231 | p = loopcxt_get_device(lc); | |
896352d3 OO |
232 | break; |
233 | case COL_BACK_FILE: | |
234 | p = loopcxt_get_backing_file(lc); | |
896352d3 OO |
235 | break; |
236 | case COL_OFFSET: | |
237 | if (loopcxt_get_offset(lc, &x) == 0) | |
238 | xasprintf(&np, "%jd", x); | |
896352d3 | 239 | break; |
4ad996d7 | 240 | case COL_SIZELIMIT: |
896352d3 OO |
241 | if (loopcxt_get_sizelimit(lc, &x) == 0) |
242 | xasprintf(&np, "%jd", x); | |
896352d3 OO |
243 | break; |
244 | case COL_BACK_MAJMIN: | |
245 | { | |
246 | dev_t dev = 0; | |
247 | if (loopcxt_get_backing_devno(lc, &dev) == 0 && dev) | |
248 | xasprintf(&np, "%8u:%-3u", major(dev), minor(dev)); | |
896352d3 OO |
249 | break; |
250 | } | |
251 | case COL_MAJMIN: | |
252 | { | |
253 | struct stat st; | |
254 | ||
255 | if (loopcxt_get_device(lc) | |
256 | && stat(loopcxt_get_device(lc), &st) == 0 | |
257 | && S_ISBLK(st.st_mode) | |
258 | && major(st.st_rdev) == LOOPDEV_MAJOR) | |
259 | xasprintf(&np, "%3u:%-3u", major(st.st_rdev), | |
260 | minor(st.st_rdev)); | |
896352d3 OO |
261 | break; |
262 | } | |
263 | case COL_BACK_INO: | |
264 | { | |
265 | ino_t ino = 0; | |
266 | if (loopcxt_get_backing_inode(lc, &ino) == 0 && ino) | |
267 | xasprintf(&np, "%ju", ino); | |
896352d3 OO |
268 | break; |
269 | } | |
270 | case COL_AUTOCLR: | |
48f1f38b | 271 | p = loopcxt_is_autoclear(lc) ? "1" : "0"; |
896352d3 OO |
272 | break; |
273 | case COL_RO: | |
48f1f38b | 274 | p = loopcxt_is_readonly(lc) ? "1" : "0"; |
896352d3 | 275 | break; |
faeef4d2 ML |
276 | case COL_DIO: |
277 | p = loopcxt_is_dio(lc) ? "1" : "0"; | |
278 | break; | |
896352d3 | 279 | case COL_PARTSCAN: |
48f1f38b | 280 | p = loopcxt_is_partscan(lc) ? "1" : "0"; |
896352d3 OO |
281 | break; |
282 | default: | |
283 | return -EINVAL; | |
284 | } | |
48f1f38b KZ |
285 | |
286 | ||
287 | if (p) | |
288 | scols_line_set_data(ln, i, p); /* calls strdup() */ | |
289 | else if (np) | |
290 | scols_line_refer_data(ln, i, np); /* only refers */ | |
896352d3 | 291 | } |
48f1f38b | 292 | |
896352d3 OO |
293 | return 0; |
294 | } | |
295 | ||
48f1f38b | 296 | static int show_table(struct loopdev_cxt *lc, |
9f56106d KZ |
297 | const char *file, |
298 | uint64_t offset, | |
7e86cd54 | 299 | int flags) |
896352d3 OO |
300 | { |
301 | struct stat sbuf, *st = &sbuf; | |
48f1f38b | 302 | struct libscols_table *tb; |
7e86cd54 | 303 | struct libscols_line *ln; |
40b17508 KZ |
304 | int rc = 0; |
305 | size_t i; | |
896352d3 | 306 | |
710ed55d KZ |
307 | scols_init_debug(0); |
308 | ||
0925a9dd | 309 | if (!(tb = scols_new_table())) |
48f1f38b | 310 | err(EXIT_FAILURE, _("failed to initialize output table")); |
0925a9dd | 311 | scols_table_enable_raw(tb, raw); |
4827093d | 312 | scols_table_enable_json(tb, json); |
0925a9dd | 313 | scols_table_enable_noheadings(tb, no_headings); |
896352d3 | 314 | |
4827093d KZ |
315 | if (json) |
316 | scols_table_set_name(tb, "loopdevices"); | |
317 | ||
e0b06769 | 318 | for (i = 0; i < ncolumns; i++) { |
896352d3 OO |
319 | struct colinfo *ci = get_column_info(i); |
320 | ||
48f1f38b KZ |
321 | if (!scols_table_new_column(tb, ci->name, ci->whint, ci->flags)) |
322 | err(EXIT_FAILURE, _("failed to initialize output column")); | |
896352d3 OO |
323 | } |
324 | ||
9f56106d | 325 | /* only one loopdev requested (already assigned to loopdev_cxt) */ |
896352d3 | 326 | if (loopcxt_get_device(lc)) { |
48f1f38b | 327 | ln = scols_table_new_line(tb, NULL); |
7e86cd54 | 328 | if (!ln) |
48f1f38b KZ |
329 | err(EXIT_FAILURE, _("failed to initialize output line")); |
330 | rc = set_scols_data(lc, ln); | |
896352d3 | 331 | |
9f56106d | 332 | /* list all loopdevs */ |
48f1f38b KZ |
333 | } else { |
334 | char *cn_file = NULL; | |
896352d3 | 335 | |
48f1f38b KZ |
336 | rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED); |
337 | if (rc) | |
338 | goto done; | |
339 | if (!file || stat(file, st)) | |
340 | st = NULL; | |
341 | ||
342 | while (loopcxt_next(lc) == 0) { | |
343 | if (file) { | |
344 | int used; | |
345 | const char *bf = cn_file ? cn_file : file; | |
6d62bc0f | 346 | |
74a4705a | 347 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
48f1f38b KZ |
348 | if (!used && !cn_file) { |
349 | bf = cn_file = canonicalize_path(file); | |
74a4705a | 350 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
48f1f38b KZ |
351 | } |
352 | if (!used) | |
353 | continue; | |
6d62bc0f | 354 | } |
48f1f38b KZ |
355 | |
356 | ln = scols_table_new_line(tb, NULL); | |
357 | if (!ln) | |
358 | err(EXIT_FAILURE, _("failed to initialize output column")); | |
359 | rc = set_scols_data(lc, ln); | |
360 | if (rc) | |
361 | break; | |
6d62bc0f | 362 | } |
896352d3 | 363 | |
48f1f38b KZ |
364 | loopcxt_deinit_iterator(lc); |
365 | free(cn_file); | |
896352d3 | 366 | } |
48f1f38b KZ |
367 | done: |
368 | if (rc == 0) | |
369 | rc = scols_print_table(tb); | |
370 | scols_unref_table(tb); | |
371 | return rc; | |
896352d3 OO |
372 | } |
373 | ||
aadb9303 KZ |
374 | static void usage(FILE *out) |
375 | { | |
e0b06769 SK |
376 | size_t i; |
377 | ||
aadb9303 KZ |
378 | fputs(USAGE_HEADER, out); |
379 | ||
380 | fprintf(out, | |
381 | _(" %1$s [options] [<loopdev>]\n" | |
382 | " %1$s [options] -f | <loopdev> <file>\n"), | |
383 | program_invocation_short_name); | |
384 | ||
451dbcfa BS |
385 | fputs(USAGE_SEPARATOR, out); |
386 | fputs(_("Set up and control loop devices.\n"), out); | |
387 | ||
aadb9303 | 388 | fputs(USAGE_OPTIONS, out); |
9c47f25e | 389 | fputs(_(" -a, --all list all used devices\n"), out); |
aa06617f | 390 | fputs(_(" -d, --detach <loopdev>... detach one or more devices\n"), out); |
9c47f25e BS |
391 | fputs(_(" -D, --detach-all detach all used devices\n"), out); |
392 | fputs(_(" -f, --find find first unused device\n"), out); | |
aa06617f | 393 | fputs(_(" -c, --set-capacity <loopdev> resize the device\n"), out); |
9c47f25e | 394 | fputs(_(" -j, --associated <file> list all devices associated with <file>\n"), out); |
9a94b634 | 395 | fputs(_(" -L, --nooverlap avoid possible conflict between devices\n"), out); |
9c47f25e | 396 | |
aadb9303 KZ |
397 | fputs(USAGE_SEPARATOR, out); |
398 | ||
59a4ed11 | 399 | fputs(_(" -o, --offset <num> start at offset <num> into file\n"), out); |
aa06617f BS |
400 | fputs(_(" --sizelimit <num> device is limited to <num> bytes of the file\n"), out); |
401 | fputs(_(" -P, --partscan create a partitioned loop device\n"), out); | |
402 | fputs(_(" -r, --read-only set up a read-only loop device\n"), out); | |
f98d9641 | 403 | fputs(_(" --direct-io[=<on|off>] open backing file with O_DIRECT\n"), out); |
59a4ed11 SK |
404 | fputs(_(" --show print device name after setup (with -f)\n"), out); |
405 | fputs(_(" -v, --verbose verbose mode\n"), out); | |
aadb9303 | 406 | |
9f56106d KZ |
407 | fputs(USAGE_SEPARATOR, out); |
408 | ||
14576644 | 409 | fputs(_(" -l, --list list info about all or specified (default)\n"), out); |
9f56106d | 410 | fputs(_(" -O, --output <cols> specify columns to output for --list\n"), out); |
0d0d12ad | 411 | fputs(_(" -n, --noheadings don't print headings for --list output\n"), out); |
9f56106d | 412 | fputs(_(" --raw use raw --list output format\n"), out); |
4827093d | 413 | fputs(_(" -J, --json use JSON --list output format\n"), out); |
9f56106d | 414 | |
aadb9303 KZ |
415 | fputs(USAGE_SEPARATOR, out); |
416 | fputs(USAGE_HELP, out); | |
417 | fputs(USAGE_VERSION, out); | |
418 | ||
896352d3 | 419 | fputs(_("\nAvailable --list columns:\n"), out); |
d78deddc | 420 | for (i = 0; i < ARRAY_SIZE(infos); i++) |
896352d3 OO |
421 | fprintf(out, " %12s %s\n", infos[i].name, _(infos[i].help)); |
422 | ||
aadb9303 | 423 | fprintf(out, USAGE_MAN_TAIL("losetup(8)")); |
6997468e KZ |
424 | |
425 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
aadb9303 | 426 | } |
22853e4a | 427 | |
35545456 KZ |
428 | static void warn_size(const char *filename, uint64_t size) |
429 | { | |
430 | struct stat st; | |
431 | ||
432 | if (!size) { | |
b048b8af | 433 | if (stat(filename, &st) || S_ISBLK(st.st_mode)) |
35545456 KZ |
434 | return; |
435 | size = st.st_size; | |
436 | } | |
437 | ||
438 | if (size < 512) | |
97b820bf BS |
439 | warnx(_("%s: Warning: file is smaller than 512 bytes; the loop device " |
440 | "may be useless or invisible for system tools."), | |
35545456 KZ |
441 | filename); |
442 | else if (size % 512) | |
97b820bf | 443 | warnx(_("%s: Warning: file does not fit into a 512-byte sector; " |
35545456 KZ |
444 | "the end of the file will be ignored."), |
445 | filename); | |
446 | } | |
447 | ||
9a94b634 KZ |
448 | static int create_loop(struct loopdev_cxt *lc, |
449 | int nooverlap, int lo_flags, int flags, | |
450 | const char *file, uint64_t offset, uint64_t sizelimit) | |
451 | { | |
452 | int hasdev = loopcxt_has_device(lc); | |
453 | int rc = 0; | |
454 | ||
d8ba61fc | 455 | /* losetup --find --noverlap file.img */ |
9a94b634 KZ |
456 | if (!hasdev && nooverlap) { |
457 | rc = loopcxt_find_overlap(lc, file, offset, sizelimit); | |
458 | switch (rc) { | |
459 | case 0: /* not found */ | |
460 | break; | |
461 | ||
462 | case 1: /* overlap */ | |
463 | loopcxt_deinit(lc); | |
464 | errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file); | |
465 | ||
466 | case 2: /* overlap -- full size and offset match (reuse) */ | |
467 | { | |
468 | uint32_t lc_encrypt_type; | |
469 | ||
470 | /* Once a loop is initialized RO, there is no | |
471 | * way to change its parameters. */ | |
472 | if (loopcxt_is_readonly(lc) | |
473 | && !(lo_flags & LO_FLAGS_READ_ONLY)) { | |
474 | loopcxt_deinit(lc); | |
475 | errx(EXIT_FAILURE, _("%s: overlapping read-only loop device exists"), file); | |
476 | } | |
477 | ||
478 | /* This is no more supported, but check to be safe. */ | |
479 | if (loopcxt_get_encrypt_type(lc, &lc_encrypt_type) == 0 | |
480 | && lc_encrypt_type != LO_CRYPT_NONE) { | |
481 | loopcxt_deinit(lc); | |
482 | errx(EXIT_FAILURE, _("%s: overlapping encrypted loop device exists"), file); | |
483 | } | |
f27d989c SB |
484 | |
485 | lc->info.lo_flags &= !LO_FLAGS_AUTOCLEAR; | |
486 | if (loopcxt_set_status(lc)) { | |
487 | loopcxt_deinit(lc); | |
488 | errx(EXIT_FAILURE, _("%s: failed to re-use loop device"), file); | |
489 | } | |
9a94b634 KZ |
490 | return 0; /* success, re-use */ |
491 | } | |
492 | default: /* error */ | |
493 | loopcxt_deinit(lc); | |
494 | errx(EXIT_FAILURE, _("failed to inspect loop devices")); | |
495 | return -errno; | |
496 | } | |
497 | } | |
498 | ||
499 | if (hasdev && !is_loopdev(loopcxt_get_device(lc))) | |
500 | loopcxt_add_device(lc); | |
501 | ||
d8ba61fc KZ |
502 | /* losetup --noverlap /dev/loopN file.img */ |
503 | if (hasdev && nooverlap) { | |
504 | struct loopdev_cxt lc2; | |
505 | ||
506 | if (loopcxt_init(&lc2, 0)) { | |
507 | loopcxt_deinit(lc); | |
508 | err(EXIT_FAILURE, _("failed to initialize loopcxt")); | |
509 | } | |
510 | rc = loopcxt_find_overlap(&lc2, file, offset, sizelimit); | |
511 | loopcxt_deinit(&lc2); | |
512 | ||
513 | if (rc) { | |
514 | loopcxt_deinit(lc); | |
515 | if (rc > 0) | |
516 | errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file); | |
517 | err(EXIT_FAILURE, _("%s: failed to check for conflicting loop devices"), file); | |
518 | } | |
519 | } | |
520 | ||
9a94b634 KZ |
521 | /* Create a new device */ |
522 | do { | |
523 | const char *errpre; | |
524 | ||
525 | /* Note that loopcxt_{find_unused,set_device}() resets | |
526 | * loopcxt struct. | |
527 | */ | |
528 | if (!hasdev && (rc = loopcxt_find_unused(lc))) { | |
529 | warnx(_("cannot find an unused loop device")); | |
530 | break; | |
531 | } | |
532 | if (flags & LOOPDEV_FL_OFFSET) | |
533 | loopcxt_set_offset(lc, offset); | |
534 | if (flags & LOOPDEV_FL_SIZELIMIT) | |
535 | loopcxt_set_sizelimit(lc, sizelimit); | |
536 | if (lo_flags) | |
537 | loopcxt_set_flags(lc, lo_flags); | |
538 | if ((rc = loopcxt_set_backing_file(lc, file))) { | |
539 | warn(_("%s: failed to use backing file"), file); | |
540 | break; | |
541 | } | |
542 | errno = 0; | |
543 | rc = loopcxt_setup_device(lc); | |
544 | if (rc == 0) | |
545 | break; /* success */ | |
546 | if (errno == EBUSY && !hasdev) | |
547 | continue; | |
548 | ||
549 | /* errors */ | |
550 | errpre = hasdev && loopcxt_get_fd(lc) < 0 ? | |
551 | loopcxt_get_device(lc) : file; | |
552 | warn(_("%s: failed to set up loop device"), errpre); | |
553 | break; | |
554 | } while (hasdev == 0); | |
555 | ||
556 | return rc; | |
557 | } | |
558 | ||
39fde137 KZ |
559 | int main(int argc, char **argv) |
560 | { | |
561 | struct loopdev_cxt lc; | |
9a94b634 | 562 | int act = 0, flags = 0, no_overlap = 0, c; |
5cf05c71 | 563 | char *file = NULL; |
c7e0925d | 564 | uint64_t offset = 0, sizelimit = 0; |
7e86cd54 | 565 | int res = 0, showdev = 0, lo_flags = 0; |
896352d3 OO |
566 | char *outarg = NULL; |
567 | int list = 0; | |
64c3bb3c | 568 | unsigned long use_dio = 0, set_dio = 0; |
6c7d5ae9 | 569 | |
aadb9303 KZ |
570 | enum { |
571 | OPT_SIZELIMIT = CHAR_MAX + 1, | |
9f56106d | 572 | OPT_SHOW, |
64c3bb3c ML |
573 | OPT_RAW, |
574 | OPT_DIO | |
aadb9303 KZ |
575 | }; |
576 | static const struct option longopts[] = { | |
d99f0140 | 577 | { "all", 0, 0, 'a' }, |
6e90a44c | 578 | { "set-capacity", 1, 0, 'c' }, |
c654c4f0 | 579 | { "detach", 1, 0, 'd' }, |
34f9b684 | 580 | { "detach-all", 0, 0, 'D' }, |
d99f0140 | 581 | { "find", 0, 0, 'f' }, |
f3b0d150 | 582 | { "nooverlap", 0, 0, 'L' }, |
d99f0140 | 583 | { "help", 0, 0, 'h' }, |
259fcc57 | 584 | { "associated", 1, 0, 'j' }, |
4827093d | 585 | { "json", 0, 0, 'J' }, |
896352d3 | 586 | { "list", 0, 0, 'l' }, |
9f56106d | 587 | { "noheadings", 0, 0, 'n' }, |
d99f0140 | 588 | { "offset", 1, 0, 'o' }, |
896352d3 | 589 | { "output", 1, 0, 'O' }, |
aadb9303 | 590 | { "sizelimit", 1, 0, OPT_SIZELIMIT }, |
916bf85e | 591 | { "partscan", 0, 0, 'P' }, |
d99f0140 | 592 | { "read-only", 0, 0, 'r' }, |
f98d9641 | 593 | { "direct-io", 2, 0, OPT_DIO }, |
9f56106d | 594 | { "raw", 0, 0, OPT_RAW }, |
896352d3 | 595 | { "show", 0, 0, OPT_SHOW }, |
d99f0140 | 596 | { "verbose", 0, 0, 'v' }, |
aadb9303 | 597 | { "version", 0, 0, 'V' }, |
d99f0140 KZ |
598 | { NULL, 0, 0, 0 } |
599 | }; | |
22853e4a | 600 | |
c1ac3144 KZ |
601 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
602 | { 'D','a','c','d','f','j' }, | |
896352d3 | 603 | { 'D','c','d','f','l' }, |
65178cb3 | 604 | { 'D','c','d','f','O' }, |
4827093d | 605 | { 'J',OPT_RAW }, |
c1ac3144 KZ |
606 | { 0 } |
607 | }; | |
608 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
609 | ||
22853e4a KZ |
610 | setlocale(LC_ALL, ""); |
611 | bindtextdomain(PACKAGE, LOCALEDIR); | |
612 | textdomain(PACKAGE); | |
efb8854f | 613 | atexit(close_stdout); |
22853e4a | 614 | |
defa0710 KZ |
615 | if (loopcxt_init(&lc, 0)) |
616 | err(EXIT_FAILURE, _("failed to initialize loopcxt")); | |
617 | ||
9a94b634 | 618 | while ((c = getopt_long(argc, argv, "ac:d:Dfhj:JlLno:O:PrvV", |
d99f0140 | 619 | longopts, NULL)) != -1) { |
c1ac3144 KZ |
620 | |
621 | err_exclusive_options(c, longopts, excl, excl_st); | |
622 | ||
22853e4a | 623 | switch (c) { |
8b125fae | 624 | case 'a': |
39fde137 | 625 | act = A_SHOW; |
8b125fae | 626 | break; |
d34ac93a | 627 | case 'c': |
6e90a44c | 628 | act = A_SET_CAPACITY; |
21ce3f3a KZ |
629 | if (!is_loopdev(optarg) || |
630 | loopcxt_set_device(&lc, optarg)) | |
defa0710 KZ |
631 | err(EXIT_FAILURE, _("%s: failed to use device"), |
632 | optarg); | |
d34ac93a | 633 | break; |
faf142b6 | 634 | case 'r': |
c7e0925d | 635 | lo_flags |= LO_FLAGS_READ_ONLY; |
faf142b6 | 636 | break; |
22853e4a | 637 | case 'd': |
c654c4f0 | 638 | act = A_DELETE; |
21ce3f3a KZ |
639 | if (!is_loopdev(optarg) || |
640 | loopcxt_set_device(&lc, optarg)) | |
defa0710 KZ |
641 | err(EXIT_FAILURE, _("%s: failed to use device"), |
642 | optarg); | |
22853e4a | 643 | break; |
34f9b684 | 644 | case 'D': |
c654c4f0 | 645 | act = A_DELETE_ALL; |
34f9b684 | 646 | break; |
d162fcb5 | 647 | case 'f': |
bcdbdc72 | 648 | act = A_FIND_FREE; |
d162fcb5 | 649 | break; |
108591d2 KZ |
650 | case 'h': |
651 | usage(stdout); | |
652 | break; | |
4827093d KZ |
653 | case 'J': |
654 | json = 1; | |
655 | break; | |
259fcc57 | 656 | case 'j': |
bc0ac075 KZ |
657 | act = A_SHOW; |
658 | file = optarg; | |
259fcc57 | 659 | break; |
896352d3 OO |
660 | case 'l': |
661 | list = 1; | |
662 | break; | |
9a94b634 KZ |
663 | case 'L': |
664 | no_overlap = 1; | |
665 | break; | |
9f56106d | 666 | case 'n': |
7e86cd54 | 667 | no_headings = 1; |
9f56106d KZ |
668 | break; |
669 | case OPT_RAW: | |
7e86cd54 | 670 | raw = 1; |
9f56106d | 671 | break; |
22853e4a | 672 | case 'o': |
e9e426eb | 673 | offset = strtosize_or_err(optarg, _("failed to parse offset")); |
bc0ac075 | 674 | flags |= LOOPDEV_FL_OFFSET; |
22853e4a | 675 | break; |
896352d3 OO |
676 | case 'O': |
677 | outarg = optarg; | |
65178cb3 | 678 | list = 1; |
896352d3 | 679 | break; |
916bf85e KZ |
680 | case 'P': |
681 | lo_flags |= LO_FLAGS_PARTSCAN; | |
682 | break; | |
aadb9303 | 683 | case OPT_SHOW: |
ba3809b0 KZ |
684 | showdev = 1; |
685 | break; | |
64c3bb3c | 686 | case OPT_DIO: |
f98d9641 KZ |
687 | use_dio = set_dio = 1; |
688 | if (optarg) | |
689 | use_dio = parse_switch(optarg, _("argument error"), "on", "off", NULL); | |
64c3bb3c | 690 | break; |
22853e4a | 691 | case 'v': |
22853e4a | 692 | break; |
aadb9303 KZ |
693 | case 'V': |
694 | printf(UTIL_LINUX_VERSION); | |
695 | return EXIT_SUCCESS; | |
696 | case OPT_SIZELIMIT: /* --sizelimit */ | |
e9e426eb | 697 | sizelimit = strtosize_or_err(optarg, _("failed to parse size")); |
c7e0925d | 698 | flags |= LOOPDEV_FL_SIZELIMIT; |
7bcefc7f | 699 | break; |
22853e4a | 700 | default: |
108591d2 | 701 | usage(stderr); |
22853e4a KZ |
702 | } |
703 | } | |
d162fcb5 | 704 | |
896352d3 OO |
705 | /* default is --list --all */ |
706 | if (argc == 1) { | |
707 | act = A_SHOW; | |
708 | list = 1; | |
709 | } | |
710 | ||
4827093d KZ |
711 | if (!act && argc == 2 && (raw || json)) { |
712 | act = A_SHOW; | |
713 | list = 1; | |
714 | } | |
715 | ||
896352d3 OO |
716 | /* default --list output columns */ |
717 | if (list && !ncolumns) { | |
718 | columns[ncolumns++] = COL_NAME; | |
4ad996d7 | 719 | columns[ncolumns++] = COL_SIZELIMIT; |
896352d3 OO |
720 | columns[ncolumns++] = COL_OFFSET; |
721 | columns[ncolumns++] = COL_AUTOCLR; | |
722 | columns[ncolumns++] = COL_RO; | |
723 | columns[ncolumns++] = COL_BACK_FILE; | |
faeef4d2 | 724 | columns[ncolumns++] = COL_DIO; |
896352d3 | 725 | } |
39fde137 | 726 | |
c7e0925d KZ |
727 | if (act == A_FIND_FREE && optind < argc) { |
728 | /* | |
729 | * losetup -f <backing_file> | |
730 | */ | |
731 | act = A_CREATE; | |
732 | file = argv[optind++]; | |
733 | } | |
896352d3 OO |
734 | |
735 | if (list && !act && optind == argc) | |
736 | /* | |
737 | * losetup --list defaults to --all | |
738 | */ | |
739 | act = A_SHOW; | |
740 | ||
09ec0c0a KZ |
741 | if (!act && optind + 1 == argc) { |
742 | /* | |
896352d3 | 743 | * losetup [--list] <device> |
64c3bb3c | 744 | * OR |
c79657ca | 745 | * losetup --direct-io[=off] <device> |
09ec0c0a | 746 | */ |
64c3bb3c ML |
747 | if (!set_dio) |
748 | act = A_SHOW_ONE; | |
749 | else | |
750 | act = A_SET_DIRECT_IO; | |
21ce3f3a KZ |
751 | if (!is_loopdev(argv[optind]) || |
752 | loopcxt_set_device(&lc, argv[optind])) | |
defa0710 KZ |
753 | err(EXIT_FAILURE, _("%s: failed to use device"), |
754 | argv[optind]); | |
755 | optind++; | |
09ec0c0a | 756 | } |
c7e0925d KZ |
757 | if (!act) { |
758 | /* | |
759 | * losetup <loopdev> <backing_file> | |
760 | */ | |
761 | act = A_CREATE; | |
762 | ||
763 | if (optind >= argc) | |
764 | errx(EXIT_FAILURE, _("no loop device specified")); | |
21ce3f3a | 765 | /* don't use is_loopdev() here, the device does not have exist yet */ |
defa0710 | 766 | if (loopcxt_set_device(&lc, argv[optind])) |
e9e7698e | 767 | err(EXIT_FAILURE, _("%s: failed to use device"), |
defa0710 KZ |
768 | argv[optind]); |
769 | optind++; | |
c7e0925d KZ |
770 | |
771 | if (optind >= argc) | |
772 | errx(EXIT_FAILURE, _("no file specified")); | |
773 | file = argv[optind++]; | |
774 | } | |
775 | ||
776 | if (act != A_CREATE && | |
5cf05c71 | 777 | (sizelimit || lo_flags || showdev)) |
c7e0925d | 778 | errx(EXIT_FAILURE, |
136b23ef | 779 | _("the options %s are allowed during loop device setup only"), |
5cf05c71 | 780 | "--{sizelimit,read-only,show}"); |
c7e0925d | 781 | |
934df30d KZ |
782 | if ((flags & LOOPDEV_FL_OFFSET) && |
783 | act != A_CREATE && (act != A_SHOW || !file)) | |
136b23ef | 784 | errx(EXIT_FAILURE, _("the option --offset is not allowed in this context")); |
c654c4f0 | 785 | |
896352d3 OO |
786 | if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), |
787 | &ncolumns, column_name_to_id) < 0) | |
788 | return EXIT_FAILURE; | |
789 | ||
39fde137 | 790 | switch (act) { |
c7e0925d | 791 | case A_CREATE: |
9a94b634 | 792 | res = create_loop(&lc, no_overlap, lo_flags, flags, file, offset, sizelimit); |
35545456 KZ |
793 | if (res == 0) { |
794 | if (showdev) | |
795 | printf("%s\n", loopcxt_get_device(&lc)); | |
796 | warn_size(file, sizelimit); | |
64c3bb3c ML |
797 | if (set_dio) |
798 | goto lo_set_dio; | |
35545456 | 799 | } |
c7e0925d | 800 | break; |
c654c4f0 KZ |
801 | case A_DELETE: |
802 | res = delete_loop(&lc); | |
803 | while (optind < argc) { | |
21ce3f3a KZ |
804 | if (!is_loopdev(argv[optind]) || |
805 | loopcxt_set_device(&lc, argv[optind])) | |
defa0710 KZ |
806 | warn(_("%s: failed to use device"), |
807 | argv[optind]); | |
808 | optind++; | |
c654c4f0 KZ |
809 | res += delete_loop(&lc); |
810 | } | |
811 | break; | |
812 | case A_DELETE_ALL: | |
813 | res = delete_all_loops(&lc); | |
814 | break; | |
bcdbdc72 | 815 | case A_FIND_FREE: |
3e55659f | 816 | res = loopcxt_find_unused(&lc); |
362f5d20 KZ |
817 | if (res) { |
818 | int errsv = errno; | |
819 | ||
820 | if (access(_PATH_DEV_LOOPCTL, F_OK) == 0 && | |
821 | access(_PATH_DEV_LOOPCTL, W_OK) != 0) | |
822 | ; | |
823 | else | |
824 | errno = errsv; | |
825 | ||
136b23ef | 826 | warn(_("cannot find an unused loop device")); |
362f5d20 | 827 | } else |
bcdbdc72 KZ |
828 | printf("%s\n", loopcxt_get_device(&lc)); |
829 | break; | |
39fde137 | 830 | case A_SHOW: |
896352d3 | 831 | if (list) |
48f1f38b | 832 | res = show_table(&lc, file, offset, flags); |
896352d3 OO |
833 | else |
834 | res = show_all_loops(&lc, file, offset, flags); | |
bc0ac075 | 835 | break; |
09ec0c0a | 836 | case A_SHOW_ONE: |
896352d3 | 837 | if (list) |
48f1f38b | 838 | res = show_table(&lc, NULL, 0, 0); |
896352d3 OO |
839 | else |
840 | res = printf_loopdev(&lc); | |
09ec0c0a | 841 | if (res) |
136b23ef | 842 | warn("%s", loopcxt_get_device(&lc)); |
09ec0c0a | 843 | break; |
6e90a44c | 844 | case A_SET_CAPACITY: |
293714c0 JM |
845 | res = loopcxt_set_capacity(&lc); |
846 | if (res) | |
847 | warn(_("%s: set capacity failed"), | |
848 | loopcxt_get_device(&lc)); | |
6e90a44c | 849 | break; |
64c3bb3c ML |
850 | case A_SET_DIRECT_IO: |
851 | lo_set_dio: | |
852 | res = loopcxt_set_dio(&lc, use_dio); | |
853 | if (res) | |
854 | warn(_("%s: set direct io failed"), | |
855 | loopcxt_get_device(&lc)); | |
856 | break; | |
39fde137 | 857 | default: |
c7e0925d | 858 | usage(stderr); |
39fde137 KZ |
859 | break; |
860 | } | |
861 | ||
862 | loopcxt_deinit(&lc); | |
c7e0925d | 863 | return res ? EXIT_FAILURE : EXIT_SUCCESS; |
22853e4a KZ |
864 | } |
865 |