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