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