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