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