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