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