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