2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * mkswap.c - set up a linux swap device
11 * Copyright (C) 1991 Linus Torvalds
12 * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
14 * Copyright (C) 1999 Jakub Jelinek <jj@ultra.linux.cz>
15 * Copyright (C) 2007-2014 Karel Zak <kzak@redhat.com>
24 #include <sys/utsname.h>
26 #include <sys/ioctl.h>
30 #ifdef HAVE_LIBSELINUX
31 # include <selinux/selinux.h>
32 # include <selinux/context.h>
33 # include "selinux-utils.h"
35 #ifdef HAVE_LINUX_FIEMAP_H
36 # include <linux/fs.h>
37 # include <linux/fiemap.h>
40 #include "linux_version.h"
41 #include "swapheader.h"
45 #include "pathnames.h"
49 #include "closestream.h"
50 #include "ismounted.h"
62 #define MIN_GOODPAGES 10
64 #define SELINUX_SWAPFILE_TYPE "swapfile_t"
72 struct mkswap_control
{
73 struct swap_header_v1_2
*hdr
; /* swap header */
74 void *signature_page
;/* buffer with swap header */
76 char *devname
; /* device or file name */
77 const char *lockmode
; /* as specified by --lock */
78 struct stat devstat
; /* stat() result */
79 int fd
; /* swap file descriptor */
81 unsigned long long npages
; /* number of pages */
82 unsigned long nbadpages
; /* number of bad pages */
84 int user_pagesize
; /* --pagesize */
85 int pagesize
; /* final pagesize used for the header */
86 off_t offset
; /* offset of the header in the target */
88 char *opt_label
; /* LABEL as specified on command line */
89 unsigned char *uuid
; /* UUID parsed by libbuuid */
93 enum ENDIANNESS endianness
;
95 unsigned int check
:1, /* --check */
96 verbose
:1, /* --verbose */
97 quiet
:1, /* --quiet */
98 force
:1; /* --force */
101 static uint32_t cpu32_to_endianness(uint32_t v
, enum ENDIANNESS e
)
104 case ENDIANNESS_NATIVE
: return v
;
105 case ENDIANNESS_LITTLE
: return cpu_to_le32(v
);
106 case ENDIANNESS_BIG
: return cpu_to_be32(v
);
111 static void init_signature_page(struct mkswap_control
*ctl
)
113 const int kernel_pagesize
= getpagesize();
115 if (ctl
->user_pagesize
) {
116 if (ctl
->user_pagesize
< 0 || !is_power_of_2(ctl
->user_pagesize
) ||
117 (size_t) ctl
->user_pagesize
< sizeof(struct swap_header_v1_2
) + 10)
119 _("Bad user-specified page size %u"),
121 if (!ctl
->quiet
&& ctl
->user_pagesize
!= kernel_pagesize
)
122 warnx(_("Using user-specified page size %d, "
123 "instead of the system value %d"),
124 ctl
->user_pagesize
, kernel_pagesize
);
125 ctl
->pagesize
= ctl
->user_pagesize
;
127 ctl
->pagesize
= kernel_pagesize
;
129 ctl
->signature_page
= xcalloc(1, ctl
->pagesize
);
130 ctl
->hdr
= (struct swap_header_v1_2
*) ctl
->signature_page
;
133 static void deinit_signature_page(struct mkswap_control
*ctl
)
135 free(ctl
->signature_page
);
138 ctl
->signature_page
= NULL
;
141 static void set_signature(const struct mkswap_control
*ctl
)
143 char *sp
= (char *) ctl
->signature_page
;
146 memcpy(sp
+ ctl
->pagesize
- SWAP_SIGNATURE_SZ
, SWAP_SIGNATURE
, SWAP_SIGNATURE_SZ
);
149 static void set_uuid_and_label(const struct mkswap_control
*ctl
)
156 memcpy(ctl
->hdr
->uuid
, ctl
->uuid
, sizeof(ctl
->hdr
->uuid
));
159 if (ctl
->opt_label
) {
160 xstrncpy(ctl
->hdr
->volume_name
,
161 ctl
->opt_label
, sizeof(ctl
->hdr
->volume_name
));
163 && strlen(ctl
->opt_label
) > strlen(ctl
->hdr
->volume_name
))
164 warnx(_("Label was truncated."));
168 if (!ctl
->quiet
&& (ctl
->uuid
|| ctl
->opt_label
)) {
170 printf("LABEL=%s, ", ctl
->hdr
->volume_name
);
172 printf(_("no label, "));
175 char uuid_string
[UUID_STR_LEN
];
176 uuid_unparse(ctl
->uuid
, uuid_string
);
177 printf("UUID=%s\n", uuid_string
);
180 printf(_("no uuid\n"));
184 static void __attribute__((__noreturn__
)) usage(void)
188 fputs(USAGE_HEADER
, out
);
189 fprintf(out
, _(" %s [options] device [size]\n"), program_invocation_short_name
);
191 fputs(USAGE_SEPARATOR
, out
);
192 fputs(_("Set up a Linux swap area.\n"), out
);
194 fputs(USAGE_OPTIONS
, out
);
195 fputs(_(" -c, --check check bad blocks before creating the swap area\n"), out
);
196 fputs(_(" -f, --force allow swap size area be larger than device\n"), out
);
197 fputs(_(" -q, --quiet suppress output and warning messages\n"), out
);
198 fputs(_(" -p, --pagesize SIZE specify page size in bytes\n"), out
);
199 fputs(_(" -L, --label LABEL specify label\n"), out
);
200 fputs(_(" -v, --swapversion NUM specify swap-space version number\n"), out
);
201 fputs(_(" -U, --uuid UUID specify the uuid to use\n"), out
);
203 _(" -e, --endianness=<value> specify the endianness to use "
204 "(%s, %s or %s)\n"), "native", "little", "big");
205 fputs(_(" -o, --offset OFFSET specify the offset in the device\n"), out
);
206 fputs(_(" --verbose verbose output\n"), out
);
209 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
211 fprintf(out
, USAGE_HELP_OPTIONS(27));
213 fprintf(out
, USAGE_MAN_TAIL("mkswap(8)"));
217 static void page_bad(struct mkswap_control
*ctl
, unsigned int page
)
219 const unsigned long max_badpages
=
220 (ctl
->pagesize
- 1024 - 128 * sizeof(int) - 10) / sizeof(int);
222 if (ctl
->nbadpages
== max_badpages
)
223 errx(EXIT_FAILURE
, _("too many bad pages: %lu"), max_badpages
);
225 ctl
->hdr
->badpages
[ctl
->nbadpages
] = page
;
229 static void check_blocks(struct mkswap_control
*ctl
)
231 unsigned int current_page
= 0;
236 assert(ctl
->fd
> -1);
238 buffer
= xmalloc(ctl
->pagesize
);
239 while (current_page
< ctl
->npages
) {
241 off_t offset
= (off_t
) current_page
* ctl
->pagesize
;
243 if (do_seek
&& lseek(ctl
->fd
, offset
, SEEK_SET
) != offset
)
244 errx(EXIT_FAILURE
, _("seek failed in check_blocks"));
246 rc
= read(ctl
->fd
, buffer
, ctl
->pagesize
);
247 do_seek
= (rc
< 0 || rc
!= ctl
->pagesize
);
249 page_bad(ctl
, current_page
);
254 printf(P_("%lu bad page\n", "%lu bad pages\n", ctl
->nbadpages
), ctl
->nbadpages
);
259 #ifdef HAVE_LINUX_FIEMAP_H
260 static void warn_extent(struct mkswap_control
*ctl
, const char *msg
, uint64_t off
)
262 if (ctl
->nbad_extents
== 0) {
266 "mkswap: %s contains holes or other unsupported extents.\n"
267 " This swap file can be rejected by kernel on swap activation!\n"),
274 " Use --verbose for more details.\n"));
278 fputs(" - ", stderr
);
279 fprintf(stderr
, msg
, off
);
285 static void check_extents(struct mkswap_control
*ctl
)
287 char buf
[BUFSIZ
] = { 0 };
288 struct fiemap
*fiemap
= (struct fiemap
*) buf
;
290 uint64_t last_logical
= 0;
292 memset(fiemap
, 0, sizeof(struct fiemap
));
298 fiemap
->fm_length
= ~0ULL;
299 fiemap
->fm_flags
= FIEMAP_FLAG_SYNC
;
300 fiemap
->fm_extent_count
=
301 (sizeof(buf
) - sizeof(*fiemap
)) / sizeof(struct fiemap_extent
);
303 rc
= ioctl(ctl
->fd
, FS_IOC_FIEMAP
, (unsigned long) fiemap
);
307 n
= fiemap
->fm_mapped_extents
;
311 for (i
= 0; i
< n
; i
++) {
312 struct fiemap_extent
*e
= &fiemap
->fm_extents
[i
];
314 if (e
->fe_logical
> last_logical
)
315 warn_extent(ctl
, _("hole detected at offset %ju"),
316 (uintmax_t) last_logical
);
318 last_logical
= (e
->fe_logical
+ e
->fe_length
);
320 if (e
->fe_flags
& FIEMAP_EXTENT_LAST
)
322 if (e
->fe_flags
& FIEMAP_EXTENT_DATA_INLINE
)
323 warn_extent(ctl
, _("data inline extent at offset %ju"),
324 (uintmax_t) e
->fe_logical
);
325 if (e
->fe_flags
& FIEMAP_EXTENT_SHARED
)
326 warn_extent(ctl
, _("shared extent at offset %ju"),
327 (uintmax_t) e
->fe_logical
);
328 if (e
->fe_flags
& FIEMAP_EXTENT_DELALLOC
)
329 warn_extent(ctl
, _("unallocated extent at offset %ju"),
330 (uintmax_t) e
->fe_logical
);
332 if (!ctl
->verbose
&& ctl
->nbad_extents
)
335 fiemap
->fm_start
= fiemap
->fm_extents
[n
- 1].fe_logical
336 + fiemap
->fm_extents
[n
- 1].fe_length
;
339 if (last_logical
< (uint64_t) ctl
->devstat
.st_size
)
340 warn_extent(ctl
, _("hole detected at offset %ju"),
341 (uintmax_t) last_logical
);
343 if (ctl
->nbad_extents
)
346 #endif /* HAVE_LINUX_FIEMAP_H */
348 /* return size in pages */
349 static unsigned long long get_size(const struct mkswap_control
*ctl
)
352 unsigned long long size
;
354 fd
= open(ctl
->devname
, O_RDONLY
);
356 err(EXIT_FAILURE
, _("cannot open %s"), ctl
->devname
);
357 if (blkdev_get_size(fd
, &size
) < 0)
358 err(EXIT_FAILURE
, _("cannot determine size of %s"), ctl
->devname
);
359 if ((unsigned long long) ctl
->offset
> size
)
360 errx(EXIT_FAILURE
, _("offset larger than file size"));
362 size
/= ctl
->pagesize
;
369 static blkid_probe
new_prober(const struct mkswap_control
*ctl
)
371 blkid_probe pr
= blkid_new_probe();
373 errx(EXIT_FAILURE
, _("unable to alloc new libblkid probe"));
374 if (blkid_probe_set_device(pr
, ctl
->fd
, 0, 0))
375 errx(EXIT_FAILURE
, _("unable to assign device to libblkid probe"));
380 static void open_device(struct mkswap_control
*ctl
)
383 assert(ctl
->devname
);
385 if (stat(ctl
->devname
, &ctl
->devstat
) < 0)
386 err(EXIT_FAILURE
, _("stat of %s failed"), ctl
->devname
);
387 ctl
->fd
= open_blkdev_or_file(&ctl
->devstat
, ctl
->devname
, O_RDWR
);
389 err(EXIT_FAILURE
, _("cannot open %s"), ctl
->devname
);
391 if (blkdev_lock(ctl
->fd
, ctl
->devname
, ctl
->lockmode
) != 0)
394 if (ctl
->check
&& S_ISREG(ctl
->devstat
.st_mode
)) {
397 warnx(_("warning: checking bad blocks from swap file is not supported: %s"),
402 static void wipe_device(struct mkswap_control
*ctl
)
407 blkid_probe pr
= NULL
;
408 const char *v
= NULL
;
411 if (lseek(ctl
->fd
, 0, SEEK_SET
) != 0)
412 errx(EXIT_FAILURE
, _("unable to rewind swap-device"));
415 pr
= new_prober(ctl
);
416 blkid_probe_enable_partitions(pr
, 1);
417 blkid_probe_enable_superblocks(pr
, 0);
419 if (blkid_do_fullprobe(pr
) == 0 &&
420 blkid_probe_lookup_value(pr
, "PTTYPE", &v
, NULL
) == 0 && v
) {
425 /* don't zap if compiled without libblkid */
434 char buf
[1024] = { '\0' };
436 if (lseek(ctl
->fd
, 0, SEEK_SET
) != 0)
437 errx(EXIT_FAILURE
, _("unable to rewind swap-device"));
439 if (write_all(ctl
->fd
, buf
, sizeof(buf
)))
440 errx(EXIT_FAILURE
, _("unable to erase bootbits sectors"));
443 * Wipe rest of the device
446 pr
= new_prober(ctl
);
448 blkid_probe_enable_superblocks(pr
, 1);
449 blkid_probe_enable_partitions(pr
, 0);
450 blkid_probe_set_superblocks_flags(pr
, BLKID_SUBLKS_MAGIC
|BLKID_SUBLKS_TYPE
);
452 while (blkid_do_probe(pr
) == 0) {
453 const char *data
= NULL
;
456 && blkid_probe_lookup_value(pr
, "TYPE", &data
, NULL
) == 0 && data
)
457 warnx(_("%s: warning: wiping old %s signature."), ctl
->devname
, data
);
458 blkid_do_wipe(pr
, 0);
461 } else if (!ctl
->quiet
) {
462 warnx(_("%s: warning: don't erase bootbits sectors"),
465 fprintf(stderr
, _(" (%s partition table detected). "), type
);
467 fprintf(stderr
, _(" (compiled without libblkid). "));
468 fprintf(stderr
, _("Use -f to force.\n"));
472 blkid_free_probe(pr
);
476 #define SIGNATURE_OFFSET 1024
478 static void write_header_to_device(struct mkswap_control
*ctl
)
483 assert(ctl
->fd
> -1);
484 assert(ctl
->signature_page
);
486 offset
= SIGNATURE_OFFSET
+ ctl
->offset
;
488 if (lseek(ctl
->fd
, offset
, SEEK_SET
) != offset
)
489 errx(EXIT_FAILURE
, _("unable to rewind swap-device"));
491 if (write_all(ctl
->fd
, (char *) ctl
->signature_page
+ SIGNATURE_OFFSET
,
492 ctl
->pagesize
- SIGNATURE_OFFSET
) == -1)
494 _("%s: unable to write signature page"),
498 int main(int argc
, char **argv
)
500 struct mkswap_control ctl
= { .fd
= -1, .endianness
= ENDIANNESS_NATIVE
};
503 int version
= SWAP_VERSION
;
504 char *block_count
= NULL
, *strsz
= NULL
;
506 const char *opt_uuid
= NULL
;
510 OPT_LOCK
= CHAR_MAX
+ 1,
513 static const struct option longopts
[] = {
514 { "check", no_argument
, NULL
, 'c' },
515 { "force", no_argument
, NULL
, 'f' },
516 { "quiet", no_argument
, NULL
, 'q' },
517 { "pagesize", required_argument
, NULL
, 'p' },
518 { "label", required_argument
, NULL
, 'L' },
519 { "swapversion", required_argument
, NULL
, 'v' },
520 { "uuid", required_argument
, NULL
, 'U' },
521 { "endianness", required_argument
, NULL
, 'e' },
522 { "offset", required_argument
, NULL
, 'o' },
523 { "version", no_argument
, NULL
, 'V' },
524 { "help", no_argument
, NULL
, 'h' },
525 { "lock", optional_argument
, NULL
, OPT_LOCK
},
526 { "verbose", no_argument
, NULL
, OPT_VERBOSE
},
530 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
534 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
536 setlocale(LC_ALL
, "");
537 bindtextdomain(PACKAGE
, LOCALEDIR
);
539 close_stdout_atexit();
541 while((c
= getopt_long(argc
, argv
, "cfp:qL:v:U:e:o:Vh", longopts
, NULL
)) != -1) {
543 err_exclusive_options(c
, longopts
, excl
, excl_st
);
553 ctl
.user_pagesize
= strtou32_or_err(optarg
, _("parsing page size failed"));
559 ctl
.opt_label
= optarg
;
562 version
= strtos32_or_err(optarg
, _("parsing version number failed"));
563 if (version
!= SWAP_VERSION
)
565 _("swapspace version %d is not supported"), version
);
571 warnx(_("warning: ignoring -U (UUIDs are unsupported by %s)"),
572 program_invocation_short_name
);
576 if (strcmp(optarg
, "native") == 0) {
577 ctl
.endianness
= ENDIANNESS_NATIVE
;
578 } else if (strcmp(optarg
, "little") == 0) {
579 ctl
.endianness
= ENDIANNESS_LITTLE
;
580 } else if (strcmp(optarg
, "big") == 0) {
581 ctl
.endianness
= ENDIANNESS_BIG
;
584 _("invalid endianness %s is not supported"), optarg
);
588 ctl
.offset
= str2unum_or_err(optarg
,
589 10, _("Invalid offset"), SINT_MAX(off_t
));
592 print_version(EXIT_SUCCESS
);
599 ctl
.lockmode
= optarg
;
608 errtryhelp(EXIT_FAILURE
);
613 ctl
.devname
= argv
[optind
++];
615 block_count
= argv
[optind
++];
616 if (optind
!= argc
) {
617 warnx(_("only one device argument is currently supported"));
618 errtryhelp(EXIT_FAILURE
);
623 if (strcmp(opt_uuid
, "clear") == 0)
624 uuid_clear(uuid_dat
);
625 else if (strcmp(opt_uuid
, "random") == 0)
626 uuid_generate_random(uuid_dat
);
627 else if (strcmp(opt_uuid
, "time") == 0)
628 uuid_generate_time(uuid_dat
);
629 else if (uuid_parse(opt_uuid
, uuid_dat
) != 0)
630 errx(EXIT_FAILURE
, _("error: parsing UUID failed"));
632 uuid_generate(uuid_dat
);
636 init_signature_page(&ctl
); /* get pagesize and allocate signature page */
639 warnx(_("error: Nowhere to set up swap on?"));
640 errtryhelp(EXIT_FAILURE
);
643 /* this silly user specified the number of blocks explicitly */
644 uint64_t blks
= strtou64_or_err(block_count
,
645 _("invalid block count argument"));
646 ctl
.npages
= blks
/ (ctl
.pagesize
/ 1024);
652 else if (ctl
.npages
> sz
&& !ctl
.force
)
655 "size %llu KiB is larger than device size %"PRIu64
" KiB"),
656 ctl
.npages
* (ctl
.pagesize
/ 1024), sz
* (ctl
.pagesize
/ 1024));
658 if (ctl
.npages
< MIN_GOODPAGES
)
660 _("error: swap area needs to be at least %ld KiB"),
661 (long)(MIN_GOODPAGES
* ctl
.pagesize
/ 1024));
662 if (ctl
.npages
> UINT32_MAX
) {
663 /* true when swap is bigger than 17.59 terabytes */
664 ctl
.npages
= UINT32_MAX
;
666 warnx(_("warning: truncating swap area to %llu KiB"),
667 ctl
.npages
* ctl
.pagesize
/ 1024);
670 if (is_mounted(ctl
.devname
))
671 errx(EXIT_FAILURE
, _("error: "
672 "%s is mounted; will not make swapspace"),
676 permMask
= S_ISBLK(ctl
.devstat
.st_mode
) ? 07007 : 07077;
677 if (!ctl
.quiet
&& (ctl
.devstat
.st_mode
& permMask
) != 0)
678 warnx(_("%s: insecure permissions %04o, fix with: chmod %04o %s"),
679 ctl
.devname
, ctl
.devstat
.st_mode
& 07777,
680 ~permMask
& 0666, ctl
.devname
);
682 && getuid() == 0 && S_ISREG(ctl
.devstat
.st_mode
) && ctl
.devstat
.st_uid
!= 0)
683 warnx(_("%s: insecure file owner %d, fix with: chown 0:0 %s"),
684 ctl
.devname
, ctl
.devstat
.st_uid
, ctl
.devname
);
689 #ifdef HAVE_LINUX_FIEMAP_H
690 if (!ctl
.quiet
&& S_ISREG(ctl
.devstat
.st_mode
))
697 ctl
.hdr
->version
= cpu32_to_endianness(version
, ctl
.endianness
);
698 ctl
.hdr
->last_page
= cpu32_to_endianness(ctl
.npages
- 1, ctl
.endianness
);
699 ctl
.hdr
->nr_badpages
= cpu32_to_endianness(ctl
.nbadpages
, ctl
.endianness
);
701 if ((ctl
.npages
- MIN_GOODPAGES
) < ctl
.nbadpages
)
702 errx(EXIT_FAILURE
, _("Unable to set up swap-space: unreadable"));
704 sz
= (ctl
.npages
- ctl
.nbadpages
- 1) * ctl
.pagesize
;
705 strsz
= size_to_human_string(SIZE_SUFFIX_SPACE
| SIZE_SUFFIX_3LETTER
, sz
);
708 printf(_("Setting up swapspace version %d, size = %s (%"PRIu64
" bytes)\n"),
713 set_uuid_and_label(&ctl
);
715 write_header_to_device(&ctl
);
717 deinit_signature_page(&ctl
);
719 #ifdef HAVE_LIBSELINUX
720 if (S_ISREG(ctl
.devstat
.st_mode
) && is_selinux_enabled() > 0) {
721 const char *context_string
;
723 context_t newcontext
;
725 if (fgetfilecon(ctl
.fd
, &oldcontext
) < 0) {
726 if (errno
!= ENODATA
)
728 _("%s: unable to obtain selinux file label"),
730 if (ul_selinux_get_default_context(ctl
.devname
,
731 ctl
.devstat
.st_mode
, &oldcontext
))
733 _("%s: unable to obtain default selinux file label"),
736 if (!(newcontext
= context_new(oldcontext
)))
737 errx(EXIT_FAILURE
, _("unable to create new selinux context"));
738 if (context_type_set(newcontext
, SELINUX_SWAPFILE_TYPE
))
739 errx(EXIT_FAILURE
, _("couldn't compute selinux context"));
741 context_string
= context_str(newcontext
);
743 if (strcmp(context_string
, oldcontext
)!=0) {
744 if (fsetfilecon(ctl
.fd
, context_string
) && errno
!= ENOTSUP
)
745 err(EXIT_FAILURE
, _("unable to relabel %s to %s"),
746 ctl
.devname
, context_string
);
748 context_free(newcontext
);
753 * A subsequent swapon() will fail if the signature
754 * is not actually on disk. (This is a kernel bug.)
755 * The fsync() in close_fd() will take care of writing.
757 if (close_fd(ctl
.fd
) != 0)
758 err(EXIT_FAILURE
, _("write failed"));