]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/mkswap.c
mkswap: remove v0 swap space support
[thirdparty/util-linux.git] / disk-utils / mkswap.c
1 /*
2 * mkswap.c - set up a linux swap device
3 *
4 * (C) 1991 Linus Torvalds. This file may be redistributed as per
5 * the Linux copyright.
6 */
7
8 /*
9 * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
10 *
11 * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks]
12 *
13 * -c for readability checking. (Use it unless you are SURE!)
14 * -vN for swap areas version N. (Only N=0,1 known today.)
15 * -f for forcing swap creation even if it would smash partition table.
16 *
17 * The device may be a block device or an image of one, but this isn't
18 * enforced (but it's not much fun on a character device :-).
19 *
20 * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
21 * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995.
22 *
23 * Version 1 swap area code (for kernel 2.1.117), aeb, 981010.
24 *
25 * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb.
26 * V1_MAX_PAGES fixes, jj, 990325.
27 * sparc64 fixes, jj, 000219.
28 *
29 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
30 * - added Native Language Support
31 *
32 */
33
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <limits.h>
40 #include <mntent.h>
41 #include <sys/ioctl.h> /* for _IO */
42 #include <sys/utsname.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 #ifdef HAVE_LIBSELINUX
46 #include <selinux/selinux.h>
47 #include <selinux/context.h>
48 #endif
49
50 #include "linux_version.h"
51 #include "swapheader.h"
52 #include "xstrncpy.h"
53 #include "nls.h"
54 #include "blkdev.h"
55 #include "pathnames.h"
56
57 #ifdef HAVE_LIBUUID
58 #include <uuid/uuid.h>
59 #endif
60
61 static char * program_name = "mkswap";
62 static char * device_name = NULL;
63 static int DEV = -1;
64 static unsigned long long PAGES = 0;
65 static unsigned long badpages = 0;
66 static int check = 0;
67
68 #define SELINUX_SWAPFILE_TYPE "swapfile_t"
69
70 #ifdef __sparc__
71 # ifdef __arch64__
72 # define is_sparc64() 1
73 # define is_be64() 1
74 # else /* sparc32 */
75 static int
76 is_sparc64(void) {
77 struct utsname un;
78 static int sparc64 = -1;
79
80 if (sparc64 != -1) return sparc64;
81 sparc64 = 0;
82
83 if (uname(&un) < 0) return 0;
84 if (! strcmp(un.machine, "sparc64")) {
85 sparc64 = 1;
86 return 1;
87 }
88 if (strcmp(un.machine, "sparc"))
89 return 0; /* Should not happen */
90
91 #ifdef HAVE_PERSONALITY
92 {
93 extern int personality(unsigned long);
94 int oldpers;
95 #define PERS_LINUX 0x00000000
96 #define PERS_LINUX_32BIT 0x00800000
97 #define PERS_LINUX32 0x00000008
98
99 oldpers = personality(PERS_LINUX_32BIT);
100 if (oldpers != -1) {
101 if (personality(PERS_LINUX) != -1) {
102 uname(&un);
103 if (! strcmp(un.machine, "sparc64")) {
104 sparc64 = 1;
105 oldpers = PERS_LINUX32;
106 }
107 }
108 personality(oldpers);
109 }
110 }
111 #endif
112
113 return sparc64;
114 }
115 # define is_be64() is_sparc64()
116 # endif /* sparc32 */
117 #else /* !sparc */
118 # define is_be64() 0
119 #endif
120
121 /*
122 * The definition of the union swap_header uses the kernel constant PAGE_SIZE.
123 * Unfortunately, on some architectures this depends on the hardware model, and
124 * can only be found at run time -- we use getpagesize(), so that we do not
125 * need separate binaries e.g. for sun4, sun4c/d/m and sun4u.
126 *
127 * Even more unfortunately, getpagesize() does not always return the right
128 * information. For example, libc4, libc5 and glibc 2.0 do not use the system
129 * call but invent a value themselves (EXEC_PAGESIZE or NBPG * CLSIZE or NBPC),
130 * and thus it may happen that e.g. on a sparc kernel PAGE_SIZE=4096 and
131 * getpagesize() returns 8192.
132 *
133 * What to do? Let us allow the user to specify the pagesize explicitly.
134 *
135 */
136 static int user_pagesize;
137 static int pagesize;
138 static unsigned long *signature_page;
139 struct swap_header_v1 *p;
140
141 static void
142 init_signature_page(void) {
143
144 int kernel_pagesize = pagesize = getpagesize();
145
146 if (user_pagesize) {
147 if ((user_pagesize & (user_pagesize-1)) ||
148 user_pagesize < 1024) {
149 fprintf(stderr, _("Bad user-specified page size %d\n"),
150 user_pagesize);
151 exit(1);
152 }
153 pagesize = user_pagesize;
154 }
155
156 if (user_pagesize && user_pagesize != kernel_pagesize)
157 fprintf(stderr, _("Using user-specified page size %d, "
158 "instead of the system value %d\n"),
159 pagesize, kernel_pagesize);
160
161 signature_page = (unsigned long *) malloc(pagesize);
162 memset(signature_page, 0, pagesize);
163 p = (struct swap_header_v1 *) signature_page;
164 }
165
166 static void
167 write_signature(char *sig) {
168 char *sp = (char *) signature_page;
169
170 strncpy(sp+pagesize-10, sig, 10);
171 }
172
173 static void
174 write_uuid_and_label(unsigned char *uuid, char *volume_name) {
175 struct swap_header_v1_2 *h;
176
177 /* Sanity check */
178 if (sizeof(struct swap_header_v1) !=
179 sizeof(struct swap_header_v1_2)) {
180 fprintf(stderr,
181 _("Bad swap header size, no label written.\n"));
182 return;
183 }
184
185 h = (struct swap_header_v1_2 *) signature_page;
186 if (uuid)
187 memcpy(h->uuid, uuid, sizeof(h->uuid));
188 if (volume_name) {
189 xstrncpy(h->volume_name, volume_name, sizeof(h->volume_name));
190 if (strlen(volume_name) > strlen(h->volume_name))
191 fprintf(stderr, _("Label was truncated.\n"));
192 }
193 if (uuid || volume_name) {
194 if (volume_name)
195 printf("LABEL=%s, ", h->volume_name);
196 else
197 printf(_("no label, "));
198 #ifdef HAVE_LIBUUID
199 if (uuid) {
200 char uuid_string[37];
201 uuid_unparse(uuid, uuid_string);
202 printf("UUID=%s\n", uuid_string);
203 } else
204 #endif
205 printf(_("no uuid\n"));
206 }
207 }
208
209 /*
210 * Find out what the maximum amount of swap space is that the kernel will
211 * handle. This wouldn't matter if the kernel just used as much of the
212 * swap space as it can handle, but until 2.3.4 it would return an error
213 * to swapon() if the swapspace was too large.
214 */
215 /* Before 2.2.0pre9 */
216 #define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1)
217 /* Since 2.2.0pre9, before 2.3.4:
218 error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL))
219 with variations on
220 #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
221 #define SWP_OFFSET(entry) ((entry) >> 8)
222 on the various architectures. Below the result - yuk.
223
224 Machine pagesize SWP_ENTRY SWP_OFFSET bound+1 oldbound+2
225 i386 2^12 o<<8 e>>8 1<<24 1<<19
226 mips 2^12 o<<15 e>>15 1<<17 1<<19
227 alpha 2^13 o<<40 e>>40 1<<24 1<<18
228 m68k 2^12 o<<12 e>>12 1<<20 1<<19
229 sparc 2^{12,13} (o&0x3ffff)<<9 (e>>9)&0x3ffff 1<<18 1<<{19,18}
230 sparc64 2^13 o<<13 e>>13 1<<51 1<<18
231 ppc 2^12 o<<8 e>>8 1<<24 1<<19
232 armo 2^{13,14,15} o<<8 e>>8 1<<24 1<<{18,17,16}
233 armv 2^12 o<<9 e>>9 1<<23 1<<19
234
235 assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere.
236
237 The bad part is that we need to know this since the kernel will
238 refuse a swap space if it is too large.
239 */
240 /* patch from jj - why does this differ from the above? */
241 /* 32bit kernels have a second limitation of 2GB, sparc64 is limited by
242 the size of virtual address space allocation for vmalloc */
243 #if defined(__alpha__)
244 #define V1_MAX_PAGES ((1 << 24) - 1)
245 #elif defined(__mips__)
246 #define V1_MAX_PAGES ((1 << 17) - 1)
247 #elif defined(__sparc__)
248 #define V1_MAX_PAGES (is_sparc64() ? ((3 << 29) - 1) : ((1 << 18) - 1))
249 #elif defined(__ia64__)
250 /*
251 * The actual size will depend on the amount of virtual address space
252 * available to vmalloc the swap map.
253 */
254 #define V1_MAX_PAGES ((1UL << 54) - 1)
255 #else
256 #define V1_MAX_PAGES V1_OLD_MAX_PAGES
257 #endif
258 /* man page now says:
259 The maximum useful size of a swap area now depends on the architecture.
260 It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips,
261 128GB on alpha and 3TB on sparc64.
262 */
263
264 #define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int))
265 #define MIN_GOODPAGES 10
266
267 static void
268 usage(void) {
269 fprintf(stderr,
270 _("Usage: %s [-c] [-pPAGESZ] [-L label] [-U UUID] /dev/name [blocks]\n"),
271 program_name);
272 exit(1);
273 }
274
275 static void
276 die(const char *str) {
277 fprintf(stderr, "%s: %s\n", program_name, str);
278 exit(1);
279 }
280
281 static void
282 page_bad(int page) {
283 if (badpages == MAX_BADPAGES)
284 die(_("too many bad pages"));
285 p->badpages[badpages] = page;
286 badpages++;
287 }
288
289 static void
290 check_blocks(void) {
291 unsigned int current_page;
292 int do_seek = 1;
293 char *buffer;
294
295 buffer = malloc(pagesize);
296 if (!buffer)
297 die(_("Out of memory"));
298 current_page = 0;
299 while (current_page < PAGES) {
300 if (!check)
301 continue;
302 if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) !=
303 current_page*pagesize)
304 die(_("seek failed in check_blocks"));
305 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
306 page_bad(current_page++);
307 continue;
308 }
309 }
310 if (badpages == 1)
311 printf(_("one bad page\n"));
312 else if (badpages > 1)
313 printf(_("%lu bad pages\n"), badpages);
314 }
315
316 /* return size in pages */
317 static unsigned long long
318 get_size(const char *file) {
319 int fd;
320 unsigned long long size;
321
322 fd = open(file, O_RDONLY);
323 if (fd < 0) {
324 perror(file);
325 exit(1);
326 }
327 if (blkdev_get_size(fd, &size) == 0)
328 size /= pagesize;
329 else
330 size = blkdev_find_size(fd) / pagesize;
331
332 close(fd);
333 return size;
334 }
335
336 static int
337 isnzdigit(char c) {
338 return (c >= '1' && c <= '9');
339 }
340
341
342 /*
343 * Check to make certain that our new filesystem won't be created on
344 * an already mounted partition. Code adapted from mke2fs, Copyright
345 * (C) 1994 Theodore Ts'o. Also licensed under GPL.
346 * (C) 2006 Karel Zak -- port to mkswap
347 */
348 static int
349 check_mount(void) {
350 FILE * f;
351 struct mntent * mnt;
352
353 if ((f = setmntent (_PATH_MOUNTED, "r")) == NULL)
354 return 0;
355 while ((mnt = getmntent (f)) != NULL)
356 if (strcmp (device_name, mnt->mnt_fsname) == 0)
357 break;
358 endmntent (f);
359 if (!mnt)
360 return 0;
361 return 1;
362 }
363
364
365 static int
366 write_all(int fd, const void *buf, size_t count) {
367 while(count) {
368 ssize_t tmp;
369
370 errno = 0;
371 tmp = write(fd, buf, count);
372 if (tmp > 0) {
373 count -= tmp;
374 if (count)
375 buf += tmp;
376 } else if (errno != EINTR && errno != EAGAIN)
377 return -1;
378 }
379 return 0;
380 }
381
382 int
383 main(int argc, char ** argv) {
384 struct stat statbuf;
385 int i;
386 unsigned long long maxpages;
387 unsigned long long goodpages;
388 unsigned long long sz;
389 off_t offset;
390 int force = 0;
391 int version = 1;
392 char *block_count = 0;
393 char *pp;
394 char *opt_label = NULL;
395 unsigned char *uuid = NULL;
396 #ifdef HAVE_LIBUUID
397 const char *opt_uuid = NULL;
398 uuid_t uuid_dat;
399 #endif
400
401 program_name = (argc && *argv) ? argv[0] : "mkswap";
402 if ((pp = strrchr(program_name, '/')) != NULL)
403 program_name = pp+1;
404
405 setlocale(LC_ALL, "");
406 bindtextdomain(PACKAGE, LOCALEDIR);
407 textdomain(PACKAGE);
408
409 if (argc == 2 &&
410 (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))) {
411 printf(_("%s (%s)\n"), program_name, PACKAGE_STRING);
412 exit(0);
413 }
414
415 for (i=1; i<argc; i++) {
416 if (argv[i][0] == '-') {
417 switch (argv[i][1]) {
418 case 'c':
419 check=1;
420 break;
421 case 'f':
422 force=1;
423 break;
424 case 'p':
425 pp = argv[i]+2;
426 if (!*pp && i+1 < argc)
427 pp = argv[++i];
428 if (isnzdigit(*pp))
429 user_pagesize = atoi(pp);
430 else
431 usage();
432 break;
433 case 'L':
434 pp = argv[i]+2;
435 if (!*pp && i+1 < argc)
436 pp = argv[++i];
437 opt_label = pp;
438 break;
439 case 'v':
440 version = atoi(argv[i]+2);
441 break;
442 case 'U':
443 #ifdef HAVE_LIBUUID
444 opt_uuid = argv[i]+2;
445 if (!*opt_uuid && i+1 < argc)
446 opt_uuid = argv[++i];
447 #else
448 fprintf(stderr, _("%1$s: warning: ignore -U (UUIDs are unsupported by %1$s)\n"),
449 program_name);
450 #endif
451 break;
452 default:
453 usage();
454 }
455 } else if (!device_name) {
456 device_name = argv[i];
457 } else if (!block_count) {
458 block_count = argv[i];
459 } else
460 usage();
461 }
462
463 if (version != 1) {
464 fprintf(stderr, _("%s: does not support swapspace version %d.\n"),
465 program_name, version);
466 exit(EXIT_FAILURE);
467 }
468
469 #ifdef HAVE_LIBUUID
470 if(opt_uuid) {
471 if (uuid_parse(opt_uuid, uuid_dat) != 0)
472 die(_("error: UUID parsing failed"));
473 } else
474 uuid_generate(uuid_dat);
475 uuid = uuid_dat;
476 #endif
477
478 init_signature_page(); /* get pagesize */
479
480 if (!device_name) {
481 fprintf(stderr,
482 _("%s: error: Nowhere to set up swap on?\n"),
483 program_name);
484 usage();
485 }
486 if (block_count) {
487 /* this silly user specified the number of blocks
488 explicitly */
489 char *tmp;
490 int blocks_per_page = pagesize/1024;
491 PAGES = strtoull(block_count,&tmp,0)/blocks_per_page;
492 if (*tmp)
493 usage();
494 }
495 sz = get_size(device_name);
496 if (!PAGES) {
497 PAGES = sz;
498 } else if (PAGES > sz && !force) {
499 fprintf(stderr,
500 _("%s: error: "
501 "size %llu KiB is larger than device size %llu KiB\n"),
502 program_name,
503 PAGES*(pagesize/1024), sz*(pagesize/1024));
504 exit(1);
505 }
506
507 if (PAGES < MIN_GOODPAGES) {
508 fprintf(stderr,
509 _("%s: error: swap area needs to be at least %ld KiB\n"),
510 program_name, (long)(MIN_GOODPAGES * pagesize/1024));
511 usage();
512 }
513
514 #ifdef __linux__
515 if (get_linux_version() >= KERNEL_VERSION(2,3,4))
516 maxpages = UINT_MAX + 1ULL;
517 else if (get_linux_version() >= KERNEL_VERSION(2,2,1))
518 maxpages = V1_MAX_PAGES;
519 else
520 #endif
521 maxpages = V1_OLD_MAX_PAGES;
522
523 if (PAGES > maxpages) {
524 PAGES = maxpages;
525 fprintf(stderr,
526 _("%s: warning: truncating swap area to %llu KiB\n"),
527 program_name, PAGES * pagesize / 1024);
528 }
529
530 if (stat(device_name, &statbuf) < 0) {
531 perror(device_name);
532 exit(EXIT_FAILURE);
533 }
534 if (S_ISBLK(statbuf.st_mode))
535 DEV = open(device_name, O_RDWR | O_EXCL);
536 else
537 DEV = open(device_name, O_RDWR);
538
539 if (DEV < 0) {
540 perror(device_name);
541 exit(1);
542 }
543
544 /* Want a block device. Probably not /dev/hda or /dev/hdb. */
545 if (!S_ISBLK(statbuf.st_mode))
546 check=0;
547 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) {
548 fprintf(stderr,
549 _("%s: error: "
550 "will not try to make swapdevice on '%s'\n"),
551 program_name, device_name);
552 exit(1);
553 } else if (check_mount()) {
554 fprintf(stderr,
555 _("%s: error: "
556 "%s is mounted; will not make swapspace.\n"),
557 program_name, device_name);
558 exit(1);
559 }
560
561 if (check)
562 check_blocks();
563
564 p->version = 1;
565 p->last_page = PAGES-1;
566 p->nr_badpages = badpages;
567
568 if (badpages > PAGES - MIN_GOODPAGES)
569 die(_("Unable to set up swap-space: unreadable"));
570
571 goodpages = PAGES - badpages - 1;
572 printf(_("Setting up swapspace version 1, size = %llu KiB\n"),
573 goodpages * pagesize / 1024);
574
575 write_signature("SWAPSPACE2");
576 write_uuid_and_label(uuid, opt_label);
577
578 offset = 1024;
579 if (lseek(DEV, offset, SEEK_SET) != offset)
580 die(_("unable to rewind swap-device"));
581 if (write_all(DEV, (char *) signature_page + offset,
582 pagesize - offset) == -1) {
583 fprintf(stderr, _("%s: %s: unable to write signature page: %s"),
584 program_name, device_name, strerror(errno));
585 exit(1);
586 }
587
588 /*
589 * A subsequent swapon() will fail if the signature
590 * is not actually on disk. (This is a kernel bug.)
591 */
592 #ifdef HAVE_FSYNC
593 if (fsync(DEV))
594 die(_("fsync failed"));
595 #endif
596
597 #ifdef HAVE_LIBSELINUX
598 if (S_ISREG(statbuf.st_mode) && is_selinux_enabled() > 0) {
599 security_context_t context_string;
600 security_context_t oldcontext;
601 context_t newcontext;
602
603 if (fgetfilecon(DEV, &oldcontext) < 0) {
604 if (errno != ENODATA) {
605 fprintf(stderr, _("%s: %s: unable to obtain selinux file label: %s\n"),
606 program_name, device_name,
607 strerror(errno));
608 exit(1);
609 }
610 if (matchpathcon(device_name, statbuf.st_mode, &oldcontext))
611 die(_("unable to matchpathcon()"));
612 }
613 if (!(newcontext = context_new(oldcontext)))
614 die(_("unable to create new selinux context"));
615 if (context_type_set(newcontext, SELINUX_SWAPFILE_TYPE))
616 die(_("couldn't compute selinux context"));
617
618 context_string = context_str(newcontext);
619
620 if (strcmp(context_string, oldcontext)!=0) {
621 if (fsetfilecon(DEV, context_string)) {
622 fprintf(stderr, _("%s: unable to relabel %s to %s: %s\n"),
623 program_name, device_name,
624 context_string,
625 strerror(errno));
626 exit(1);
627 }
628 }
629 context_free(newcontext);
630 freecon(oldcontext);
631 }
632 #endif
633 return 0;
634 }