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