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