blob: 601188f865b2e215a5cb5d9dde5a0537e764b88c [file] [log] [blame]
Eric Andersencc8ed391999-10-05 16:24:54 +00001/*
2 * mkswap.c - set up a linux swap device
3 *
4 * (C) 1991 Linus Torvalds. This file may be redistributed as per
5 * the Linux copyright.
6 */
7
8/*
9 * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
10 *
Eric Andersene77ae3a1999-10-19 20:03:34 +000011 * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks]
Eric Andersencc8ed391999-10-05 16:24:54 +000012 *
Eric Andersene77ae3a1999-10-19 20:03:34 +000013 * -c for readability checking. (Use it unless you are SURE!)
14 * -vN for swap areas version N. (Only N=0,1 known today.)
15 * -f for forcing swap creation even if it would smash partition table.
Eric Andersencc8ed391999-10-05 16:24:54 +000016 *
Eric Andersene77ae3a1999-10-19 20:03:34 +000017 * The device may be a block device or an image of one, but this isn't
Eric Andersencc8ed391999-10-05 16:24:54 +000018 * enforced (but it's not much fun on a character device :-).
19 *
20 * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
21 * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995.
Eric Andersene77ae3a1999-10-19 20:03:34 +000022 *
23 * Version 1 swap area code (for kernel 2.1.117), aeb, 981010.
24 *
25 * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb.
26 * V1_MAX_PAGES fixes, jj, 990325.
27 *
28 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
29 * - added Native Language Support
30 *
31 * from util-linux -- adapted for busybox by
32 * Erik Andersen <andersee@debian.org>. I ripped out Native Language
33 * Support, made some stuff smaller, and fitted for life in busybox.
34 *
Eric Andersencc8ed391999-10-05 16:24:54 +000035 */
36
Eric Andersene77ae3a1999-10-19 20:03:34 +000037#include "internal.h"
Eric Andersencc8ed391999-10-05 16:24:54 +000038#include <stdio.h>
39#include <unistd.h>
40#include <string.h>
41#include <fcntl.h>
42#include <stdlib.h>
Eric Andersene77ae3a1999-10-19 20:03:34 +000043#include <sys/ioctl.h> /* for _IO */
44#include <sys/utsname.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000045#include <sys/stat.h>
Eric Andersene77ae3a1999-10-19 20:03:34 +000046#include <asm/page.h> /* for PAGE_SIZE and PAGE_SHIFT */
47 /* we also get PAGE_SIZE via getpagesize() */
Eric Andersencc8ed391999-10-05 16:24:54 +000048
Eric Andersencc8ed391999-10-05 16:24:54 +000049
Eric Andersend73dc5b1999-11-10 23:13:02 +000050static const char mkswap_usage[] = "mkswap [-c] [-v0|-v1] device [block-count]\n\n"
Eric Andersene77ae3a1999-10-19 20:03:34 +000051"Prepare a disk partition to be used as a swap partition.\n\n"
Eric Andersend73dc5b1999-11-10 23:13:02 +000052"Options:\n"
53"\t-c\t\tCheck for read-ability.\n"
54"\t-v0\t\tMake version 0 swap [max 128 Megs].\n"
55"\t-v1\t\tMake version 1 swap [big!] (default for kernels > 2.1.117).\n"
Eric Andersene77ae3a1999-10-19 20:03:34 +000056"\tblock-count\tNumber of block to use (default is entire partition).\n";
57
58
59#ifndef _IO
60/* pre-1.3.45 */
61#define BLKGETSIZE 0x1260
62#else
63/* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */
64#define BLKGETSIZE _IO(0x12,96)
Eric Andersencc8ed391999-10-05 16:24:54 +000065#endif
66
Eric Andersene77ae3a1999-10-19 20:03:34 +000067static char * program_name = "mkswap";
68static char * device_name = NULL;
Eric Andersencc8ed391999-10-05 16:24:54 +000069static int DEV = -1;
70static long PAGES = 0;
Eric Andersene77ae3a1999-10-19 20:03:34 +000071static int check = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +000072static int badpages = 0;
Eric Andersene77ae3a1999-10-19 20:03:34 +000073static int version = -1;
Eric Andersencc8ed391999-10-05 16:24:54 +000074
Eric Andersene77ae3a1999-10-19 20:03:34 +000075#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
Eric Andersencc8ed391999-10-05 16:24:54 +000076
Eric Andersene77ae3a1999-10-19 20:03:34 +000077static int
78linux_version_code(void) {
79 struct utsname my_utsname;
80 int p, q, r;
81
82 if (uname(&my_utsname) == 0) {
83 p = atoi(strtok(my_utsname.release, "."));
84 q = atoi(strtok(NULL, "."));
85 r = atoi(strtok(NULL, "."));
86 return MAKE_VERSION(p,q,r);
87 }
88 return 0;
89}
90
91/*
92 * The definition of the union swap_header uses the constant PAGE_SIZE.
93 * Unfortunately, on some architectures this depends on the hardware model,
94 * and can only be found at run time -- we use getpagesize().
95 */
96
97static int pagesize;
98static int *signature_page;
99
100struct swap_header_v1 {
101 char bootbits[1024]; /* Space for disklabel etc. */
102 unsigned int version;
103 unsigned int last_page;
104 unsigned int nr_badpages;
105 unsigned int padding[125];
106 unsigned int badpages[1];
107} *p;
108
109static void
110init_signature_page() {
111 pagesize = getpagesize();
112
113#ifdef PAGE_SIZE
114 if (pagesize != PAGE_SIZE)
115 fprintf(stderr, "Assuming pages of size %d\n", pagesize);
116#endif
117 signature_page = (int *) malloc(pagesize);
118 memset(signature_page,0,pagesize);
119 p = (struct swap_header_v1 *) signature_page;
120}
121
122static void
123write_signature(char *sig) {
124 char *sp = (char *) signature_page;
125
126 strncpy(sp+pagesize-10, sig, 10);
127}
128
129#define V0_MAX_PAGES (8 * (pagesize - 10))
130/* Before 2.2.0pre9 */
131#define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1)
132/* Since 2.2.0pre9:
133 error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL))
134 with variations on
135 #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
136 #define SWP_OFFSET(entry) ((entry) >> 8)
137 on the various architectures. Below the result - yuk.
138
139 Machine pagesize SWP_ENTRY SWP_OFFSET bound+1 oldbound+2
140 i386 2^12 o<<8 e>>8 1<<24 1<<19
141 mips 2^12 o<<15 e>>15 1<<17 1<<19
142 alpha 2^13 o<<40 e>>40 1<<24 1<<18
143 m68k 2^12 o<<12 e>>12 1<<20 1<<19
144 sparc 2^{12,13} (o&0x3ffff)<<9 (e>>9)&0x3ffff 1<<18 1<<{19,18}
145 sparc64 2^13 o<<13 e>>13 1<<51 1<<18
146 ppc 2^12 o<<8 e>>8 1<<24 1<<19
147 armo 2^{13,14,15} o<<8 e>>8 1<<24 1<<{18,17,16}
148 armv 2^12 o<<9 e>>9 1<<23 1<<19
149
150 assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere.
151
152 The bad part is that we need to know this since the kernel will
153 refuse a swap space if it is too large.
154*/
155/* patch from jj - why does this differ from the above? */
156#if defined(__alpha__)
157#define V1_MAX_PAGES ((1 << 24) - 1)
158#elif defined(__mips__)
159#define V1_MAX_PAGES ((1 << 17) - 1)
160#elif defined(__sparc_v9__)
161#define V1_MAX_PAGES ((3 << 29) - 1)
162#elif defined(__sparc__)
163#define V1_MAX_PAGES (pagesize == 8192 ? ((3 << 29) - 1) : ((1 << 18) - 1))
164#else
165#define V1_MAX_PAGES V1_OLD_MAX_PAGES
166#endif
167/* man page now says:
168The maximum useful size of a swap area now depends on the architecture.
169It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips,
170128GB on alpha and 3TB on sparc64.
171*/
172
173#define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int))
174
175static void bit_set (unsigned int *addr, unsigned int nr)
Eric Andersencc8ed391999-10-05 16:24:54 +0000176{
177 unsigned int r, m;
178
179 addr += nr / (8 * sizeof(int));
180 r = *addr;
181 m = 1 << (nr & (8 * sizeof(int) - 1));
182 *addr = r | m;
Eric Andersencc8ed391999-10-05 16:24:54 +0000183}
184
185static int bit_test_and_clear (unsigned int *addr, unsigned int nr)
186{
187 unsigned int r, m;
188
189 addr += nr / (8 * sizeof(int));
190 r = *addr;
191 m = 1 << (nr & (8 * sizeof(int) - 1));
192 *addr = r & ~m;
193 return (r & m) != 0;
194}
195
Eric Andersene77ae3a1999-10-19 20:03:34 +0000196
197void
198die(const char *str) {
199 fprintf(stderr, "%s: %s\n", program_name, str);
200 exit( FALSE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000201}
202
Eric Andersene77ae3a1999-10-19 20:03:34 +0000203void
204page_ok(int page) {
205 if (version==0)
206 bit_set(signature_page, page);
207}
Eric Andersencc8ed391999-10-05 16:24:54 +0000208
Eric Andersene77ae3a1999-10-19 20:03:34 +0000209void
210page_bad(int page) {
211 if (version == 0)
212 bit_test_and_clear(signature_page, page);
213 else {
214 if (badpages == MAX_BADPAGES)
215 die("too many bad pages");
216 p->badpages[badpages] = page;
217 }
218 badpages++;
219}
220
221void
222check_blocks(void) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000223 unsigned int current_page;
224 int do_seek = 1;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000225 char *buffer;
Eric Andersencc8ed391999-10-05 16:24:54 +0000226
Eric Andersene77ae3a1999-10-19 20:03:34 +0000227 buffer = malloc(pagesize);
228 if (!buffer)
229 die("Out of memory");
Eric Andersencc8ed391999-10-05 16:24:54 +0000230 current_page = 0;
231 while (current_page < PAGES) {
Eric Andersene77ae3a1999-10-19 20:03:34 +0000232 if (!check) {
233 page_ok(current_page++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000234 continue;
Eric Andersencc8ed391999-10-05 16:24:54 +0000235 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000236 if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) !=
237 current_page*pagesize)
Eric Andersencc8ed391999-10-05 16:24:54 +0000238 die("seek failed in check_blocks");
Eric Andersene77ae3a1999-10-19 20:03:34 +0000239 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
240 page_bad(current_page++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000241 continue;
242 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000243 page_ok(current_page++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000244 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000245 if (badpages == 1)
246 printf("one bad page\n");
247 else if (badpages > 1)
248 printf("%d bad pages\n", badpages);
Eric Andersencc8ed391999-10-05 16:24:54 +0000249}
250
251static long valid_offset (int fd, int offset)
252{
253 char ch;
254
255 if (lseek (fd, offset, 0) < 0)
256 return 0;
257 if (read (fd, &ch, 1) < 1)
258 return 0;
259 return 1;
260}
261
Eric Andersene77ae3a1999-10-19 20:03:34 +0000262static int
263find_size (int fd)
Eric Andersencc8ed391999-10-05 16:24:54 +0000264{
Eric Andersene77ae3a1999-10-19 20:03:34 +0000265 unsigned int high, low;
Eric Andersencc8ed391999-10-05 16:24:54 +0000266
267 low = 0;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000268 for (high = 1; high > 0 && valid_offset (fd, high); high *= 2)
Eric Andersencc8ed391999-10-05 16:24:54 +0000269 low = high;
270 while (low < high - 1)
271 {
272 const int mid = (low + high) / 2;
273
274 if (valid_offset (fd, mid))
275 low = mid;
276 else
277 high = mid;
278 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000279 return (low + 1);
280}
281
Eric Andersene77ae3a1999-10-19 20:03:34 +0000282/* return size in pages, to avoid integer overflow */
283static long
284get_size(const char *file)
Eric Andersencc8ed391999-10-05 16:24:54 +0000285{
286 int fd;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000287 long size;
Eric Andersencc8ed391999-10-05 16:24:54 +0000288
Eric Andersene77ae3a1999-10-19 20:03:34 +0000289 fd = open(file, O_RDONLY);
Eric Andersencc8ed391999-10-05 16:24:54 +0000290 if (fd < 0) {
291 perror(file);
292 exit(1);
293 }
294 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
Eric Andersene77ae3a1999-10-19 20:03:34 +0000295 int sectors_per_page = pagesize/512;
296 size /= sectors_per_page;
297 } else {
298 size = find_size(fd) / pagesize;
Eric Andersencc8ed391999-10-05 16:24:54 +0000299 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000300 close(fd);
301 return size;
302}
303
Eric Andersenb0e9a701999-10-18 22:28:26 +0000304int mkswap_main(int argc, char ** argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000305{
306 char * tmp;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000307 struct stat statbuf;
308 int sz;
309 int maxpages;
310 int goodpages;
311 int offset;
312 int force = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000313
314 if (argc && *argv)
315 program_name = *argv;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000316
317 init_signature_page(); /* get pagesize */
318
319 while (argc-- > 1) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000320 argv++;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000321 if (argv[0][0] != '-') {
Eric Andersencc8ed391999-10-05 16:24:54 +0000322 if (device_name) {
Eric Andersene77ae3a1999-10-19 20:03:34 +0000323 int blocks_per_page = pagesize/1024;
324 PAGES = strtol(argv[0],&tmp,0)/blocks_per_page;
325 if (*tmp)
326 usage( mkswap_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000327 } else
328 device_name = argv[0];
Eric Andersene77ae3a1999-10-19 20:03:34 +0000329 } else {
330 switch (argv[0][1]) {
331 case 'c':
332 check=1;
333 break;
334 case 'f':
335 force=1;
336 break;
337 case 'v':
338 version=atoi(argv[0]+2);
339 break;
340 default:
341 usage( mkswap_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000342 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000343 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000344 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000345 if (!device_name) {
346 fprintf(stderr,
347 "%s: error: Nowhere to set up swap on?\n",
348 program_name);
349 usage( mkswap_usage);
350 }
351 sz = get_size(device_name);
352 if (!PAGES) {
353 PAGES = sz;
354 } else if (PAGES > sz && !force) {
355 fprintf(stderr,
356 "%s: error: "
357 "size %ld is larger than device size %d\n",
358 program_name,
359 PAGES*(pagesize/1024), sz*(pagesize/1024));
360 exit( FALSE);
361 }
362
363 if (version == -1) {
364 if (PAGES <= V0_MAX_PAGES)
365 version = 0;
366 else if (linux_version_code() < MAKE_VERSION(2,1,117))
367 version = 0;
368 else if (pagesize < 2048)
369 version = 0;
370 else
371 version = 1;
372 }
373 if (version != 0 && version != 1) {
374 fprintf(stderr, "%s: error: unknown version %d\n",
375 program_name, version);
376 usage( mkswap_usage);
377 }
378 if (PAGES < 10) {
379 fprintf(stderr,
380 "%s: error: swap area needs to be at least %ldkB\n",
381 program_name, (long)(10 * pagesize / 1024));
382 usage( mkswap_usage);
383 }
384#if 0
385 maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
386#else
387 if (!version)
388 maxpages = V0_MAX_PAGES;
389 else if (linux_version_code() >= MAKE_VERSION(2,2,1))
390 maxpages = V1_MAX_PAGES;
391 else {
392 maxpages = V1_OLD_MAX_PAGES;
393 if (maxpages > V1_MAX_PAGES)
394 maxpages = V1_MAX_PAGES;
395 }
396#endif
397 if (PAGES > maxpages) {
398 PAGES = maxpages;
399 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n",
400 program_name, PAGES * pagesize / 1024);
401 }
402
403 DEV = open(device_name,O_RDWR);
404 if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
405 perror(device_name);
406 exit( FALSE);
407 }
408 if (!S_ISBLK(statbuf.st_mode))
409 check=0;
410 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
411 die("Will not try to make swapdevice on '%s'");
412
413#ifdef __sparc__
414 if (!force && version == 0) {
415 /* Don't overwrite partition table unless forced */
416 unsigned char *buffer = (unsigned char *)signature_page;
417 unsigned short *q, sum;
418
419 if (read(DEV, buffer, 512) != 512)
420 die("fatal: first page unreadable");
421 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
422 q = (unsigned short *)(buffer + 510);
423 for (sum = 0; q >= (unsigned short *) buffer;)
424 sum ^= *q--;
425 if (!sum) {
426 fprintf(stderr, "\
427%s: Device '%s' contains a valid Sun disklabel.\n\
428This probably means creating v0 swap would destroy your partition table\n\
429No swap created. If you really want to create swap v0 on that device, use\n\
430the -f option to force it.\n",
431 program_name, device_name);
432 exit( FALSE);
433 }
434 }
435 }
436#endif
437
438 if (version == 0 || check)
439 check_blocks();
440 if (version == 0 && !bit_test_and_clear(signature_page,0))
441 die("fatal: first page unreadable");
442 if (version == 1) {
443 p->version = version;
444 p->last_page = PAGES-1;
445 p->nr_badpages = badpages;
446 }
447
448 goodpages = PAGES - badpages - 1;
449 if (goodpages <= 0)
450 die("Unable to set up swap-space: unreadable");
451 printf("Setting up swapspace version %d, size = %ld bytes\n",
452 version, (long)(goodpages*pagesize));
453 write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
454
455 offset = ((version == 0) ? 0 : 1024);
456 if (lseek(DEV, offset, SEEK_SET) != offset)
457 die("unable to rewind swap-device");
458 if (write(DEV,(char*)signature_page+offset, pagesize-offset)
459 != pagesize-offset)
460 die("unable to write signature page");
461
462 /*
463 * A subsequent swapon() will fail if the signature
464 * is not actually on disk. (This is a kernel bug.)
465 */
466 if (fsync(DEV))
467 die("fsync failed");
468 exit ( TRUE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000469}