]>
Commit | Line | Data |
---|---|---|
c49057d2 | 1 | /* |
870768ad | 2 | * wipefs - utility to wipe filesystems from device |
c49057d2 KZ |
3 | * |
4 | * Copyright (C) 2009 Red Hat, Inc. All rights reserved. | |
5 | * Written by Karel Zak <kzak@redhat.com> | |
6 | * | |
bfbe2933 KZ |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
c49057d2 KZ |
11 | * |
12 | * This program is distributed in the hope that it would be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
7cebf0bb SK |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
c49057d2 KZ |
20 | */ |
21 | #include <sys/stat.h> | |
22 | #include <sys/types.h> | |
23 | #include <ctype.h> | |
24 | #include <errno.h> | |
25 | #include <fcntl.h> | |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <unistd.h> | |
29 | #include <getopt.h> | |
c49057d2 KZ |
30 | #include <string.h> |
31 | #include <limits.h> | |
8acff75a | 32 | #include <libgen.h> |
c49057d2 KZ |
33 | |
34 | #include <blkid.h> | |
d9921b2a | 35 | #include <libsmartcols.h> |
c49057d2 KZ |
36 | |
37 | #include "nls.h" | |
87f3feac | 38 | #include "xalloc.h" |
8abcf290 | 39 | #include "strutils.h" |
e12c9866 | 40 | #include "all-io.h" |
f126cd46 | 41 | #include "match.h" |
eb76ca98 | 42 | #include "c.h" |
c05a80ca | 43 | #include "closestream.h" |
58f0252a | 44 | #include "optutils.h" |
081dc33b | 45 | #include "blkdev.h" |
c49057d2 KZ |
46 | |
47 | struct wipe_desc { | |
48 | loff_t offset; /* magic string offset */ | |
49 | size_t len; /* length of magic string */ | |
50 | unsigned char *magic; /* magic string */ | |
51 | ||
c49057d2 KZ |
52 | char *usage; /* raid, filesystem, ... */ |
53 | char *type; /* FS type */ | |
54 | char *label; /* FS label */ | |
55 | char *uuid; /* FS uuid */ | |
56 | ||
57 | struct wipe_desc *next; | |
081dc33b | 58 | |
0e45b256 | 59 | unsigned int on_disk : 1, |
081dc33b KZ |
60 | is_parttable : 1; |
61 | ||
c49057d2 KZ |
62 | }; |
63 | ||
4e609281 | 64 | struct wipe_control { |
921f6343 | 65 | char *devname; |
0e45b256 | 66 | const char *type_pattern; /* -t <pattern> */ |
4e609281 | 67 | |
d9921b2a | 68 | struct libscols_table *outtab; |
0e45b256 | 69 | struct wipe_desc *offsets; /* -o <offset> -o <offset> ... */ |
d9921b2a | 70 | |
921f6343 KZ |
71 | size_t ndevs; /* number of devices to probe */ |
72 | ||
73 | char **reread; /* devices to BLKRRPART */ | |
74 | size_t nrereads; /* size of reread */ | |
75 | ||
4e609281 KZ |
76 | unsigned int noact : 1, |
77 | all : 1, | |
78 | quiet : 1, | |
79 | backup : 1, | |
80 | force : 1, | |
d9921b2a KZ |
81 | json : 1, |
82 | no_headings : 1, | |
4e609281 | 83 | parsable : 1; |
4f053737 | 84 | }; |
c49057d2 | 85 | |
d9921b2a KZ |
86 | |
87 | /* column IDs */ | |
88 | enum { | |
89 | COL_UUID = 0, | |
90 | COL_LABEL, | |
91 | COL_LEN, | |
92 | COL_TYPE, | |
93 | COL_OFFSET, | |
94 | COL_USAGE, | |
95 | COL_DEVICE | |
96 | }; | |
97 | ||
98 | /* column names */ | |
99 | struct colinfo { | |
100 | const char *name; /* header */ | |
101 | double whint; /* width hint (N < 1 is in percent of termwidth) */ | |
102 | int flags; /* SCOLS_FL_* */ | |
103 | const char *help; | |
104 | }; | |
105 | ||
106 | /* columns descriptions */ | |
107 | static const struct colinfo infos[] = { | |
108 | [COL_UUID] = {"UUID", 4, 0, N_("partition/filesystem UUID")}, | |
109 | [COL_LABEL] = {"LABEL", 5, 0, N_("filesystem LABEL")}, | |
110 | [COL_LEN] = {"LENGTH", 6, 0, N_("magic string length")}, | |
111 | [COL_TYPE] = {"TYPE", 4, 0, N_("superblok type")}, | |
112 | [COL_OFFSET] = {"OFFSET", 5, 0, N_("magic string offset")}, | |
113 | [COL_USAGE] = {"USAGE", 5, 0, N_("type description")}, | |
114 | [COL_DEVICE] = {"DEVICE", 5, 0, N_("block device name")} | |
115 | }; | |
116 | ||
117 | static int columns[ARRAY_SIZE(infos) * 2]; | |
118 | static size_t ncolumns; | |
119 | ||
120 | static int column_name_to_id(const char *name, size_t namesz) | |
c49057d2 | 121 | { |
d9921b2a KZ |
122 | size_t i; |
123 | ||
124 | assert(name); | |
125 | ||
126 | for (i = 0; i < ARRAY_SIZE(infos); i++) { | |
127 | const char *cn = infos[i].name; | |
128 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
129 | return i; | |
c49057d2 | 130 | } |
d9921b2a KZ |
131 | warnx(_("unknown column: %s"), name); |
132 | return -1; | |
133 | } | |
c49057d2 | 134 | |
d9921b2a KZ |
135 | static int get_column_id(size_t num) |
136 | { | |
137 | assert(num < ncolumns); | |
138 | assert(columns[num] < (int)ARRAY_SIZE(infos)); | |
139 | return columns[num]; | |
140 | } | |
c49057d2 | 141 | |
d9921b2a KZ |
142 | static const struct colinfo *get_column_info(int num) |
143 | { | |
144 | return &infos[get_column_id(num)]; | |
c49057d2 KZ |
145 | } |
146 | ||
d9921b2a KZ |
147 | |
148 | static void init_output(struct wipe_control *ctl) | |
c49057d2 | 149 | { |
d9921b2a KZ |
150 | struct libscols_table *tb; |
151 | size_t i; | |
c49057d2 | 152 | |
d9921b2a KZ |
153 | scols_init_debug(0); |
154 | tb = scols_new_table(); | |
155 | if (!tb) | |
156 | err(EXIT_FAILURE, _("failed to allocate output table")); | |
c49057d2 | 157 | |
d9921b2a KZ |
158 | if (ctl->json) { |
159 | scols_table_enable_json(tb, 1); | |
160 | scols_table_set_name(tb, "signatures"); | |
161 | } | |
162 | scols_table_enable_noheadings(tb, ctl->no_headings); | |
c49057d2 | 163 | |
d9921b2a KZ |
164 | if (ctl->parsable) { |
165 | scols_table_enable_raw(tb, 1); | |
166 | scols_table_set_column_separator(tb, ","); | |
167 | } | |
c49057d2 | 168 | |
d9921b2a KZ |
169 | for (i = 0; i < ncolumns; i++) { |
170 | const struct colinfo *col = get_column_info(i); | |
a3571e1b | 171 | struct libscols_column *cl; |
c49057d2 | 172 | |
a3571e1b KZ |
173 | cl = scols_table_new_column(tb, col->name, col->whint, |
174 | col->flags); | |
175 | if (!cl) | |
d9921b2a KZ |
176 | err(EXIT_FAILURE, |
177 | _("failed to initialize output column")); | |
a3571e1b KZ |
178 | if (ctl->json) { |
179 | int id = get_column_id(i); | |
180 | ||
181 | if (id == COL_LEN) | |
182 | scols_column_set_json_type(cl, SCOLS_JSON_NUMBER); | |
183 | } | |
d9921b2a KZ |
184 | } |
185 | ctl->outtab = tb; | |
c49057d2 KZ |
186 | } |
187 | ||
d9921b2a KZ |
188 | static void finalize_output(struct wipe_control *ctl) |
189 | { | |
190 | if (ctl->parsable && !ctl->no_headings | |
191 | && !scols_table_is_empty(ctl->outtab)) { | |
192 | struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); | |
193 | struct libscols_column *cl; | |
194 | int i = 0; | |
195 | ||
196 | if (!itr) | |
197 | err_oom(); | |
198 | ||
199 | fputs("# ", stdout); | |
200 | while (scols_table_next_column(ctl->outtab, itr, &cl) == 0) { | |
201 | struct libscols_cell *hdr = scols_column_get_header(cl); | |
202 | const char *name = scols_cell_get_data(hdr); | |
203 | ||
204 | if (i) | |
205 | fputc(',', stdout); | |
206 | fputs(name, stdout); | |
207 | i++; | |
208 | } | |
209 | fputc('\n', stdout); | |
210 | scols_free_iter(itr); | |
211 | } | |
212 | scols_print_table(ctl->outtab); | |
213 | scols_unref_table(ctl->outtab); | |
214 | } | |
215 | ||
216 | static void fill_table_row(struct wipe_control *ctl, struct wipe_desc *wp) | |
c49057d2 | 217 | { |
d9921b2a KZ |
218 | static struct libscols_line *ln; |
219 | size_t i; | |
220 | ||
221 | ln = scols_table_new_line(ctl->outtab, NULL); | |
222 | if (!ln) | |
223 | errx(EXIT_FAILURE, _("failed to allocate output line")); | |
224 | ||
225 | for (i = 0; i < ncolumns; i++) { | |
226 | char *str = NULL; | |
227 | ||
228 | switch (get_column_id(i)) { | |
229 | case COL_UUID: | |
230 | if (wp->uuid) | |
231 | str = xstrdup(wp->uuid); | |
232 | break; | |
233 | case COL_LABEL: | |
234 | if (wp->label) | |
235 | str = xstrdup(wp->label); | |
236 | break; | |
237 | case COL_OFFSET: | |
238 | xasprintf(&str, "0x%jx", (intmax_t)wp->offset); | |
239 | break; | |
240 | case COL_LEN: | |
241 | xasprintf(&str, "%zu", wp->len); | |
242 | break; | |
243 | case COL_USAGE: | |
244 | if (wp->usage) | |
245 | str = xstrdup(wp->usage); | |
246 | break; | |
247 | case COL_TYPE: | |
248 | if (wp->type) | |
249 | str = xstrdup(wp->type); | |
250 | break; | |
251 | case COL_DEVICE: | |
252 | if (ctl->devname) { | |
253 | char *dev = xstrdup(ctl->devname); | |
254 | str = xstrdup(basename(dev)); | |
255 | free(dev); | |
256 | } | |
257 | break; | |
258 | default: | |
259 | abort(); | |
260 | } | |
c49057d2 | 261 | |
d9921b2a KZ |
262 | if (str && scols_line_refer_data(ln, i, str)) |
263 | errx(EXIT_FAILURE, _("failed to add output data")); | |
c49057d2 KZ |
264 | } |
265 | } | |
266 | ||
d9921b2a KZ |
267 | static void add_to_output(struct wipe_control *ctl, struct wipe_desc *wp) |
268 | { | |
269 | for (/*nothing*/; wp; wp = wp->next) | |
270 | fill_table_row(ctl, wp); | |
271 | } | |
272 | ||
0e45b256 KZ |
273 | /* Allocates a new wipe_desc and add to the wp0 if not NULL */ |
274 | static struct wipe_desc *add_offset(struct wipe_desc **wp0, loff_t offset) | |
c49057d2 | 275 | { |
0e45b256 KZ |
276 | struct wipe_desc *wp, *last = NULL; |
277 | ||
278 | if (wp0) { | |
279 | /* check if already exists */ | |
280 | for (wp = *wp0; wp; wp = wp->next) { | |
281 | if (wp->offset == offset) | |
282 | return wp; | |
283 | last = wp; | |
284 | } | |
c49057d2 KZ |
285 | } |
286 | ||
a2f5c221 | 287 | wp = xcalloc(1, sizeof(struct wipe_desc)); |
c49057d2 | 288 | wp->offset = offset; |
0e45b256 | 289 | wp->next = NULL; |
c10695f8 | 290 | |
0e45b256 KZ |
291 | if (last) |
292 | last->next = wp; | |
293 | if (wp0 && !*wp0) | |
294 | *wp0 = wp; | |
c10695f8 MB |
295 | return wp; |
296 | } | |
297 | ||
0e45b256 KZ |
298 | /* Read data from libblkid and if detected type pass -t and -o filters than: |
299 | * - allocates a new wipe_desc | |
300 | * - add the new wipe_desc to wp0 list (if not NULL) | |
301 | * | |
302 | * The function always returns offset and len if libblkid detected something. | |
303 | */ | |
304 | static struct wipe_desc *get_desc_for_probe(struct wipe_control *ctl, | |
305 | struct wipe_desc **wp0, | |
306 | blkid_probe pr, | |
307 | loff_t *offset, | |
308 | size_t *len) | |
c49057d2 | 309 | { |
9abc8a42 | 310 | const char *off, *type, *mag, *p, *use = NULL; |
0e45b256 | 311 | struct wipe_desc *wp; |
081dc33b | 312 | int rc, ispt = 0; |
c49057d2 | 313 | |
0e45b256 | 314 | *len = 0; |
193d6f27 | 315 | |
44765fdd KZ |
316 | /* superblocks */ |
317 | if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) == 0) { | |
318 | rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); | |
319 | if (!rc) | |
0e45b256 | 320 | rc = blkid_probe_lookup_value(pr, "SBMAGIC", &mag, len); |
44765fdd | 321 | if (rc) |
0e45b256 | 322 | return NULL; |
c49057d2 | 323 | |
44765fdd KZ |
324 | /* partitions */ |
325 | } else if (blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL) == 0) { | |
326 | rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL); | |
327 | if (!rc) | |
0e45b256 | 328 | rc = blkid_probe_lookup_value(pr, "PTMAGIC", &mag, len); |
44765fdd | 329 | if (rc) |
0e45b256 | 330 | return NULL; |
9abc8a42 | 331 | use = N_("partition-table"); |
081dc33b | 332 | ispt = 1; |
44765fdd | 333 | } else |
0e45b256 KZ |
334 | return NULL; |
335 | ||
336 | *offset = strtoll(off, NULL, 10); | |
c49057d2 | 337 | |
0e45b256 | 338 | /* Filter out by -t <type> */ |
4e609281 | 339 | if (ctl->type_pattern && !match_fstype(type, ctl->type_pattern)) |
0e45b256 KZ |
340 | return NULL; |
341 | ||
342 | /* Filter out by -o <offset> */ | |
343 | if (ctl->offsets) { | |
344 | struct wipe_desc *w = NULL; | |
f126cd46 | 345 | |
0e45b256 KZ |
346 | for (w = ctl->offsets; w; w = w->next) { |
347 | if (w->offset == *offset) | |
348 | break; | |
349 | } | |
350 | if (!w) | |
351 | return NULL; | |
352 | ||
353 | w->on_disk = 1; /* mark as "found" */ | |
354 | } | |
c49057d2 | 355 | |
0e45b256 | 356 | wp = add_offset(wp0, *offset); |
44765fdd KZ |
357 | if (!wp) |
358 | return NULL; | |
359 | ||
9abc8a42 SK |
360 | if (use || blkid_probe_lookup_value(pr, "USAGE", &use, NULL) == 0) |
361 | wp->usage = xstrdup(use); | |
c49057d2 | 362 | |
44765fdd KZ |
363 | wp->type = xstrdup(type); |
364 | wp->on_disk = 1; | |
081dc33b | 365 | wp->is_parttable = ispt ? 1 : 0; |
c49057d2 | 366 | |
0e45b256 KZ |
367 | wp->magic = xmalloc(*len); |
368 | memcpy(wp->magic, mag, *len); | |
369 | wp->len = *len; | |
c49057d2 | 370 | |
44765fdd KZ |
371 | if (blkid_probe_lookup_value(pr, "LABEL", &p, NULL) == 0) |
372 | wp->label = xstrdup(p); | |
373 | ||
374 | if (blkid_probe_lookup_value(pr, "UUID", &p, NULL) == 0) | |
375 | wp->uuid = xstrdup(p); | |
c49057d2 KZ |
376 | |
377 | return wp; | |
378 | } | |
379 | ||
6611a3dd KZ |
380 | static blkid_probe |
381 | new_probe(const char *devname, int mode) | |
c49057d2 | 382 | { |
4e6fc113 | 383 | blkid_probe pr = NULL; |
c49057d2 | 384 | |
6611a3dd | 385 | if (!devname) |
c49057d2 KZ |
386 | return NULL; |
387 | ||
6611a3dd KZ |
388 | if (mode) { |
389 | int fd = open(devname, mode); | |
390 | if (fd < 0) | |
391 | goto error; | |
392 | ||
393 | pr = blkid_new_probe(); | |
f1b64c1c | 394 | if (!pr || blkid_probe_set_device(pr, fd, 0, 0) != 0) { |
4e6fc113 | 395 | close(fd); |
6611a3dd | 396 | goto error; |
4e6fc113 | 397 | } |
6611a3dd KZ |
398 | } else |
399 | pr = blkid_new_probe_from_filename(devname); | |
400 | ||
c49057d2 | 401 | if (!pr) |
6611a3dd | 402 | goto error; |
269c1a2a | 403 | |
c49057d2 | 404 | blkid_probe_enable_superblocks(pr, 1); |
90faf9eb KZ |
405 | blkid_probe_set_superblocks_flags(pr, |
406 | BLKID_SUBLKS_MAGIC | /* return magic string and offset */ | |
407 | BLKID_SUBLKS_TYPE | /* return superblock type */ | |
408 | BLKID_SUBLKS_USAGE | /* return USAGE= */ | |
409 | BLKID_SUBLKS_LABEL | /* return LABEL= */ | |
410 | BLKID_SUBLKS_UUID | /* return UUID= */ | |
411 | BLKID_SUBLKS_BADCSUM); /* accept bad checksums */ | |
c49057d2 | 412 | |
44765fdd | 413 | blkid_probe_enable_partitions(pr, 1); |
949e5c96 KZ |
414 | blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC | |
415 | BLKID_PARTS_FORCE_GPT); | |
6611a3dd KZ |
416 | return pr; |
417 | error: | |
4e6fc113 | 418 | blkid_free_probe(pr); |
6611a3dd | 419 | err(EXIT_FAILURE, _("error: %s: probing initialization failed"), devname); |
6611a3dd KZ |
420 | } |
421 | ||
0e45b256 | 422 | static struct wipe_desc *read_offsets(struct wipe_control *ctl) |
6611a3dd | 423 | { |
4e609281 | 424 | blkid_probe pr = new_probe(ctl->devname, 0); |
0e45b256 | 425 | struct wipe_desc *wp0 = NULL; |
6611a3dd KZ |
426 | |
427 | if (!pr) | |
428 | return NULL; | |
429 | ||
c49057d2 | 430 | while (blkid_do_probe(pr) == 0) { |
0e45b256 KZ |
431 | size_t len = 0; |
432 | loff_t offset = 0; | |
193d6f27 | 433 | |
0e45b256 KZ |
434 | /* add a new offset to wp0 */ |
435 | get_desc_for_probe(ctl, &wp0, pr, &offset, &len); | |
92296e9b KZ |
436 | |
437 | /* hide last detected signature and scan again */ | |
0e45b256 KZ |
438 | if (len) { |
439 | blkid_probe_hide_range(pr, offset, len); | |
193d6f27 KZ |
440 | blkid_probe_step_back(pr); |
441 | } | |
c49057d2 KZ |
442 | } |
443 | ||
444 | blkid_free_probe(pr); | |
0e45b256 | 445 | return wp0; |
c49057d2 KZ |
446 | } |
447 | ||
0e45b256 | 448 | static void free_wipe(struct wipe_desc *wp) |
24df2633 MB |
449 | { |
450 | while (wp) { | |
451 | struct wipe_desc *next = wp->next; | |
452 | ||
453 | free(wp->usage); | |
454 | free(wp->type); | |
455 | free(wp->magic); | |
456 | free(wp->label); | |
457 | free(wp->uuid); | |
458 | free(wp); | |
459 | ||
460 | wp = next; | |
461 | } | |
462 | } | |
463 | ||
4e609281 KZ |
464 | static void do_wipe_real(struct wipe_control *ctl, blkid_probe pr, |
465 | struct wipe_desc *w) | |
24df2633 MB |
466 | { |
467 | size_t i; | |
468 | ||
4e609281 | 469 | if (blkid_do_wipe(pr, ctl->noact) != 0) |
6f1507f1 | 470 | err(EXIT_FAILURE, _("%s: failed to erase %s magic string at offset 0x%08jx"), |
4e609281 | 471 | ctl->devname, w->type, (intmax_t)w->offset); |
24df2633 | 472 | |
4e609281 | 473 | if (ctl->quiet) |
24df2633 MB |
474 | return; |
475 | ||
1619e3e7 BS |
476 | printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ", |
477 | "%s: %zd bytes were erased at offset 0x%08jx (%s): ", | |
478 | w->len), | |
4e609281 | 479 | ctl->devname, w->len, (intmax_t)w->offset, w->type); |
24df2633 MB |
480 | |
481 | for (i = 0; i < w->len; i++) { | |
482 | printf("%02x", w->magic[i]); | |
483 | if (i + 1 < w->len) | |
484 | fputc(' ', stdout); | |
485 | } | |
486 | putchar('\n'); | |
487 | } | |
488 | ||
7e658c15 OO |
489 | static void do_backup(struct wipe_desc *wp, const char *base) |
490 | { | |
491 | char *fname = NULL; | |
492 | int fd; | |
493 | ||
fdbd7bb9 | 494 | xasprintf(&fname, "%s0x%08jx.bak", base, (intmax_t)wp->offset); |
7e658c15 OO |
495 | |
496 | fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); | |
497 | if (fd < 0) | |
498 | goto err; | |
499 | if (write_all(fd, wp->magic, wp->len) != 0) | |
500 | goto err; | |
501 | close(fd); | |
502 | free(fname); | |
503 | return; | |
504 | err: | |
505 | err(EXIT_FAILURE, _("%s: failed to create a signature backup"), fname); | |
506 | } | |
507 | ||
11e88c86 | 508 | #ifdef BLKRRPART |
081dc33b KZ |
509 | static void rereadpt(int fd, const char *devname) |
510 | { | |
081dc33b | 511 | struct stat st; |
921f6343 | 512 | int try = 0; |
081dc33b KZ |
513 | |
514 | if (fstat(fd, &st) || !S_ISBLK(st.st_mode)) | |
515 | return; | |
516 | ||
921f6343 KZ |
517 | do { |
518 | /* | |
519 | * Unfortunately, it's pretty common that the first re-read | |
520 | * without delay is uncuccesful. The reason is probably kernel | |
521 | * and/or udevd. Let's wait a moment and try more attempts. | |
522 | */ | |
523 | xusleep(25000); | |
524 | ||
525 | errno = 0; | |
526 | ioctl(fd, BLKRRPART); | |
527 | if (errno != EBUSY) | |
528 | break; | |
529 | } while (try++ < 4); | |
530 | ||
a5c523a0 | 531 | printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname); |
081dc33b | 532 | } |
11e88c86 | 533 | #endif |
081dc33b | 534 | |
0e45b256 | 535 | static int do_wipe(struct wipe_control *ctl) |
c49057d2 | 536 | { |
24ed0781 | 537 | int mode = O_RDWR, reread = 0, need_force = 0; |
2968c3fc | 538 | blkid_probe pr; |
1666c155 | 539 | char *backup = NULL; |
0e45b256 | 540 | struct wipe_desc *w; |
c49057d2 | 541 | |
4e609281 | 542 | if (!ctl->force) |
1666c155 | 543 | mode |= O_EXCL; |
4e609281 KZ |
544 | |
545 | pr = new_probe(ctl->devname, mode); | |
6611a3dd | 546 | if (!pr) |
0e45b256 | 547 | return -errno; |
c49057d2 | 548 | |
0e45b256 | 549 | if (ctl->backup) { |
7e658c15 | 550 | const char *home = getenv ("HOME"); |
4e609281 | 551 | char *tmp = xstrdup(ctl->devname); |
8acff75a | 552 | |
7e658c15 OO |
553 | if (!home) |
554 | errx(EXIT_FAILURE, _("failed to create a signature backup, $HOME undefined")); | |
8acff75a RM |
555 | xasprintf (&backup, "%s/wipefs-%s-", home, basename(tmp)); |
556 | free(tmp); | |
7e658c15 OO |
557 | } |
558 | ||
6611a3dd | 559 | while (blkid_do_probe(pr) == 0) { |
2cd417ea | 560 | int wiped = 0; |
0e45b256 KZ |
561 | size_t len = 0; |
562 | loff_t offset = 0; | |
563 | struct wipe_desc *wp; | |
2cd417ea | 564 | |
0e45b256 | 565 | wp = get_desc_for_probe(ctl, NULL, pr, &offset, &len); |
24df2633 | 566 | if (!wp) |
2cd417ea | 567 | goto done; |
6611a3dd | 568 | |
4e609281 | 569 | if (!ctl->force |
24ed0781 KZ |
570 | && wp->is_parttable |
571 | && !blkid_probe_is_wholedisk(pr)) { | |
09af3db4 | 572 | warnx(_("%s: ignoring nested \"%s\" partition table " |
4e609281 | 573 | "on non-whole disk device"), ctl->devname, wp->type); |
24ed0781 | 574 | need_force = 1; |
2cd417ea | 575 | goto done; |
24ed0781 KZ |
576 | } |
577 | ||
0e45b256 KZ |
578 | if (backup) |
579 | do_backup(wp, backup); | |
580 | do_wipe_real(ctl, pr, wp); | |
581 | if (wp->is_parttable) | |
582 | reread = 1; | |
583 | wiped = 1; | |
2cd417ea | 584 | done: |
0e45b256 | 585 | if (!wiped && len) { |
2cd417ea KZ |
586 | /* if the offset has not been wiped (probably because |
587 | * filtered out by -t or -o) we need to hide it for | |
588 | * libblkid to try another magic string for the same | |
589 | * superblock, otherwise libblkid will continue with | |
590 | * another superblock. Don't forget that the same | |
591 | * superblock could be detected by more magic strings | |
592 | * */ | |
0e45b256 | 593 | blkid_probe_hide_range(pr, offset, len); |
2cd417ea | 594 | blkid_probe_step_back(pr); |
7e658c15 | 595 | } |
0e45b256 | 596 | free_wipe(wp); |
c49057d2 KZ |
597 | } |
598 | ||
0e45b256 | 599 | for (w = ctl->offsets; w; w = w->next) { |
4e609281 KZ |
600 | if (!w->on_disk && !ctl->quiet) |
601 | warnx(_("%s: offset 0x%jx not found"), | |
602 | ctl->devname, (uintmax_t)w->offset); | |
c49057d2 KZ |
603 | } |
604 | ||
24ed0781 KZ |
605 | if (need_force) |
606 | warnx(_("Use the --force option to force erase.")); | |
607 | ||
6611a3dd | 608 | fsync(blkid_probe_get_fd(pr)); |
081dc33b | 609 | |
11e88c86 | 610 | #ifdef BLKRRPART |
921f6343 KZ |
611 | if (reread && (mode & O_EXCL)) { |
612 | if (ctl->ndevs > 1) { | |
613 | /* | |
614 | * We're going to probe more device, let's postpone | |
615 | * re-read PT ioctl until all is erased to avoid | |
616 | * situation we erase PT on /dev/sda before /dev/sdaN | |
617 | * devices are processed. | |
618 | */ | |
619 | if (!ctl->reread) | |
620 | ctl->reread = xcalloc(ctl->ndevs, sizeof(char *)); | |
621 | ||
622 | ctl->reread[ctl->nrereads++] = ctl->devname; | |
623 | } else | |
624 | rereadpt(blkid_probe_get_fd(pr), ctl->devname); | |
625 | } | |
11e88c86 | 626 | #endif |
081dc33b | 627 | |
6611a3dd KZ |
628 | close(blkid_probe_get_fd(pr)); |
629 | blkid_free_probe(pr); | |
1666c155 | 630 | free(backup); |
0e45b256 | 631 | return 0; |
c49057d2 KZ |
632 | } |
633 | ||
69cc2ec0 | 634 | |
c49057d2 | 635 | static void __attribute__((__noreturn__)) |
6e1eda6f | 636 | usage(void) |
c49057d2 | 637 | { |
d9921b2a KZ |
638 | size_t i; |
639 | ||
42d2a8b3 KZ |
640 | fputs(USAGE_HEADER, stdout); |
641 | printf(_(" %s [options] <device>\n"), program_invocation_short_name); | |
642 | ||
643 | fputs(USAGE_SEPARATOR, stdout); | |
644 | puts(_("Wipe signatures from a device.")); | |
645 | ||
646 | fputs(USAGE_OPTIONS, stdout); | |
647 | puts(_(" -a, --all wipe all magic strings (BE CAREFUL!)")); | |
648 | puts(_(" -b, --backup create a signature backup in $HOME")); | |
649 | puts(_(" -f, --force force erasure")); | |
650 | puts(_(" -i, --noheadings don't print headings")); | |
651 | puts(_(" -J, --json use JSON output format")); | |
652 | puts(_(" -n, --no-act do everything except the actual write() call")); | |
653 | puts(_(" -o, --offset <num> offset to erase, in bytes")); | |
654 | puts(_(" -O, --output <list> COLUMNS to display (see below)")); | |
655 | puts(_(" -p, --parsable print out in parsable instead of printable format")); | |
656 | puts(_(" -q, --quiet suppress output messages")); | |
657 | puts(_(" -t, --types <list> limit the set of filesystem, RAIDs or partition tables")); | |
d9921b2a | 658 | |
f45f3ec3 | 659 | printf(USAGE_HELP_OPTIONS(21)); |
c49057d2 | 660 | |
d9921b2a KZ |
661 | fputs(USAGE_COLUMNS, stdout); |
662 | for (i = 0; i < ARRAY_SIZE(infos); i++) | |
663 | fprintf(stdout, " %8s %s\n", infos[i].name, _(infos[i].help)); | |
664 | ||
f45f3ec3 | 665 | printf(USAGE_MAN_TAIL("wipefs(8)")); |
6e1eda6f | 666 | exit(EXIT_SUCCESS); |
c49057d2 KZ |
667 | } |
668 | ||
669 | ||
670 | int | |
671 | main(int argc, char **argv) | |
672 | { | |
4e609281 | 673 | struct wipe_control ctl = { .devname = NULL }; |
0e45b256 | 674 | int c; |
d9921b2a | 675 | char *outarg = NULL; |
c49057d2 | 676 | |
6c7d5ae9 | 677 | static const struct option longopts[] = { |
87918040 SK |
678 | { "all", no_argument, NULL, 'a' }, |
679 | { "backup", no_argument, NULL, 'b' }, | |
680 | { "force", no_argument, NULL, 'f' }, | |
681 | { "help", no_argument, NULL, 'h' }, | |
682 | { "no-act", no_argument, NULL, 'n' }, | |
683 | { "offset", required_argument, NULL, 'o' }, | |
684 | { "parsable", no_argument, NULL, 'p' }, | |
685 | { "quiet", no_argument, NULL, 'q' }, | |
686 | { "types", required_argument, NULL, 't' }, | |
687 | { "version", no_argument, NULL, 'V' }, | |
d9921b2a KZ |
688 | { "json", no_argument, NULL, 'J'}, |
689 | { "noheadings",no_argument, NULL, 'i'}, | |
690 | { "output", required_argument, NULL, 'O'}, | |
87918040 | 691 | { NULL, 0, NULL, 0 } |
c49057d2 KZ |
692 | }; |
693 | ||
a7349ee3 | 694 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
d9921b2a | 695 | { 'O','a','o' }, |
abb7b98c KZ |
696 | { 0 } |
697 | }; | |
698 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
699 | ||
c49057d2 KZ |
700 | setlocale(LC_ALL, ""); |
701 | bindtextdomain(PACKAGE, LOCALEDIR); | |
702 | textdomain(PACKAGE); | |
2c308875 | 703 | close_stdout_atexit(); |
c49057d2 | 704 | |
d9921b2a | 705 | while ((c = getopt_long(argc, argv, "abfhiJnO:o:pqt:V", longopts, NULL)) != -1) { |
abb7b98c KZ |
706 | |
707 | err_exclusive_options(c, longopts, excl, excl_st); | |
708 | ||
c49057d2 KZ |
709 | switch(c) { |
710 | case 'a': | |
4e609281 | 711 | ctl.all = 1; |
c49057d2 | 712 | break; |
7e658c15 | 713 | case 'b': |
4e609281 | 714 | ctl.backup = 1; |
7e658c15 | 715 | break; |
2968c3fc | 716 | case 'f': |
4e609281 | 717 | ctl.force = 1; |
2968c3fc | 718 | break; |
d9921b2a KZ |
719 | case 'J': |
720 | ctl.json = 1; | |
721 | break; | |
722 | case 'i': | |
723 | ctl.no_headings = 1; | |
724 | break; | |
725 | case 'O': | |
726 | outarg = optarg; | |
727 | break; | |
c49057d2 | 728 | case 'n': |
4e609281 | 729 | ctl.noact = 1; |
c49057d2 KZ |
730 | break; |
731 | case 'o': | |
0e45b256 KZ |
732 | add_offset(&ctl.offsets, strtosize_or_err(optarg, |
733 | _("invalid offset argument"))); | |
c49057d2 KZ |
734 | break; |
735 | case 'p': | |
4e609281 | 736 | ctl.parsable = 1; |
d9921b2a | 737 | ctl.no_headings = 1; |
c49057d2 | 738 | break; |
0fd93af6 | 739 | case 'q': |
4e609281 | 740 | ctl.quiet = 1; |
0fd93af6 | 741 | break; |
f126cd46 | 742 | case 't': |
4e609281 | 743 | ctl.type_pattern = optarg; |
f126cd46 | 744 | break; |
2c308875 KZ |
745 | |
746 | case 'h': | |
747 | usage(); | |
11295cd1 | 748 | case 'V': |
2c308875 | 749 | print_version(EXIT_SUCCESS); |
c49057d2 | 750 | default: |
677ec86c | 751 | errtryhelp(EXIT_FAILURE); |
c49057d2 KZ |
752 | } |
753 | } | |
754 | ||
6e1eda6f RM |
755 | if (optind == argc) { |
756 | warnx(_("no device specified")); | |
757 | errtryhelp(EXIT_FAILURE); | |
758 | ||
759 | } | |
c49057d2 | 760 | |
0e45b256 | 761 | if (ctl.backup && !(ctl.all || ctl.offsets)) |
7e658c15 OO |
762 | warnx(_("The --backup option is meaningless in this context")); |
763 | ||
0e45b256 | 764 | if (!ctl.all && !ctl.offsets) { |
6611a3dd KZ |
765 | /* |
766 | * Print only | |
767 | */ | |
d9921b2a KZ |
768 | if (ctl.parsable) { |
769 | /* keep it backward compatible */ | |
770 | columns[ncolumns++] = COL_OFFSET; | |
771 | columns[ncolumns++] = COL_UUID; | |
772 | columns[ncolumns++] = COL_LABEL; | |
773 | columns[ncolumns++] = COL_TYPE; | |
774 | } else { | |
775 | /* default, may be modified by -O <list> */ | |
776 | columns[ncolumns++] = COL_DEVICE; | |
777 | columns[ncolumns++] = COL_OFFSET; | |
778 | columns[ncolumns++] = COL_TYPE; | |
779 | columns[ncolumns++] = COL_UUID; | |
780 | columns[ncolumns++] = COL_LABEL; | |
781 | } | |
782 | ||
783 | if (outarg | |
784 | && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), | |
785 | &ncolumns, column_name_to_id) < 0) | |
786 | return EXIT_FAILURE; | |
787 | ||
788 | init_output(&ctl); | |
4e609281 | 789 | |
c10695f8 | 790 | while (optind < argc) { |
0e45b256 KZ |
791 | struct wipe_desc *wp; |
792 | ||
4e609281 | 793 | ctl.devname = argv[optind++]; |
0e45b256 KZ |
794 | wp = read_offsets(&ctl); |
795 | if (wp) | |
796 | add_to_output(&ctl, wp); | |
797 | free_wipe(wp); | |
c10695f8 | 798 | } |
d9921b2a | 799 | finalize_output(&ctl); |
6611a3dd KZ |
800 | } else { |
801 | /* | |
802 | * Erase | |
803 | */ | |
921f6343 KZ |
804 | ctl.ndevs = argc - optind; |
805 | ||
c10695f8 | 806 | while (optind < argc) { |
4e609281 | 807 | ctl.devname = argv[optind++]; |
0e45b256 | 808 | do_wipe(&ctl); |
921f6343 | 809 | ctl.ndevs--; |
c10695f8 | 810 | } |
6611a3dd | 811 | |
3097f788 | 812 | #ifdef BLKRRPART |
921f6343 KZ |
813 | /* Re-read partition tables on whole-disk devices. This is |
814 | * postponed until all is done to avoid conflicts. | |
815 | */ | |
3097f788 | 816 | for (size_t i = 0; i < ctl.nrereads; i++) { |
921f6343 KZ |
817 | char *devname = ctl.reread[i]; |
818 | int fd = open(devname, O_RDONLY); | |
819 | ||
820 | if (fd >= 0) { | |
821 | rereadpt(fd, devname); | |
822 | close(fd); | |
823 | } | |
824 | } | |
825 | free(ctl.reread); | |
3097f788 | 826 | #endif |
921f6343 | 827 | } |
c49057d2 KZ |
828 | return EXIT_SUCCESS; |
829 | } |