Latest and greatest. Some effort at libc5 (aiming towards newlib)
[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 <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 *) xmalloc(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 = xmalloc(pagesize);
234         current_page = 0;
235         while (current_page < PAGES) {
236                 if (!check) {
237                         page_ok(current_page++);
238                         continue;
239                 }
240                 if (do_seek && lseek(DEV, current_page * pagesize, SEEK_SET) !=
241                         current_page * pagesize)
242                         die("seek failed in check_blocks");
243                 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
244                         page_bad(current_page++);
245                         continue;
246                 }
247                 page_ok(current_page++);
248         }
249         if (badpages == 1)
250                 printf("one bad page\n");
251         else if (badpages > 1)
252                 printf("%d bad pages\n", badpages);
253 }
254
255 static long valid_offset(int fd, int offset)
256 {
257         char ch;
258
259         if (lseek(fd, offset, 0) < 0)
260                 return 0;
261         if (read(fd, &ch, 1) < 1)
262                 return 0;
263         return 1;
264 }
265
266 static int find_size(int fd)
267 {
268         unsigned int high, low;
269
270         low = 0;
271         for (high = 1; high > 0 && valid_offset(fd, high); high *= 2)
272                 low = high;
273         while (low < high - 1) {
274                 const int mid = (low + high) / 2;
275
276                 if (valid_offset(fd, mid))
277                         low = mid;
278                 else
279                         high = mid;
280         }
281         return (low + 1);
282 }
283
284 /* return size in pages, to avoid integer overflow */
285 static long get_size(const char *file)
286 {
287         int fd;
288         long size;
289
290         fd = open(file, O_RDONLY);
291         if (fd < 0) {
292                 perror(file);
293                 exit(1);
294         }
295         if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
296                 int sectors_per_page = pagesize / 512;
297
298                 size /= sectors_per_page;
299         } else {
300                 size = find_size(fd) / pagesize;
301         }
302         close(fd);
303         return size;
304 }
305
306 int mkswap_main(int argc, char **argv)
307 {
308         char *tmp;
309         struct stat statbuf;
310         int sz;
311         int maxpages;
312         int goodpages;
313         int offset;
314         int force = 0;
315
316         if (argc && *argv)
317                 program_name = *argv;
318
319         init_signature_page();          /* get pagesize */
320
321         while (argc-- > 1) {
322                 argv++;
323                 if (argv[0][0] != '-') {
324                         if (device_name) {
325                                 int blocks_per_page = pagesize / 1024;
326
327                                 PAGES = strtol(argv[0], &tmp, 0) / blocks_per_page;
328                                 if (*tmp)
329                                         usage(mkswap_usage);
330                         } else
331                                 device_name = argv[0];
332                 } else {
333                         switch (argv[0][1]) {
334                         case 'c':
335                                 check = 1;
336                                 break;
337                         case 'f':
338                                 force = 1;
339                                 break;
340                         case 'v':
341                                 version = atoi(argv[0] + 2);
342                                 break;
343                         default:
344                                 usage(mkswap_usage);
345                         }
346                 }
347         }
348         if (!device_name) {
349                 fprintf(stderr,
350                                 "%s: error: Nowhere to set up swap on?\n", program_name);
351                 usage(mkswap_usage);
352         }
353         sz = get_size(device_name);
354         if (!PAGES) {
355                 PAGES = sz;
356         } else if (PAGES > sz && !force) {
357                 fprintf(stderr,
358                                 "%s: error: "
359                                 "size %ld is larger than device size %d\n",
360                                 program_name,
361                                 PAGES * (pagesize / 1024), sz * (pagesize / 1024));
362                 exit(FALSE);
363         }
364
365         if (version == -1) {
366                 if (PAGES <= V0_MAX_PAGES)
367                         version = 0;
368                 else if (linux_version_code() < MAKE_VERSION(2, 1, 117))
369                         version = 0;
370                 else if (pagesize < 2048)
371                         version = 0;
372                 else
373                         version = 1;
374         }
375         if (version != 0 && version != 1) {
376                 fprintf(stderr, "%s: error: unknown version %d\n",
377                                 program_name, version);
378                 usage(mkswap_usage);
379         }
380         if (PAGES < 10) {
381                 fprintf(stderr,
382                                 "%s: error: swap area needs to be at least %ldkB\n",
383                                 program_name, (long) (10 * pagesize / 1024));
384                 usage(mkswap_usage);
385         }
386 #if 0
387         maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
388 #else
389         if (!version)
390                 maxpages = V0_MAX_PAGES;
391         else if (linux_version_code() >= MAKE_VERSION(2, 2, 1))
392                 maxpages = V1_MAX_PAGES;
393         else {
394                 maxpages = V1_OLD_MAX_PAGES;
395                 if (maxpages > V1_MAX_PAGES)
396                         maxpages = V1_MAX_PAGES;
397         }
398 #endif
399         if (PAGES > maxpages) {
400                 PAGES = maxpages;
401                 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n",
402                                 program_name, PAGES * pagesize / 1024);
403         }
404
405         DEV = open(device_name, O_RDWR);
406         if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
407                 perror(device_name);
408                 exit(FALSE);
409         }
410         if (!S_ISBLK(statbuf.st_mode))
411                 check = 0;
412         else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
413                 die("Will not try to make swapdevice on '%s'");
414
415 #ifdef __sparc__
416         if (!force && version == 0) {
417                 /* Don't overwrite partition table unless forced */
418                 unsigned char *buffer = (unsigned char *) signature_page;
419                 unsigned short *q, sum;
420
421                 if (read(DEV, buffer, 512) != 512)
422                         die("fatal: first page unreadable");
423                 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
424                         q = (unsigned short *) (buffer + 510);
425                         for (sum = 0; q >= (unsigned short *) buffer;)
426                                 sum ^= *q--;
427                         if (!sum) {
428                                 fprintf(stderr, "\
429 %s: Device '%s' contains a valid Sun disklabel.\n\
430 This probably means creating v0 swap would destroy your partition table\n\
431 No swap created. If you really want to create swap v0 on that device, use\n\
432 the -f option to force it.\n", program_name, device_name);
433                                 exit(FALSE);
434                         }
435                 }
436         }
437 #endif
438
439         if (version == 0 || check)
440                 check_blocks();
441         if (version == 0 && !bit_test_and_clear(signature_page, 0))
442                 die("fatal: first page unreadable");
443         if (version == 1) {
444                 p->version = version;
445                 p->last_page = PAGES - 1;
446                 p->nr_badpages = badpages;
447         }
448
449         goodpages = PAGES - badpages - 1;
450         if (goodpages <= 0)
451                 die("Unable to set up swap-space: unreadable");
452         printf("Setting up swapspace version %d, size = %ld bytes\n",
453                    version, (long) (goodpages * pagesize));
454         write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
455
456         offset = ((version == 0) ? 0 : 1024);
457         if (lseek(DEV, offset, SEEK_SET) != offset)
458                 die("unable to rewind swap-device");
459         if (write(DEV, (char *) signature_page + offset, pagesize - offset)
460                 != pagesize - offset)
461                 die("unable to write signature page");
462
463         /*
464          * A subsequent swapon() will fail if the signature
465          * is not actually on disk. (This is a kernel bug.)
466          */
467         if (fsync(DEV))
468                 die("fsync failed");
469         exit(TRUE);
470 }