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