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