Patch from Denis Vlasenko turning static const int (which gets emitted into
[oweals/busybox.git] / util-linux / 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 <andersen@codepoet.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 enum { 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 unsigned 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 inline void init_signature_page(void)
85 {
86         pagesize = getpagesize();
87
88 #ifdef PAGE_SIZE
89         if (pagesize != PAGE_SIZE)
90                 bb_error_msg("Assuming pages of size %d", pagesize);
91 #endif
92         signature_page = (unsigned int *) xmalloc(pagesize);
93         memset(signature_page, 0, pagesize);
94         p = (struct swap_header_v1 *) signature_page;
95 }
96
97 static inline 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 inline 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 inline 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                         bb_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                         bb_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                 bb_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                                         bb_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                                 bb_show_usage();
306                         }
307                 }
308         }
309         if (!device_name) {
310                 bb_error_msg("error: Nowhere to set up swap on?");
311                 bb_show_usage();
312         }
313         sz = get_size(device_name);
314         if (!PAGES) {
315                 PAGES = sz;
316         } else if (PAGES > sz && !force) {
317                 bb_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 (get_kernel_revision() < MAKE_VERSION(2, 1, 117))
324                         version = 0;
325                 else
326                         version = 1;
327         }
328         if (version != 0 && version != 1) {
329                 bb_error_msg("error: unknown version %d", version);
330                 bb_show_usage();
331         }
332         if (PAGES < 10) {
333                 bb_error_msg("error: swap area needs to be at least %ldkB",
334                                 (long) (10 * pagesize / 1024));
335                 bb_show_usage();
336         }
337 #if 0
338         maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
339 #else
340         if (!version)
341                 maxpages = V0_MAX_PAGES;
342         else if (get_kernel_revision() >= MAKE_VERSION(2, 2, 1))
343                 maxpages = V1_MAX_PAGES;
344         else {
345                 maxpages = V1_OLD_MAX_PAGES;
346                 if (maxpages > V1_MAX_PAGES)
347                         maxpages = V1_MAX_PAGES;
348         }
349 #endif
350         if (PAGES > maxpages) {
351                 PAGES = maxpages;
352                 bb_error_msg("warning: truncating swap area to %ldkB",
353                                 PAGES * pagesize / 1024);
354         }
355
356         DEV = open(device_name, O_RDWR);
357         if (DEV < 0 || fstat(DEV, &statbuf) < 0)
358                 bb_perror_msg_and_die("%s", device_name);
359         if (!S_ISBLK(statbuf.st_mode))
360                 check = 0;
361         else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
362                 bb_error_msg_and_die("Will not try to make swapdevice on '%s'", device_name);
363
364 #ifdef __sparc__
365         if (!force && version == 0) {
366                 /* Don't overwrite partition table unless forced */
367                 unsigned char *buffer = (unsigned char *) signature_page;
368                 unsigned short *q, sum;
369
370                 if (read(DEV, buffer, 512) != 512)
371                         bb_error_msg_and_die("fatal: first page unreadable");
372                 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
373                         q = (unsigned short *) (buffer + 510);
374                         for (sum = 0; q >= (unsigned short *) buffer;)
375                                 sum ^= *q--;
376                         if (!sum) {
377                                 bb_error_msg("Device '%s' contains a valid Sun disklabel.\n"
378 "This probably means creating v0 swap would destroy your partition table\n"
379 "No swap created. If you really want to create swap v0 on that device, use\n"
380 "the -f option to force it.", device_name);
381                                 return EXIT_FAILURE;
382                         }
383                 }
384         }
385 #endif
386
387         if (version == 0 || check)
388                 check_blocks();
389         if (version == 0 && !bit_test_and_clear(signature_page, 0))
390                 bb_error_msg_and_die("fatal: first page unreadable");
391         if (version == 1) {
392                 p->version = version;
393                 p->last_page = PAGES - 1;
394                 p->nr_badpages = badpages;
395         }
396
397         goodpages = PAGES - badpages - 1;
398         if (goodpages <= 0)
399                 bb_error_msg_and_die("Unable to set up swap-space: unreadable");
400         printf("Setting up swapspace version %d, size = %ld bytes\n",
401                    version, (long) (goodpages * pagesize));
402         write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
403
404         offset = ((version == 0) ? 0 : 1024);
405         if (lseek(DEV, offset, SEEK_SET) != offset)
406                 bb_error_msg_and_die("unable to rewind swap-device");
407         if (write(DEV, (char *) signature_page + offset, pagesize - offset)
408                 != pagesize - offset)
409                 bb_error_msg_and_die("unable to write signature page");
410
411         /*
412          * A subsequent swapon() will fail if the signature
413          * is not actually on disk. (This is a kernel bug.)
414          */
415         if (fsync(DEV))
416                 bb_error_msg_and_die("fsync failed");
417         return EXIT_SUCCESS;
418 }