ce123e50236e34b197ee7fd26a5716308a4f06f0
[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         if ((fd = open(file, O_RDONLY)) < 0) /* TODO: bb_xopen3 */
262                 bb_perror_msg_and_die("%s", file);
263         if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
264                 size /= pagesize / 512;
265         } else {
266                 size = find_size(fd) / pagesize;
267         }
268         close(fd);
269         return size;
270 }
271
272 int mkswap_main(int argc, char **argv)
273 {
274         char *tmp;
275         struct stat statbuf;
276         int sz;
277         int maxpages;
278         int goodpages;
279 #ifdef __sparc__
280         int force = 0;
281 #endif
282
283         init_signature_page();          /* get pagesize */
284
285         bb_opt_complementally = "?"; /* call bb_show_usage internally */
286         sz = bb_getopt_ulflags(argc, argv, "+cfv:", &tmp);
287         if (sz & 1)
288                 check = 1;
289 #ifdef __sparc__
290         if (sz & 2)
291                 force = 1;
292 #endif
293 #if ENABLE_FEATURE_MKSWAP_V0
294         if (sz & 4) {
295                 version = bb_xgetlarg(tmp, 10, 0, 1);
296         } else {
297                 if (get_kernel_revision() < MAKE_VERSION(2, 1, 117))
298                         version = 0;
299                 else
300                         version = 1;
301         }
302 #endif
303
304         argv += optind;
305         argc -= optind;
306
307         goodpages = pagesize / 1024; /* cache division */
308         while (argc--) {
309                 if (device_name) {
310                         PAGES = bb_xgetlarg(argv[0], 0, 10, sz * goodpages) / goodpages;
311                         argc = 0; /* ignore any surplus args.. */
312                 } else {
313                         device_name = argv[0];
314                         sz = get_size(device_name);
315                         argv++;
316                 }
317         }
318
319         if (!device_name) {
320                 bb_error_msg_and_die("error: Nowhere to set up swap on?");
321         }
322         if (!PAGES) {
323                 PAGES = sz;
324         }
325
326 #if 0
327         maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
328 #else
329         if (!version)
330                 maxpages = V0_MAX_PAGES;
331         else if (get_kernel_revision() >= MAKE_VERSION(2, 2, 1))
332                 maxpages = V1_MAX_PAGES;
333         else {
334                 maxpages = V1_OLD_MAX_PAGES;
335                 if (maxpages > V1_MAX_PAGES)
336                         maxpages = V1_MAX_PAGES;
337         }
338 #endif
339         if (PAGES > maxpages) {
340                 PAGES = maxpages;
341                 bb_error_msg("warning: truncating swap area to %ldkB",
342                                 PAGES * goodpages);
343         }
344
345         DEV = open(device_name, O_RDWR);
346         if (DEV < 0 || fstat(DEV, &statbuf) < 0)
347                 bb_perror_msg_and_die("%s", device_name);
348         if (!S_ISBLK(statbuf.st_mode))
349                 check = 0;
350         else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
351                 bb_error_msg_and_die("Will not try to make swapdevice on '%s'", device_name);
352
353 #ifdef __sparc__
354         if (!force && version == 0) {
355                 /* Don't overwrite partition table unless forced */
356                 unsigned char *buffer = (unsigned char *) signature_page;
357                 unsigned short *q, sum;
358
359                 if (read(DEV, buffer, 512) != 512)
360                         bb_error_msg_and_die("fatal: first page unreadable");
361                 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
362                         q = (unsigned short *) (buffer + 510);
363                         for (sum = 0; q >= (unsigned short *) buffer;)
364                                 sum ^= *q--;
365                         if (!sum) {
366                                 bb_error_msg("Device '%s' contains a valid Sun disklabel.\n"
367 "This probably means creating v0 swap would destroy your partition table\n"
368 "No swap created. If you really want to create swap v0 on that device, use\n"
369 "the -f option to force it.", device_name);
370                                 return EXIT_FAILURE;
371                         }
372                 }
373         }
374 #endif
375
376         if (version == 0 || check)
377                 check_blocks();
378         if (version == 0 && !bit_test_and_clear(signature_page, 0))
379                 bb_error_msg_and_die("fatal: first page unreadable");
380         if (version == 1) {
381                 p->swap_version = version;
382                 p->last_page = PAGES - 1;
383                 p->nr_badpages = badpages;
384         }
385
386         goodpages = PAGES - badpages - 1;
387         if (goodpages <= 0)
388                 bb_error_msg_and_die("Unable to set up swap-space: unreadable");
389         printf("Setting up swapspace version %d, size = %ld bytes\n",
390                    version, (long) (goodpages * pagesize));
391         write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
392
393         sz = ((version == 0) ? 0 : 1024); /* offset */
394         if (lseek(DEV, sz, SEEK_SET) != sz)
395                 bb_error_msg_and_die("unable to rewind swap-device");
396         goodpages = pagesize - sz; /* cache substraction */
397         if (write(DEV, (char *) signature_page + sz, goodpages)
398                 != goodpages)
399                 bb_error_msg_and_die("unable to write signature page");
400
401         /*
402          * A subsequent swapon() will fail if the signature
403          * is not actually on disk. (This is a kernel bug.)
404          */
405         if (fsync(DEV))
406                 bb_error_msg_and_die("fsync failed");
407         if (ENABLE_FEATURE_CLEAN_UP) {
408                 close(DEV);
409                 free(signature_page);
410         }
411         return EXIT_SUCCESS;
412 }