]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/mkswap.c
mkswap: check for holes and unwanted extentd in file
[thirdparty/util-linux.git] / disk-utils / mkswap.c
1 /*
2 * mkswap.c - set up a linux swap device
3 *
4 * Copyright (C) 1991 Linus Torvalds
5 * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
6 *
7 * Copyright (C) 1999 Jakub Jelinek <jj@ultra.linux.cz>
8 * Copyright (C) 2007-2014 Karel Zak <kzak@redhat.com>
9 */
10
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <stdlib.h>
16 #include <limits.h>
17 #include <sys/utsname.h>
18 #include <sys/stat.h>
19 #include <sys/ioctl.h>
20 #include <errno.h>
21 #include <getopt.h>
22 #include <assert.h>
23 #ifdef HAVE_LIBSELINUX
24 # include <selinux/selinux.h>
25 # include <selinux/context.h>
26 #endif
27 #ifdef HAVE_LINUX_FIEMAP_H
28 # include <linux/fs.h>
29 # include <linux/fiemap.h>
30 #endif
31
32 #include "linux_version.h"
33 #include "swapheader.h"
34 #include "strutils.h"
35 #include "nls.h"
36 #include "blkdev.h"
37 #include "pathnames.h"
38 #include "all-io.h"
39 #include "xalloc.h"
40 #include "c.h"
41 #include "closestream.h"
42 #include "ismounted.h"
43
44 #ifdef HAVE_LIBUUID
45 # include <uuid.h>
46 #endif
47
48 #ifdef HAVE_LIBBLKID
49 # include <blkid.h>
50 #endif
51
52 #define MIN_GOODPAGES 10
53
54 #define SELINUX_SWAPFILE_TYPE "swapfile_t"
55
56 struct mkswap_control {
57 struct swap_header_v1_2 *hdr; /* swap header */
58 void *signature_page;/* buffer with swap header */
59
60 char *devname; /* device or file name */
61 const char *lockmode; /* as specified by --lock */
62 struct stat devstat; /* stat() result */
63 int fd; /* swap file descriptor */
64
65 unsigned long long npages; /* number of pages */
66 unsigned long nbadpages; /* number of bad pages */
67
68 int user_pagesize; /* --pagesize */
69 int pagesize; /* final pagesize used for the header */
70
71 char *opt_label; /* LABEL as specified on command line */
72 unsigned char *uuid; /* UUID parsed by libbuuid */
73
74 unsigned int check:1, /* --check */
75 force:1; /* --force */
76 };
77
78 static void init_signature_page(struct mkswap_control *ctl)
79 {
80 const int kernel_pagesize = getpagesize();
81
82 if (ctl->user_pagesize) {
83 if (ctl->user_pagesize < 0 || !is_power_of_2(ctl->user_pagesize) ||
84 (size_t) ctl->user_pagesize < sizeof(struct swap_header_v1_2) + 10)
85 errx(EXIT_FAILURE,
86 _("Bad user-specified page size %u"),
87 ctl->user_pagesize);
88 if (ctl->user_pagesize != kernel_pagesize)
89 warnx(_("Using user-specified page size %d, "
90 "instead of the system value %d"),
91 ctl->user_pagesize, kernel_pagesize);
92 ctl->pagesize = ctl->user_pagesize;
93 } else
94 ctl->pagesize = kernel_pagesize;
95
96 ctl->signature_page = xcalloc(1, ctl->pagesize);
97 ctl->hdr = (struct swap_header_v1_2 *) ctl->signature_page;
98 }
99
100 static void deinit_signature_page(struct mkswap_control *ctl)
101 {
102 free(ctl->signature_page);
103
104 ctl->hdr = NULL;
105 ctl->signature_page = NULL;
106 }
107
108 static void set_signature(const struct mkswap_control *ctl)
109 {
110 char *sp = (char *) ctl->signature_page;
111
112 assert(sp);
113 memcpy(sp + ctl->pagesize - SWAP_SIGNATURE_SZ, SWAP_SIGNATURE, SWAP_SIGNATURE_SZ);
114 }
115
116 static void set_uuid_and_label(const struct mkswap_control *ctl)
117 {
118 assert(ctl);
119 assert(ctl->hdr);
120
121 /* set UUID */
122 if (ctl->uuid)
123 memcpy(ctl->hdr->uuid, ctl->uuid, sizeof(ctl->hdr->uuid));
124
125 /* set LABEL */
126 if (ctl->opt_label) {
127 xstrncpy(ctl->hdr->volume_name,
128 ctl->opt_label, sizeof(ctl->hdr->volume_name));
129 if (strlen(ctl->opt_label) > strlen(ctl->hdr->volume_name))
130 warnx(_("Label was truncated."));
131 }
132
133 /* report results */
134 if (ctl->uuid || ctl->opt_label) {
135 if (ctl->opt_label)
136 printf("LABEL=%s, ", ctl->hdr->volume_name);
137 else
138 printf(_("no label, "));
139 #ifdef HAVE_LIBUUID
140 if (ctl->uuid) {
141 char uuid_string[UUID_STR_LEN];
142 uuid_unparse(ctl->uuid, uuid_string);
143 printf("UUID=%s\n", uuid_string);
144 } else
145 #endif
146 printf(_("no uuid\n"));
147 }
148 }
149
150 static void __attribute__((__noreturn__)) usage(void)
151 {
152 FILE *out = stdout;
153 fprintf(out,
154 _("\nUsage:\n"
155 " %s [options] device [size]\n"),
156 program_invocation_short_name);
157
158 fputs(USAGE_SEPARATOR, out);
159 fputs(_("Set up a Linux swap area.\n"), out);
160
161 fprintf(out, _(
162 "\nOptions:\n"
163 " -c, --check check bad blocks before creating the swap area\n"
164 " -f, --force allow swap size area be larger than device\n"
165 " -p, --pagesize SIZE specify page size in bytes\n"
166 " -L, --label LABEL specify label\n"
167 " -v, --swapversion NUM specify swap-space version number\n"
168 " -U, --uuid UUID specify the uuid to use\n"
169 ));
170 fprintf(out,
171 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
172 printf(USAGE_HELP_OPTIONS(27));
173
174 printf(USAGE_MAN_TAIL("mkswap(8)"));
175 exit(EXIT_SUCCESS);
176 }
177
178 static void page_bad(struct mkswap_control *ctl, unsigned int page)
179 {
180 const unsigned long max_badpages =
181 (ctl->pagesize - 1024 - 128 * sizeof(int) - 10) / sizeof(int);
182
183 if (ctl->nbadpages == max_badpages)
184 errx(EXIT_FAILURE, _("too many bad pages: %lu"), max_badpages);
185
186 ctl->hdr->badpages[ctl->nbadpages] = page;
187 ctl->nbadpages++;
188 }
189
190 static void check_blocks(struct mkswap_control *ctl)
191 {
192 unsigned int current_page = 0;
193 int do_seek = 1;
194 char *buffer;
195
196 assert(ctl);
197 assert(ctl->fd > -1);
198
199 buffer = xmalloc(ctl->pagesize);
200 while (current_page < ctl->npages) {
201 ssize_t rc;
202 off_t offset = (off_t) current_page * ctl->pagesize;
203
204 if (do_seek && lseek(ctl->fd, offset, SEEK_SET) != offset)
205 errx(EXIT_FAILURE, _("seek failed in check_blocks"));
206
207 rc = read(ctl->fd, buffer, ctl->pagesize);
208 do_seek = (rc < 0 || rc != ctl->pagesize);
209 if (do_seek)
210 page_bad(ctl, current_page);
211 current_page++;
212 }
213 printf(P_("%lu bad page\n", "%lu bad pages\n", ctl->nbadpages), ctl->nbadpages);
214 free(buffer);
215 }
216
217
218 #ifdef HAVE_LINUX_FIEMAP_H
219 static void check_extents_print_hdr(int *n)
220 {
221 if (*n == 0) {
222 fputc('\n', stderr);
223 warnx(_("extents check failed:"));
224 }
225 ++*n;
226 }
227
228 static void check_extents(struct mkswap_control *ctl)
229 {
230 char buf[BUFSIZ] = { 0 };
231 struct fiemap *fiemap = (struct fiemap *) buf;
232 int last = 0, nerrs = 0;
233 uint64_t last_logical = 0;
234
235 memset(fiemap, 0, sizeof(struct fiemap));
236
237 do {
238 int rc;
239 size_t n, i;
240
241 fiemap->fm_length = ~0ULL;
242 fiemap->fm_flags = 0;
243 fiemap->fm_extent_count =
244 (sizeof(buf) - sizeof(*fiemap)) / sizeof(struct fiemap_extent);
245
246 rc = ioctl(ctl->fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
247 if (rc < 0) {
248 warn(_("FIEMAP failed -- ignore extents check"));
249 return;
250 }
251
252 n = fiemap->fm_mapped_extents;
253
254 for (i = 0; i < n; i++) {
255 struct fiemap_extent *e = &fiemap->fm_extents[i];
256
257 if (e->fe_logical > last_logical) {
258 check_extents_print_hdr(&nerrs);
259 fprintf(stderr, (" - hole detected at offset %ju (size %ju bytes)\n"),
260 (uintmax_t) last_logical,
261 (uintmax_t) e->fe_logical - last_logical);
262 }
263
264 last_logical = (e->fe_logical + e->fe_length);
265
266 if (e->fe_flags & FIEMAP_EXTENT_LAST)
267 last = 1;
268 if (e->fe_flags & FIEMAP_EXTENT_UNKNOWN) {
269 check_extents_print_hdr(&nerrs);
270 fprintf(stderr, _(" - unknown file extent type at offset %ju\n"),
271 (uintmax_t) last_logical);
272 }
273 if (e->fe_flags & FIEMAP_EXTENT_DATA_INLINE){
274 check_extents_print_hdr(&nerrs);
275 fprintf(stderr, _(" - data inline extent at offset %ju\n"),
276 (uintmax_t) last_logical);
277 }
278 if (e->fe_flags & FIEMAP_EXTENT_SHARED){
279 check_extents_print_hdr(&nerrs);
280 fprintf(stderr, _(" - shared extent at offset %ju\n"),
281 (uintmax_t) last_logical);
282 }
283 if (e->fe_flags & FIEMAP_EXTENT_DELALLOC){
284 check_extents_print_hdr(&nerrs);
285 fprintf(stderr, _(" - deallocated extent at offset %ju\n"),
286 (uintmax_t) last_logical);
287 }
288
289 }
290 fiemap->fm_start = fiemap->fm_extents[n - 1].fe_logical
291 + fiemap->fm_extents[n - 1].fe_length;
292 } while (last == 0);
293
294 if (nerrs)
295 fprintf(stderr, _("file %s can be rejected by kernel on swap activation.\n\n"),
296 ctl->devname);
297 }
298 #endif /* HAVE_LINUX_FIEMAP_H */
299
300 /* return size in pages */
301 static unsigned long long get_size(const struct mkswap_control *ctl)
302 {
303 int fd;
304 unsigned long long size;
305
306 fd = open(ctl->devname, O_RDONLY);
307 if (fd < 0)
308 err(EXIT_FAILURE, _("cannot open %s"), ctl->devname);
309 if (blkdev_get_size(fd, &size) == 0)
310 size /= ctl->pagesize;
311
312 close(fd);
313 return size;
314 }
315
316 #ifdef HAVE_LIBBLKID
317 static blkid_probe new_prober(const struct mkswap_control *ctl)
318 {
319 blkid_probe pr = blkid_new_probe();
320 if (!pr)
321 errx(EXIT_FAILURE, _("unable to alloc new libblkid probe"));
322 if (blkid_probe_set_device(pr, ctl->fd, 0, 0))
323 errx(EXIT_FAILURE, _("unable to assign device to libblkid probe"));
324 return pr;
325 }
326 #endif
327
328 static void open_device(struct mkswap_control *ctl)
329 {
330 assert(ctl);
331 assert(ctl->devname);
332
333 if (stat(ctl->devname, &ctl->devstat) < 0)
334 err(EXIT_FAILURE, _("stat of %s failed"), ctl->devname);
335 ctl->fd = open_blkdev_or_file(&ctl->devstat, ctl->devname, O_RDWR);
336 if (ctl->fd < 0)
337 err(EXIT_FAILURE, _("cannot open %s"), ctl->devname);
338
339 if (blkdev_lock(ctl->fd, ctl->devname, ctl->lockmode) != 0)
340 exit(EXIT_FAILURE);
341
342 if (ctl->check && S_ISREG(ctl->devstat.st_mode)) {
343 ctl->check = 0;
344 warnx(_("warning: checking bad blocks from swap file is not supported: %s"),
345 ctl->devname);
346 }
347 }
348
349 static void wipe_device(struct mkswap_control *ctl)
350 {
351 char *type = NULL;
352 int zap = 1;
353 #ifdef HAVE_LIBBLKID
354 blkid_probe pr = NULL;
355 #endif
356 if (!ctl->force) {
357 const char *v = NULL;
358
359 if (lseek(ctl->fd, 0, SEEK_SET) != 0)
360 errx(EXIT_FAILURE, _("unable to rewind swap-device"));
361
362 #ifdef HAVE_LIBBLKID
363 pr = new_prober(ctl);
364 blkid_probe_enable_partitions(pr, 1);
365 blkid_probe_enable_superblocks(pr, 0);
366
367 if (blkid_do_fullprobe(pr) == 0 &&
368 blkid_probe_lookup_value(pr, "PTTYPE", &v, NULL) == 0 && v) {
369 type = xstrdup(v);
370 zap = 0;
371 }
372 #else
373 /* don't zap if compiled without libblkid */
374 zap = 0;
375 #endif
376 }
377
378 if (zap) {
379 /*
380 * Wipe bootbits
381 */
382 char buf[1024] = { '\0' };
383
384 if (lseek(ctl->fd, 0, SEEK_SET) != 0)
385 errx(EXIT_FAILURE, _("unable to rewind swap-device"));
386
387 if (write_all(ctl->fd, buf, sizeof(buf)))
388 errx(EXIT_FAILURE, _("unable to erase bootbits sectors"));
389 #ifdef HAVE_LIBBLKID
390 /*
391 * Wipe rest of the device
392 */
393 if (!pr)
394 pr = new_prober(ctl);
395
396 blkid_probe_enable_superblocks(pr, 1);
397 blkid_probe_enable_partitions(pr, 0);
398 blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_TYPE);
399
400 while (blkid_do_probe(pr) == 0) {
401 const char *data = NULL;
402
403 if (blkid_probe_lookup_value(pr, "TYPE", &data, NULL) == 0 && data)
404 warnx(_("%s: warning: wiping old %s signature."), ctl->devname, data);
405 blkid_do_wipe(pr, 0);
406 }
407 #endif
408 } else {
409 warnx(_("%s: warning: don't erase bootbits sectors"),
410 ctl->devname);
411 if (type)
412 fprintf(stderr, _(" (%s partition table detected). "), type);
413 else
414 fprintf(stderr, _(" (compiled without libblkid). "));
415 fprintf(stderr, _("Use -f to force.\n"));
416 }
417 free(type);
418 #ifdef HAVE_LIBBLKID
419 blkid_free_probe(pr);
420 #endif
421 }
422
423 #define SIGNATURE_OFFSET 1024
424
425 static void write_header_to_device(struct mkswap_control *ctl)
426 {
427 assert(ctl);
428 assert(ctl->fd > -1);
429 assert(ctl->signature_page);
430
431 if (lseek(ctl->fd, SIGNATURE_OFFSET, SEEK_SET) != SIGNATURE_OFFSET)
432 errx(EXIT_FAILURE, _("unable to rewind swap-device"));
433
434 if (write_all(ctl->fd, (char *) ctl->signature_page + SIGNATURE_OFFSET,
435 ctl->pagesize - SIGNATURE_OFFSET) == -1)
436 err(EXIT_FAILURE,
437 _("%s: unable to write signature page"),
438 ctl->devname);
439 }
440
441 int main(int argc, char **argv)
442 {
443 struct mkswap_control ctl = { .fd = -1 };
444 int c, permMask;
445 uint64_t sz;
446 int version = SWAP_VERSION;
447 char *block_count = NULL, *strsz = NULL;
448 #ifdef HAVE_LIBUUID
449 const char *opt_uuid = NULL;
450 uuid_t uuid_dat;
451 #endif
452 enum {
453 OPT_LOCK = CHAR_MAX + 1,
454 };
455 static const struct option longopts[] = {
456 { "check", no_argument, NULL, 'c' },
457 { "force", no_argument, NULL, 'f' },
458 { "pagesize", required_argument, NULL, 'p' },
459 { "label", required_argument, NULL, 'L' },
460 { "swapversion", required_argument, NULL, 'v' },
461 { "uuid", required_argument, NULL, 'U' },
462 { "version", no_argument, NULL, 'V' },
463 { "help", no_argument, NULL, 'h' },
464 { "lock", optional_argument, NULL, OPT_LOCK },
465 { NULL, 0, NULL, 0 }
466 };
467
468 setlocale(LC_ALL, "");
469 bindtextdomain(PACKAGE, LOCALEDIR);
470 textdomain(PACKAGE);
471 close_stdout_atexit();
472
473 while((c = getopt_long(argc, argv, "cfp:L:v:U:Vh", longopts, NULL)) != -1) {
474 switch (c) {
475 case 'c':
476 ctl.check = 1;
477 break;
478 case 'f':
479 ctl.force = 1;
480 break;
481 case 'p':
482 ctl.user_pagesize = strtou32_or_err(optarg, _("parsing page size failed"));
483 break;
484 case 'L':
485 ctl.opt_label = optarg;
486 break;
487 case 'v':
488 version = strtos32_or_err(optarg, _("parsing version number failed"));
489 if (version != SWAP_VERSION)
490 errx(EXIT_FAILURE,
491 _("swapspace version %d is not supported"), version);
492 break;
493 case 'U':
494 #ifdef HAVE_LIBUUID
495 opt_uuid = optarg;
496 #else
497 warnx(_("warning: ignoring -U (UUIDs are unsupported by %s)"),
498 program_invocation_short_name);
499 #endif
500 break;
501 case 'V':
502 print_version(EXIT_SUCCESS);
503 break;
504 case OPT_LOCK:
505 ctl.lockmode = "1";
506 if (optarg) {
507 if (*optarg == '=')
508 optarg++;
509 ctl.lockmode = optarg;
510 }
511 break;
512 case 'h':
513 usage();
514 default:
515 errtryhelp(EXIT_FAILURE);
516 }
517 }
518
519 if (optind < argc)
520 ctl.devname = argv[optind++];
521 if (optind < argc)
522 block_count = argv[optind++];
523 if (optind != argc) {
524 warnx(_("only one device argument is currently supported"));
525 errtryhelp(EXIT_FAILURE);
526 }
527
528 #ifdef HAVE_LIBUUID
529 if(opt_uuid) {
530 if (uuid_parse(opt_uuid, uuid_dat) != 0)
531 errx(EXIT_FAILURE, _("error: parsing UUID failed"));
532 } else
533 uuid_generate(uuid_dat);
534 ctl.uuid = uuid_dat;
535 #endif
536
537 init_signature_page(&ctl); /* get pagesize and allocate signature page */
538
539 if (!ctl.devname) {
540 warnx(_("error: Nowhere to set up swap on?"));
541 errtryhelp(EXIT_FAILURE);
542 }
543 if (block_count) {
544 /* this silly user specified the number of blocks explicitly */
545 uint64_t blks = strtou64_or_err(block_count,
546 _("invalid block count argument"));
547 ctl.npages = blks / (ctl.pagesize / 1024);
548 }
549
550 sz = get_size(&ctl);
551 if (!ctl.npages)
552 ctl.npages = sz;
553 else if (ctl.npages > sz && !ctl.force)
554 errx(EXIT_FAILURE,
555 _("error: "
556 "size %llu KiB is larger than device size %"PRIu64" KiB"),
557 ctl.npages * (ctl.pagesize / 1024), sz * (ctl.pagesize / 1024));
558
559 if (ctl.npages < MIN_GOODPAGES)
560 errx(EXIT_FAILURE,
561 _("error: swap area needs to be at least %ld KiB"),
562 (long)(MIN_GOODPAGES * ctl.pagesize / 1024));
563 if (ctl.npages > UINT32_MAX) {
564 /* true when swap is bigger than 17.59 terabytes */
565 ctl.npages = UINT32_MAX;
566 warnx(_("warning: truncating swap area to %llu KiB"),
567 ctl.npages * ctl.pagesize / 1024);
568 }
569
570 if (is_mounted(ctl.devname))
571 errx(EXIT_FAILURE, _("error: "
572 "%s is mounted; will not make swapspace"),
573 ctl.devname);
574
575 open_device(&ctl);
576 permMask = S_ISBLK(ctl.devstat.st_mode) ? 07007 : 07077;
577 if ((ctl.devstat.st_mode & permMask) != 0)
578 warnx(_("%s: insecure permissions %04o, %04o suggested."),
579 ctl.devname, ctl.devstat.st_mode & 07777,
580 ~permMask & 0666);
581 if (getuid() == 0 && S_ISREG(ctl.devstat.st_mode) && ctl.devstat.st_uid != 0)
582 warnx(_("%s: insecure file owner %d, 0 (root) suggested."),
583 ctl.devname, ctl.devstat.st_uid);
584
585
586 if (ctl.check)
587 check_blocks(&ctl);
588 #ifdef HAVE_LINUX_FIEMAP_H
589 if (S_ISREG(ctl.devstat.st_mode))
590 check_extents(&ctl);
591 #endif
592
593 wipe_device(&ctl);
594
595 assert(ctl.hdr);
596 ctl.hdr->version = version;
597 ctl.hdr->last_page = ctl.npages - 1;
598 ctl.hdr->nr_badpages = ctl.nbadpages;
599
600 if ((ctl.npages - MIN_GOODPAGES) < ctl.nbadpages)
601 errx(EXIT_FAILURE, _("Unable to set up swap-space: unreadable"));
602
603 sz = (ctl.npages - ctl.nbadpages - 1) * ctl.pagesize;
604 strsz = size_to_human_string(SIZE_SUFFIX_SPACE | SIZE_SUFFIX_3LETTER, sz);
605
606 printf(_("Setting up swapspace version %d, size = %s (%"PRIu64" bytes)\n"),
607 version, strsz, sz);
608 free(strsz);
609
610 set_signature(&ctl);
611 set_uuid_and_label(&ctl);
612
613 write_header_to_device(&ctl);
614
615 deinit_signature_page(&ctl);
616
617 #ifdef HAVE_LIBSELINUX
618 if (S_ISREG(ctl.devstat.st_mode) && is_selinux_enabled() > 0) {
619 security_context_t context_string;
620 security_context_t oldcontext;
621 context_t newcontext;
622
623 if (fgetfilecon(ctl.fd, &oldcontext) < 0) {
624 if (errno != ENODATA)
625 err(EXIT_FAILURE,
626 _("%s: unable to obtain selinux file label"),
627 ctl.devname);
628 if (matchpathcon(ctl.devname, ctl.devstat.st_mode, &oldcontext))
629 errx(EXIT_FAILURE, _("unable to matchpathcon()"));
630 }
631 if (!(newcontext = context_new(oldcontext)))
632 errx(EXIT_FAILURE, _("unable to create new selinux context"));
633 if (context_type_set(newcontext, SELINUX_SWAPFILE_TYPE))
634 errx(EXIT_FAILURE, _("couldn't compute selinux context"));
635
636 context_string = context_str(newcontext);
637
638 if (strcmp(context_string, oldcontext)!=0) {
639 if (fsetfilecon(ctl.fd, context_string) && errno != ENOTSUP)
640 err(EXIT_FAILURE, _("unable to relabel %s to %s"),
641 ctl.devname, context_string);
642 }
643 context_free(newcontext);
644 freecon(oldcontext);
645 }
646 #endif
647 /*
648 * A subsequent swapon() will fail if the signature
649 * is not actually on disk. (This is a kernel bug.)
650 * The fsync() in close_fd() will take care of writing.
651 */
652 if (close_fd(ctl.fd) != 0)
653 err(EXIT_FAILURE, _("write failed"));
654 return EXIT_SUCCESS;
655 }