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