20096361f9df283e377454c6c7fe9b13b7178b6b
[oweals/busybox.git] / util-linux / mkswap.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * mkswap.c - set up a linux swap device
4  *
5  * (C) 1991 Linus Torvalds. This file may be redistributed as per
6  * the Linux copyright.
7  */
8
9 /*
10  * 20.12.91  -  time began. Got VM working yesterday by doing this by hand.
11  *
12  * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks]
13  *
14  *      -c   for readability checking. (Use it unless you are SURE!)
15  *      -vN  for swap areas version N. (Only N=0,1 known today.)
16  *      -f   for forcing swap creation even if it would smash partition table.
17  *
18  * The device may be a block device or an image of one, but this isn't
19  * enforced (but it's not much fun on a character device :-).
20  *
21  * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
22  * size-in-blocks parameter optional added Wed Feb  8 10:33:43 1995.
23  *
24  * Version 1 swap area code (for kernel 2.1.117), aeb, 981010.
25  *
26  * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb.
27  * V1_MAX_PAGES fixes, jj, 990325.
28  *
29  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
30  * - added Native Language Support
31  *
32  *  from util-linux -- adapted for busybox by
33  *  Erik Andersen <andersee@debian.org>. I ripped out Native Language
34  *  Support, made some stuff smaller, and fitted for life in busybox.
35  *
36  */
37
38 #include "internal.h"
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <fcntl.h>
43 #include <stdlib.h>
44 #include <sys/ioctl.h>                  /* for _IO */
45 #include <sys/utsname.h>
46 #include <sys/stat.h>
47 #include <asm/page.h>                   /* for PAGE_SIZE and PAGE_SHIFT */
48                                 /* we also get PAGE_SIZE via getpagesize() */
49
50
51 static const char mkswap_usage[] =
52         "mkswap [-c] [-v0|-v1] device [block-count]\n"
53 #ifndef BB_FEATURE_TRIVIAL_HELP
54         "\nPrepare a disk partition to be used as a swap partition.\n\n"
55         "Options:\n" "\t-c\t\tCheck for read-ability.\n"
56         "\t-v0\t\tMake version 0 swap [max 128 Megs].\n"
57         "\t-v1\t\tMake version 1 swap [big!] (default for kernels > 2.1.117).\n"
58
59         "\tblock-count\tNumber of block to use (default is entire partition).\n"
60 #endif
61         ;
62
63
64 #ifndef _IO
65 /* pre-1.3.45 */
66 #define BLKGETSIZE 0x1260
67 #else
68 /* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */
69 #define BLKGETSIZE _IO(0x12,96)
70 #endif
71
72 static char *device_name = NULL;
73 static int DEV = -1;
74 static long PAGES = 0;
75 static int check = 0;
76 static int badpages = 0;
77 static int version = -1;
78
79 #define MAKE_VERSION(p,q,r)     (65536*(p) + 256*(q) + (r))
80
81 static int linux_version_code(void)
82 {
83         struct utsname my_utsname;
84         int p, q, r;
85
86         if (uname(&my_utsname) == 0) {
87                 p = atoi(strtok(my_utsname.release, "."));
88                 q = atoi(strtok(NULL, "."));
89                 r = atoi(strtok(NULL, "."));
90                 return MAKE_VERSION(p, q, r);
91         }
92         return 0;
93 }
94
95 /*
96  * The definition of the union swap_header uses the constant PAGE_SIZE.
97  * Unfortunately, on some architectures this depends on the hardware model,
98  * and can only be found at run time -- we use getpagesize().
99  */
100
101 static int pagesize;
102 static int *signature_page;
103
104 struct swap_header_v1 {
105         char bootbits[1024];            /* Space for disklabel etc. */
106         unsigned int version;
107         unsigned int last_page;
108         unsigned int nr_badpages;
109         unsigned int padding[125];
110         unsigned int badpages[1];
111 } *p;
112
113 static void init_signature_page()
114 {
115         pagesize = getpagesize();
116
117 #ifdef PAGE_SIZE
118         if (pagesize != PAGE_SIZE)
119                 fprintf(stderr, "Assuming pages of size %d\n", pagesize);
120 #endif
121         signature_page = (int *) xmalloc(pagesize);
122         memset(signature_page, 0, pagesize);
123         p = (struct swap_header_v1 *) signature_page;
124 }
125
126 static void write_signature(char *sig)
127 {
128         char *sp = (char *) signature_page;
129
130         strncpy(sp + pagesize - 10, sig, 10);
131 }
132
133 #define V0_MAX_PAGES    (8 * (pagesize - 10))
134 /* Before 2.2.0pre9 */
135 #define V1_OLD_MAX_PAGES        ((0x7fffffff / pagesize) - 1)
136 /* Since 2.2.0pre9:
137    error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL))
138    with variations on
139         #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
140         #define SWP_OFFSET(entry) ((entry) >> 8)
141    on the various architectures. Below the result - yuk.
142
143    Machine      pagesize        SWP_ENTRY       SWP_OFFSET      bound+1 oldbound+2
144    i386         2^12            o<<8            e>>8            1<<24   1<<19
145    mips         2^12            o<<15           e>>15           1<<17   1<<19
146    alpha        2^13            o<<40           e>>40           1<<24   1<<18
147    m68k         2^12            o<<12           e>>12           1<<20   1<<19
148    sparc        2^{12,13}       (o&0x3ffff)<<9  (e>>9)&0x3ffff  1<<18   1<<{19,18}
149    sparc64      2^13            o<<13           e>>13           1<<51   1<<18
150    ppc          2^12            o<<8            e>>8            1<<24   1<<19
151    armo         2^{13,14,15}    o<<8            e>>8            1<<24   1<<{18,17,16}
152    armv         2^12            o<<9            e>>9            1<<23   1<<19
153
154    assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere.
155
156    The bad part is that we need to know this since the kernel will
157    refuse a swap space if it is too large.
158 */
159 /* patch from jj - why does this differ from the above? */
160 #if defined(__alpha__)
161 #define V1_MAX_PAGES           ((1 << 24) - 1)
162 #elif defined(__mips__)
163 #define V1_MAX_PAGES           ((1 << 17) - 1)
164 #elif defined(__sparc_v9__)
165 #define V1_MAX_PAGES           ((3 << 29) - 1)
166 #elif defined(__sparc__)
167 #define V1_MAX_PAGES           (pagesize == 8192 ? ((3 << 29) - 1) : ((1 << 18) - 1))
168 #else
169 #define V1_MAX_PAGES           V1_OLD_MAX_PAGES
170 #endif
171 /* man page now says:
172 The maximum useful size of a swap area now depends on the architecture.
173 It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips,
174 128GB on alpha and 3TB on sparc64.
175 */
176
177 #define MAX_BADPAGES    ((pagesize-1024-128*sizeof(int)-10)/sizeof(int))
178
179 static void bit_set(unsigned int *addr, unsigned int nr)
180 {
181         unsigned int r, m;
182
183         addr += nr / (8 * sizeof(int));
184
185         r = *addr;
186         m = 1 << (nr & (8 * sizeof(int) - 1));
187
188         *addr = r | m;
189 }
190
191 static int bit_test_and_clear(unsigned int *addr, unsigned int nr)
192 {
193         unsigned int r, m;
194
195         addr += nr / (8 * sizeof(int));
196
197         r = *addr;
198         m = 1 << (nr & (8 * sizeof(int) - 1));
199
200         *addr = r & ~m;
201         return (r & m) != 0;
202 }
203
204
205 void die(const char *str)
206 {
207         fprintf(stderr, "%s: %s\n", applet_name, str);
208         exit(FALSE);
209 }
210
211 void page_ok(int page)
212 {
213         if (version == 0)
214                 bit_set(signature_page, page);
215 }
216
217 void page_bad(int page)
218 {
219         if (version == 0)
220                 bit_test_and_clear(signature_page, page);
221         else {
222                 if (badpages == MAX_BADPAGES)
223                         die("too many bad pages");
224                 p->badpages[badpages] = page;
225         }
226         badpages++;
227 }
228
229 void check_blocks(void)
230 {
231         unsigned int current_page;
232         int do_seek = 1;
233         char *buffer;
234
235         buffer = xmalloc(pagesize);
236         current_page = 0;
237         while (current_page < PAGES) {
238                 if (!check) {
239                         page_ok(current_page++);
240                         continue;
241                 }
242                 if (do_seek && lseek(DEV, current_page * pagesize, SEEK_SET) !=
243                         current_page * pagesize)
244                         die("seek failed in check_blocks");
245                 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
246                         page_bad(current_page++);
247                         continue;
248                 }
249                 page_ok(current_page++);
250         }
251         if (badpages == 1)
252                 printf("one bad page\n");
253         else if (badpages > 1)
254                 printf("%d bad pages\n", badpages);
255 }
256
257 static long valid_offset(int fd, int offset)
258 {
259         char ch;
260
261         if (lseek(fd, offset, 0) < 0)
262                 return 0;
263         if (read(fd, &ch, 1) < 1)
264                 return 0;
265         return 1;
266 }
267
268 static int find_size(int fd)
269 {
270         unsigned int high, low;
271
272         low = 0;
273         for (high = 1; high > 0 && valid_offset(fd, high); high *= 2)
274                 low = high;
275         while (low < high - 1) {
276                 const int mid = (low + high) / 2;
277
278                 if (valid_offset(fd, mid))
279                         low = mid;
280                 else
281                         high = mid;
282         }
283         return (low + 1);
284 }
285
286 /* return size in pages, to avoid integer overflow */
287 static long get_size(const char *file)
288 {
289         int fd;
290         long size;
291
292         fd = open(file, O_RDONLY);
293         if (fd < 0) {
294                 perror(file);
295                 exit(1);
296         }
297         if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
298                 int sectors_per_page = pagesize / 512;
299
300                 size /= sectors_per_page;
301         } else {
302                 size = find_size(fd) / pagesize;
303         }
304         close(fd);
305         return size;
306 }
307
308 int mkswap_main(int argc, char **argv)
309 {
310         char *tmp;
311         struct stat statbuf;
312         int sz;
313         int maxpages;
314         int goodpages;
315         int offset;
316         int force = 0;
317
318         init_signature_page();          /* get pagesize */
319
320         while (argc-- > 1) {
321                 argv++;
322                 if (argv[0][0] != '-') {
323                         if (device_name) {
324                                 int blocks_per_page = pagesize / 1024;
325
326                                 PAGES = strtol(argv[0], &tmp, 0) / blocks_per_page;
327                                 if (*tmp)
328                                         usage(mkswap_usage);
329                         } else
330                                 device_name = argv[0];
331                 } else {
332                         switch (argv[0][1]) {
333                         case 'c':
334                                 check = 1;
335                                 break;
336                         case 'f':
337                                 force = 1;
338                                 break;
339                         case 'v':
340                                 version = atoi(argv[0] + 2);
341                                 break;
342                         default:
343                                 usage(mkswap_usage);
344                         }
345                 }
346         }
347         if (!device_name) {
348                 fprintf(stderr,
349                                 "%s: error: Nowhere to set up swap on?\n", applet_name);
350                 usage(mkswap_usage);
351         }
352         sz = get_size(device_name);
353         if (!PAGES) {
354                 PAGES = sz;
355         } else if (PAGES > sz && !force) {
356                 fprintf(stderr,
357                                 "%s: error: "
358                                 "size %ld is larger than device size %d\n",
359                                 applet_name,
360                                 PAGES * (pagesize / 1024), sz * (pagesize / 1024));
361                 exit(FALSE);
362         }
363
364         if (version == -1) {
365                 if (PAGES <= V0_MAX_PAGES)
366                         version = 0;
367                 else if (linux_version_code() < MAKE_VERSION(2, 1, 117))
368                         version = 0;
369                 else if (pagesize < 2048)
370                         version = 0;
371                 else
372                         version = 1;
373         }
374         if (version != 0 && version != 1) {
375                 fprintf(stderr, "%s: error: unknown version %d\n",
376                                 applet_name, version);
377                 usage(mkswap_usage);
378         }
379         if (PAGES < 10) {
380                 fprintf(stderr,
381                                 "%s: error: swap area needs to be at least %ldkB\n",
382                                 applet_name, (long) (10 * pagesize / 1024));
383                 usage(mkswap_usage);
384         }
385 #if 0
386         maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
387 #else
388         if (!version)
389                 maxpages = V0_MAX_PAGES;
390         else if (linux_version_code() >= MAKE_VERSION(2, 2, 1))
391                 maxpages = V1_MAX_PAGES;
392         else {
393                 maxpages = V1_OLD_MAX_PAGES;
394                 if (maxpages > V1_MAX_PAGES)
395                         maxpages = V1_MAX_PAGES;
396         }
397 #endif
398         if (PAGES > maxpages) {
399                 PAGES = maxpages;
400                 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n",
401                                 applet_name, PAGES * pagesize / 1024);
402         }
403
404         DEV = open(device_name, O_RDWR);
405         if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
406                 perror(device_name);
407                 exit(FALSE);
408         }
409         if (!S_ISBLK(statbuf.st_mode))
410                 check = 0;
411         else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
412                 die("Will not try to make swapdevice on '%s'");
413
414 #ifdef __sparc__
415         if (!force && version == 0) {
416                 /* Don't overwrite partition table unless forced */
417                 unsigned char *buffer = (unsigned char *) signature_page;
418                 unsigned short *q, sum;
419
420                 if (read(DEV, buffer, 512) != 512)
421                         die("fatal: first page unreadable");
422                 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
423                         q = (unsigned short *) (buffer + 510);
424                         for (sum = 0; q >= (unsigned short *) buffer;)
425                                 sum ^= *q--;
426                         if (!sum) {
427                                 fprintf(stderr, "\
428 %s: Device '%s' contains a valid Sun disklabel.\n\
429 This probably means creating v0 swap would destroy your partition table\n\
430 No swap created. If you really want to create swap v0 on that device, use\n\
431 the -f option to force it.\n", applet_name, device_name);
432                                 exit(FALSE);
433                         }
434                 }
435         }
436 #endif
437
438         if (version == 0 || check)
439                 check_blocks();
440         if (version == 0 && !bit_test_and_clear(signature_page, 0))
441                 die("fatal: first page unreadable");
442         if (version == 1) {
443                 p->version = version;
444                 p->last_page = PAGES - 1;
445                 p->nr_badpages = badpages;
446         }
447
448         goodpages = PAGES - badpages - 1;
449         if (goodpages <= 0)
450                 die("Unable to set up swap-space: unreadable");
451         printf("Setting up swapspace version %d, size = %ld bytes\n",
452                    version, (long) (goodpages * pagesize));
453         write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
454
455         offset = ((version == 0) ? 0 : 1024);
456         if (lseek(DEV, offset, SEEK_SET) != offset)
457                 die("unable to rewind swap-device");
458         if (write(DEV, (char *) signature_page + offset, pagesize - offset)
459                 != pagesize - offset)
460                 die("unable to write signature page");
461
462         /*
463          * A subsequent swapon() will fail if the signature
464          * is not actually on disk. (This is a kernel bug.)
465          */
466         if (fsync(DEV))
467                 die("fsync failed");
468         return(TRUE);
469 }