More cleanups.
[oweals/busybox.git] / 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                 errorMsg("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         errorMsg("%s\n", 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                 errorMsg("error: Nowhere to set up swap on?\n");
349                 usage(mkswap_usage);
350         }
351         sz = get_size(device_name);
352         if (!PAGES) {
353                 PAGES = sz;
354         } else if (PAGES > sz && !force) {
355                 errorMsg("error: size %ld is larger than device size %d\n",
356                                 PAGES * (pagesize / 1024), sz * (pagesize / 1024));
357                 exit(FALSE);
358         }
359
360         if (version == -1) {
361                 if (PAGES <= V0_MAX_PAGES)
362                         version = 0;
363                 else if (linux_version_code() < MAKE_VERSION(2, 1, 117))
364                         version = 0;
365                 else if (pagesize < 2048)
366                         version = 0;
367                 else
368                         version = 1;
369         }
370         if (version != 0 && version != 1) {
371                 errorMsg("error: unknown version %d\n", version);
372                 usage(mkswap_usage);
373         }
374         if (PAGES < 10) {
375                 errorMsg("error: swap area needs to be at least %ldkB\n",
376                                 (long) (10 * pagesize / 1024));
377                 usage(mkswap_usage);
378         }
379 #if 0
380         maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
381 #else
382         if (!version)
383                 maxpages = V0_MAX_PAGES;
384         else if (linux_version_code() >= MAKE_VERSION(2, 2, 1))
385                 maxpages = V1_MAX_PAGES;
386         else {
387                 maxpages = V1_OLD_MAX_PAGES;
388                 if (maxpages > V1_MAX_PAGES)
389                         maxpages = V1_MAX_PAGES;
390         }
391 #endif
392         if (PAGES > maxpages) {
393                 PAGES = maxpages;
394                 errorMsg("warning: truncating swap area to %ldkB\n",
395                                 PAGES * pagesize / 1024);
396         }
397
398         DEV = open(device_name, O_RDWR);
399         if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
400                 perror(device_name);
401                 exit(FALSE);
402         }
403         if (!S_ISBLK(statbuf.st_mode))
404                 check = 0;
405         else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
406                 die("Will not try to make swapdevice on '%s'");
407
408 #ifdef __sparc__
409         if (!force && version == 0) {
410                 /* Don't overwrite partition table unless forced */
411                 unsigned char *buffer = (unsigned char *) signature_page;
412                 unsigned short *q, sum;
413
414                 if (read(DEV, buffer, 512) != 512)
415                         die("fatal: first page unreadable");
416                 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
417                         q = (unsigned short *) (buffer + 510);
418                         for (sum = 0; q >= (unsigned short *) buffer;)
419                                 sum ^= *q--;
420                         if (!sum) {
421                                 errorMsg("Device '%s' contains a valid Sun disklabel.\n"
422 "This probably means creating v0 swap would destroy your partition table\n"
423 "No swap created. If you really want to create swap v0 on that device, use\n"
424 "the -f option to force it.\n", device_name);
425                                 exit(FALSE);
426                         }
427                 }
428         }
429 #endif
430
431         if (version == 0 || check)
432                 check_blocks();
433         if (version == 0 && !bit_test_and_clear(signature_page, 0))
434                 die("fatal: first page unreadable");
435         if (version == 1) {
436                 p->version = version;
437                 p->last_page = PAGES - 1;
438                 p->nr_badpages = badpages;
439         }
440
441         goodpages = PAGES - badpages - 1;
442         if (goodpages <= 0)
443                 die("Unable to set up swap-space: unreadable");
444         printf("Setting up swapspace version %d, size = %ld bytes\n",
445                    version, (long) (goodpages * pagesize));
446         write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
447
448         offset = ((version == 0) ? 0 : 1024);
449         if (lseek(DEV, offset, SEEK_SET) != offset)
450                 die("unable to rewind swap-device");
451         if (write(DEV, (char *) signature_page + offset, pagesize - offset)
452                 != pagesize - offset)
453                 die("unable to write signature page");
454
455         /*
456          * A subsequent swapon() will fail if the signature
457          * is not actually on disk. (This is a kernel bug.)
458          */
459         if (fsync(DEV))
460                 die("fsync failed");
461         return(TRUE);
462 }