]>
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 | ||
455 | /* Check for conflicts and re-user loop device if possible */ | |
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 | } | |
484 | return 0; /* success, re-use */ | |
485 | } | |
486 | default: /* error */ | |
487 | loopcxt_deinit(lc); | |
488 | errx(EXIT_FAILURE, _("failed to inspect loop devices")); | |
489 | return -errno; | |
490 | } | |
491 | } | |
492 | ||
493 | if (hasdev && !is_loopdev(loopcxt_get_device(lc))) | |
494 | loopcxt_add_device(lc); | |
495 | ||
496 | /* Create a new device */ | |
497 | do { | |
498 | const char *errpre; | |
499 | ||
500 | /* Note that loopcxt_{find_unused,set_device}() resets | |
501 | * loopcxt struct. | |
502 | */ | |
503 | if (!hasdev && (rc = loopcxt_find_unused(lc))) { | |
504 | warnx(_("cannot find an unused loop device")); | |
505 | break; | |
506 | } | |
507 | if (flags & LOOPDEV_FL_OFFSET) | |
508 | loopcxt_set_offset(lc, offset); | |
509 | if (flags & LOOPDEV_FL_SIZELIMIT) | |
510 | loopcxt_set_sizelimit(lc, sizelimit); | |
511 | if (lo_flags) | |
512 | loopcxt_set_flags(lc, lo_flags); | |
513 | if ((rc = loopcxt_set_backing_file(lc, file))) { | |
514 | warn(_("%s: failed to use backing file"), file); | |
515 | break; | |
516 | } | |
517 | errno = 0; | |
518 | rc = loopcxt_setup_device(lc); | |
519 | if (rc == 0) | |
520 | break; /* success */ | |
521 | if (errno == EBUSY && !hasdev) | |
522 | continue; | |
523 | ||
524 | /* errors */ | |
525 | errpre = hasdev && loopcxt_get_fd(lc) < 0 ? | |
526 | loopcxt_get_device(lc) : file; | |
527 | warn(_("%s: failed to set up loop device"), errpre); | |
528 | break; | |
529 | } while (hasdev == 0); | |
530 | ||
531 | return rc; | |
532 | } | |
533 | ||
39fde137 KZ |
534 | int main(int argc, char **argv) |
535 | { | |
536 | struct loopdev_cxt lc; | |
9a94b634 | 537 | int act = 0, flags = 0, no_overlap = 0, c; |
5cf05c71 | 538 | char *file = NULL; |
c7e0925d | 539 | uint64_t offset = 0, sizelimit = 0; |
7e86cd54 | 540 | int res = 0, showdev = 0, lo_flags = 0; |
896352d3 OO |
541 | char *outarg = NULL; |
542 | int list = 0; | |
64c3bb3c | 543 | unsigned long use_dio = 0, set_dio = 0; |
6c7d5ae9 | 544 | |
aadb9303 KZ |
545 | enum { |
546 | OPT_SIZELIMIT = CHAR_MAX + 1, | |
9f56106d | 547 | OPT_SHOW, |
64c3bb3c ML |
548 | OPT_RAW, |
549 | OPT_DIO | |
aadb9303 KZ |
550 | }; |
551 | static const struct option longopts[] = { | |
d99f0140 | 552 | { "all", 0, 0, 'a' }, |
6e90a44c | 553 | { "set-capacity", 1, 0, 'c' }, |
c654c4f0 | 554 | { "detach", 1, 0, 'd' }, |
34f9b684 | 555 | { "detach-all", 0, 0, 'D' }, |
d99f0140 | 556 | { "find", 0, 0, 'f' }, |
9a94b634 | 557 | { "nooverlaps", 0, 0, 'L' }, |
d99f0140 | 558 | { "help", 0, 0, 'h' }, |
259fcc57 | 559 | { "associated", 1, 0, 'j' }, |
4827093d | 560 | { "json", 0, 0, 'J' }, |
896352d3 | 561 | { "list", 0, 0, 'l' }, |
9f56106d | 562 | { "noheadings", 0, 0, 'n' }, |
d99f0140 | 563 | { "offset", 1, 0, 'o' }, |
896352d3 | 564 | { "output", 1, 0, 'O' }, |
aadb9303 | 565 | { "sizelimit", 1, 0, OPT_SIZELIMIT }, |
916bf85e | 566 | { "partscan", 0, 0, 'P' }, |
d99f0140 | 567 | { "read-only", 0, 0, 'r' }, |
f98d9641 | 568 | { "direct-io", 2, 0, OPT_DIO }, |
9f56106d | 569 | { "raw", 0, 0, OPT_RAW }, |
896352d3 | 570 | { "show", 0, 0, OPT_SHOW }, |
d99f0140 | 571 | { "verbose", 0, 0, 'v' }, |
aadb9303 | 572 | { "version", 0, 0, 'V' }, |
d99f0140 KZ |
573 | { NULL, 0, 0, 0 } |
574 | }; | |
22853e4a | 575 | |
c1ac3144 KZ |
576 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
577 | { 'D','a','c','d','f','j' }, | |
896352d3 | 578 | { 'D','c','d','f','l' }, |
65178cb3 | 579 | { 'D','c','d','f','O' }, |
4827093d | 580 | { 'J',OPT_RAW }, |
c1ac3144 KZ |
581 | { 0 } |
582 | }; | |
583 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
584 | ||
22853e4a KZ |
585 | setlocale(LC_ALL, ""); |
586 | bindtextdomain(PACKAGE, LOCALEDIR); | |
587 | textdomain(PACKAGE); | |
efb8854f | 588 | atexit(close_stdout); |
22853e4a | 589 | |
defa0710 KZ |
590 | if (loopcxt_init(&lc, 0)) |
591 | err(EXIT_FAILURE, _("failed to initialize loopcxt")); | |
592 | ||
9a94b634 | 593 | while ((c = getopt_long(argc, argv, "ac:d:Dfhj:JlLno:O:PrvV", |
d99f0140 | 594 | longopts, NULL)) != -1) { |
c1ac3144 KZ |
595 | |
596 | err_exclusive_options(c, longopts, excl, excl_st); | |
597 | ||
22853e4a | 598 | switch (c) { |
8b125fae | 599 | case 'a': |
39fde137 | 600 | act = A_SHOW; |
8b125fae | 601 | break; |
d34ac93a | 602 | case 'c': |
6e90a44c | 603 | act = A_SET_CAPACITY; |
21ce3f3a KZ |
604 | if (!is_loopdev(optarg) || |
605 | loopcxt_set_device(&lc, optarg)) | |
defa0710 KZ |
606 | err(EXIT_FAILURE, _("%s: failed to use device"), |
607 | optarg); | |
d34ac93a | 608 | break; |
faf142b6 | 609 | case 'r': |
c7e0925d | 610 | lo_flags |= LO_FLAGS_READ_ONLY; |
faf142b6 | 611 | break; |
22853e4a | 612 | case 'd': |
c654c4f0 | 613 | act = A_DELETE; |
21ce3f3a KZ |
614 | if (!is_loopdev(optarg) || |
615 | loopcxt_set_device(&lc, optarg)) | |
defa0710 KZ |
616 | err(EXIT_FAILURE, _("%s: failed to use device"), |
617 | optarg); | |
22853e4a | 618 | break; |
34f9b684 | 619 | case 'D': |
c654c4f0 | 620 | act = A_DELETE_ALL; |
34f9b684 | 621 | break; |
d162fcb5 | 622 | case 'f': |
bcdbdc72 | 623 | act = A_FIND_FREE; |
d162fcb5 | 624 | break; |
108591d2 KZ |
625 | case 'h': |
626 | usage(stdout); | |
627 | break; | |
4827093d KZ |
628 | case 'J': |
629 | json = 1; | |
630 | break; | |
259fcc57 | 631 | case 'j': |
bc0ac075 KZ |
632 | act = A_SHOW; |
633 | file = optarg; | |
259fcc57 | 634 | break; |
896352d3 OO |
635 | case 'l': |
636 | list = 1; | |
637 | break; | |
9a94b634 KZ |
638 | case 'L': |
639 | no_overlap = 1; | |
640 | break; | |
9f56106d | 641 | case 'n': |
7e86cd54 | 642 | no_headings = 1; |
9f56106d KZ |
643 | break; |
644 | case OPT_RAW: | |
7e86cd54 | 645 | raw = 1; |
9f56106d | 646 | break; |
22853e4a | 647 | case 'o': |
e9e426eb | 648 | offset = strtosize_or_err(optarg, _("failed to parse offset")); |
bc0ac075 | 649 | flags |= LOOPDEV_FL_OFFSET; |
22853e4a | 650 | break; |
896352d3 OO |
651 | case 'O': |
652 | outarg = optarg; | |
65178cb3 | 653 | list = 1; |
896352d3 | 654 | break; |
916bf85e KZ |
655 | case 'P': |
656 | lo_flags |= LO_FLAGS_PARTSCAN; | |
657 | break; | |
aadb9303 | 658 | case OPT_SHOW: |
ba3809b0 KZ |
659 | showdev = 1; |
660 | break; | |
64c3bb3c | 661 | case OPT_DIO: |
f98d9641 KZ |
662 | use_dio = set_dio = 1; |
663 | if (optarg) | |
664 | use_dio = parse_switch(optarg, _("argument error"), "on", "off", NULL); | |
64c3bb3c | 665 | break; |
22853e4a | 666 | case 'v': |
22853e4a | 667 | break; |
aadb9303 KZ |
668 | case 'V': |
669 | printf(UTIL_LINUX_VERSION); | |
670 | return EXIT_SUCCESS; | |
671 | case OPT_SIZELIMIT: /* --sizelimit */ | |
e9e426eb | 672 | sizelimit = strtosize_or_err(optarg, _("failed to parse size")); |
c7e0925d | 673 | flags |= LOOPDEV_FL_SIZELIMIT; |
7bcefc7f | 674 | break; |
22853e4a | 675 | default: |
108591d2 | 676 | usage(stderr); |
22853e4a KZ |
677 | } |
678 | } | |
d162fcb5 | 679 | |
896352d3 OO |
680 | /* default is --list --all */ |
681 | if (argc == 1) { | |
682 | act = A_SHOW; | |
683 | list = 1; | |
684 | } | |
685 | ||
4827093d KZ |
686 | if (!act && argc == 2 && (raw || json)) { |
687 | act = A_SHOW; | |
688 | list = 1; | |
689 | } | |
690 | ||
896352d3 OO |
691 | /* default --list output columns */ |
692 | if (list && !ncolumns) { | |
693 | columns[ncolumns++] = COL_NAME; | |
4ad996d7 | 694 | columns[ncolumns++] = COL_SIZELIMIT; |
896352d3 OO |
695 | columns[ncolumns++] = COL_OFFSET; |
696 | columns[ncolumns++] = COL_AUTOCLR; | |
697 | columns[ncolumns++] = COL_RO; | |
698 | columns[ncolumns++] = COL_BACK_FILE; | |
faeef4d2 | 699 | columns[ncolumns++] = COL_DIO; |
896352d3 | 700 | } |
39fde137 | 701 | |
c7e0925d KZ |
702 | if (act == A_FIND_FREE && optind < argc) { |
703 | /* | |
704 | * losetup -f <backing_file> | |
705 | */ | |
706 | act = A_CREATE; | |
707 | file = argv[optind++]; | |
708 | } | |
896352d3 OO |
709 | |
710 | if (list && !act && optind == argc) | |
711 | /* | |
712 | * losetup --list defaults to --all | |
713 | */ | |
714 | act = A_SHOW; | |
715 | ||
09ec0c0a KZ |
716 | if (!act && optind + 1 == argc) { |
717 | /* | |
896352d3 | 718 | * losetup [--list] <device> |
64c3bb3c | 719 | * OR |
c79657ca | 720 | * losetup --direct-io[=off] <device> |
09ec0c0a | 721 | */ |
64c3bb3c ML |
722 | if (!set_dio) |
723 | act = A_SHOW_ONE; | |
724 | else | |
725 | act = A_SET_DIRECT_IO; | |
21ce3f3a KZ |
726 | if (!is_loopdev(argv[optind]) || |
727 | loopcxt_set_device(&lc, argv[optind])) | |
defa0710 KZ |
728 | err(EXIT_FAILURE, _("%s: failed to use device"), |
729 | argv[optind]); | |
730 | optind++; | |
09ec0c0a | 731 | } |
c7e0925d KZ |
732 | if (!act) { |
733 | /* | |
734 | * losetup <loopdev> <backing_file> | |
735 | */ | |
736 | act = A_CREATE; | |
737 | ||
738 | if (optind >= argc) | |
739 | errx(EXIT_FAILURE, _("no loop device specified")); | |
21ce3f3a | 740 | /* don't use is_loopdev() here, the device does not have exist yet */ |
defa0710 | 741 | if (loopcxt_set_device(&lc, argv[optind])) |
e9e7698e | 742 | err(EXIT_FAILURE, _("%s: failed to use device"), |
defa0710 KZ |
743 | argv[optind]); |
744 | optind++; | |
c7e0925d KZ |
745 | |
746 | if (optind >= argc) | |
747 | errx(EXIT_FAILURE, _("no file specified")); | |
748 | file = argv[optind++]; | |
749 | } | |
750 | ||
751 | if (act != A_CREATE && | |
5cf05c71 | 752 | (sizelimit || lo_flags || showdev)) |
c7e0925d | 753 | errx(EXIT_FAILURE, |
136b23ef | 754 | _("the options %s are allowed during loop device setup only"), |
5cf05c71 | 755 | "--{sizelimit,read-only,show}"); |
c7e0925d | 756 | |
934df30d KZ |
757 | if ((flags & LOOPDEV_FL_OFFSET) && |
758 | act != A_CREATE && (act != A_SHOW || !file)) | |
136b23ef | 759 | errx(EXIT_FAILURE, _("the option --offset is not allowed in this context")); |
c654c4f0 | 760 | |
896352d3 OO |
761 | if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), |
762 | &ncolumns, column_name_to_id) < 0) | |
763 | return EXIT_FAILURE; | |
764 | ||
39fde137 | 765 | switch (act) { |
c7e0925d | 766 | case A_CREATE: |
9a94b634 | 767 | res = create_loop(&lc, no_overlap, lo_flags, flags, file, offset, sizelimit); |
35545456 KZ |
768 | if (res == 0) { |
769 | if (showdev) | |
770 | printf("%s\n", loopcxt_get_device(&lc)); | |
771 | warn_size(file, sizelimit); | |
64c3bb3c ML |
772 | if (set_dio) |
773 | goto lo_set_dio; | |
35545456 | 774 | } |
c7e0925d | 775 | break; |
c654c4f0 KZ |
776 | case A_DELETE: |
777 | res = delete_loop(&lc); | |
778 | while (optind < argc) { | |
21ce3f3a KZ |
779 | if (!is_loopdev(argv[optind]) || |
780 | loopcxt_set_device(&lc, argv[optind])) | |
defa0710 KZ |
781 | warn(_("%s: failed to use device"), |
782 | argv[optind]); | |
783 | optind++; | |
c654c4f0 KZ |
784 | res += delete_loop(&lc); |
785 | } | |
786 | break; | |
787 | case A_DELETE_ALL: | |
788 | res = delete_all_loops(&lc); | |
789 | break; | |
bcdbdc72 | 790 | case A_FIND_FREE: |
3e55659f | 791 | res = loopcxt_find_unused(&lc); |
362f5d20 KZ |
792 | if (res) { |
793 | int errsv = errno; | |
794 | ||
795 | if (access(_PATH_DEV_LOOPCTL, F_OK) == 0 && | |
796 | access(_PATH_DEV_LOOPCTL, W_OK) != 0) | |
797 | ; | |
798 | else | |
799 | errno = errsv; | |
800 | ||
136b23ef | 801 | warn(_("cannot find an unused loop device")); |
362f5d20 | 802 | } else |
bcdbdc72 KZ |
803 | printf("%s\n", loopcxt_get_device(&lc)); |
804 | break; | |
39fde137 | 805 | case A_SHOW: |
896352d3 | 806 | if (list) |
48f1f38b | 807 | res = show_table(&lc, file, offset, flags); |
896352d3 OO |
808 | else |
809 | res = show_all_loops(&lc, file, offset, flags); | |
bc0ac075 | 810 | break; |
09ec0c0a | 811 | case A_SHOW_ONE: |
896352d3 | 812 | if (list) |
48f1f38b | 813 | res = show_table(&lc, NULL, 0, 0); |
896352d3 OO |
814 | else |
815 | res = printf_loopdev(&lc); | |
09ec0c0a | 816 | if (res) |
136b23ef | 817 | warn("%s", loopcxt_get_device(&lc)); |
09ec0c0a | 818 | break; |
6e90a44c | 819 | case A_SET_CAPACITY: |
293714c0 JM |
820 | res = loopcxt_set_capacity(&lc); |
821 | if (res) | |
822 | warn(_("%s: set capacity failed"), | |
823 | loopcxt_get_device(&lc)); | |
6e90a44c | 824 | break; |
64c3bb3c ML |
825 | case A_SET_DIRECT_IO: |
826 | lo_set_dio: | |
827 | res = loopcxt_set_dio(&lc, use_dio); | |
828 | if (res) | |
829 | warn(_("%s: set direct io failed"), | |
830 | loopcxt_get_device(&lc)); | |
831 | break; | |
39fde137 | 832 | default: |
c7e0925d | 833 | usage(stderr); |
39fde137 KZ |
834 | break; |
835 | } | |
836 | ||
837 | loopcxt_deinit(&lc); | |
c7e0925d | 838 | return res ? EXIT_FAILURE : EXIT_SUCCESS; |
22853e4a KZ |
839 | } |
840 |