blob: fceeb28dda7170ec070d47551c0fc5e8e1d58984 [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 Andersene77ae3a1999-10-19 20:03:34 +000050static const char mkswap_usage[] = "mkswap [-c] [-v0|-v1] device [block-count]\n"
51"Prepare a disk partition to be used as a swap partition.\n\n"
52"\t-c\tCheck for read-ability.\n"
53"\t-v0\tMake version 0 swap [max 128 Megs].\n"
54"\t-v1\tMake version 1 swap [big!] (default for kernels > 2.1.117).\n"
55"\tblock-count\tNumber of block to use (default is entire partition).\n";
56
57
58#ifndef _IO
59/* pre-1.3.45 */
60#define BLKGETSIZE 0x1260
61#else
62/* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */
63#define BLKGETSIZE _IO(0x12,96)
Eric Andersencc8ed391999-10-05 16:24:54 +000064#endif
65
Eric Andersene77ae3a1999-10-19 20:03:34 +000066static char * program_name = "mkswap";
67static char * device_name = NULL;
Eric Andersencc8ed391999-10-05 16:24:54 +000068static int DEV = -1;
69static long PAGES = 0;
Eric Andersene77ae3a1999-10-19 20:03:34 +000070static int check = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +000071static int badpages = 0;
Eric Andersene77ae3a1999-10-19 20:03:34 +000072static int version = -1;
Eric Andersencc8ed391999-10-05 16:24:54 +000073
Eric Andersene77ae3a1999-10-19 20:03:34 +000074#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
Eric Andersencc8ed391999-10-05 16:24:54 +000075
Eric Andersene77ae3a1999-10-19 20:03:34 +000076static int
77linux_version_code(void) {
78 struct utsname my_utsname;
79 int p, q, r;
80
81 if (uname(&my_utsname) == 0) {
82 p = atoi(strtok(my_utsname.release, "."));
83 q = atoi(strtok(NULL, "."));
84 r = atoi(strtok(NULL, "."));
85 return MAKE_VERSION(p,q,r);
86 }
87 return 0;
88}
89
90/*
91 * The definition of the union swap_header uses the constant PAGE_SIZE.
92 * Unfortunately, on some architectures this depends on the hardware model,
93 * and can only be found at run time -- we use getpagesize().
94 */
95
96static int pagesize;
97static int *signature_page;
98
99struct swap_header_v1 {
100 char bootbits[1024]; /* Space for disklabel etc. */
101 unsigned int version;
102 unsigned int last_page;
103 unsigned int nr_badpages;
104 unsigned int padding[125];
105 unsigned int badpages[1];
106} *p;
107
108static void
109init_signature_page() {
110 pagesize = getpagesize();
111
112#ifdef PAGE_SIZE
113 if (pagesize != PAGE_SIZE)
114 fprintf(stderr, "Assuming pages of size %d\n", pagesize);
115#endif
116 signature_page = (int *) malloc(pagesize);
117 memset(signature_page,0,pagesize);
118 p = (struct swap_header_v1 *) signature_page;
119}
120
121static void
122write_signature(char *sig) {
123 char *sp = (char *) signature_page;
124
125 strncpy(sp+pagesize-10, sig, 10);
126}
127
128#define V0_MAX_PAGES (8 * (pagesize - 10))
129/* Before 2.2.0pre9 */
130#define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1)
131/* Since 2.2.0pre9:
132 error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL))
133 with variations on
134 #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
135 #define SWP_OFFSET(entry) ((entry) >> 8)
136 on the various architectures. Below the result - yuk.
137
138 Machine pagesize SWP_ENTRY SWP_OFFSET bound+1 oldbound+2
139 i386 2^12 o<<8 e>>8 1<<24 1<<19
140 mips 2^12 o<<15 e>>15 1<<17 1<<19
141 alpha 2^13 o<<40 e>>40 1<<24 1<<18
142 m68k 2^12 o<<12 e>>12 1<<20 1<<19
143 sparc 2^{12,13} (o&0x3ffff)<<9 (e>>9)&0x3ffff 1<<18 1<<{19,18}
144 sparc64 2^13 o<<13 e>>13 1<<51 1<<18
145 ppc 2^12 o<<8 e>>8 1<<24 1<<19
146 armo 2^{13,14,15} o<<8 e>>8 1<<24 1<<{18,17,16}
147 armv 2^12 o<<9 e>>9 1<<23 1<<19
148
149 assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere.
150
151 The bad part is that we need to know this since the kernel will
152 refuse a swap space if it is too large.
153*/
154/* patch from jj - why does this differ from the above? */
155#if defined(__alpha__)
156#define V1_MAX_PAGES ((1 << 24) - 1)
157#elif defined(__mips__)
158#define V1_MAX_PAGES ((1 << 17) - 1)
159#elif defined(__sparc_v9__)
160#define V1_MAX_PAGES ((3 << 29) - 1)
161#elif defined(__sparc__)
162#define V1_MAX_PAGES (pagesize == 8192 ? ((3 << 29) - 1) : ((1 << 18) - 1))
163#else
164#define V1_MAX_PAGES V1_OLD_MAX_PAGES
165#endif
166/* man page now says:
167The maximum useful size of a swap area now depends on the architecture.
168It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips,
169128GB on alpha and 3TB on sparc64.
170*/
171
172#define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int))
173
174static void bit_set (unsigned int *addr, unsigned int nr)
Eric Andersencc8ed391999-10-05 16:24:54 +0000175{
176 unsigned int r, m;
177
178 addr += nr / (8 * sizeof(int));
179 r = *addr;
180 m = 1 << (nr & (8 * sizeof(int) - 1));
181 *addr = r | m;
Eric Andersencc8ed391999-10-05 16:24:54 +0000182}
183
184static int bit_test_and_clear (unsigned int *addr, unsigned int nr)
185{
186 unsigned int r, m;
187
188 addr += nr / (8 * sizeof(int));
189 r = *addr;
190 m = 1 << (nr & (8 * sizeof(int) - 1));
191 *addr = r & ~m;
192 return (r & m) != 0;
193}
194
Eric Andersene77ae3a1999-10-19 20:03:34 +0000195
196void
197die(const char *str) {
198 fprintf(stderr, "%s: %s\n", program_name, str);
199 exit( FALSE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000200}
201
Eric Andersene77ae3a1999-10-19 20:03:34 +0000202void
203page_ok(int page) {
204 if (version==0)
205 bit_set(signature_page, page);
206}
Eric Andersencc8ed391999-10-05 16:24:54 +0000207
Eric Andersene77ae3a1999-10-19 20:03:34 +0000208void
209page_bad(int page) {
210 if (version == 0)
211 bit_test_and_clear(signature_page, page);
212 else {
213 if (badpages == MAX_BADPAGES)
214 die("too many bad pages");
215 p->badpages[badpages] = page;
216 }
217 badpages++;
218}
219
220void
221check_blocks(void) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000222 unsigned int current_page;
223 int do_seek = 1;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000224 char *buffer;
Eric Andersencc8ed391999-10-05 16:24:54 +0000225
Eric Andersene77ae3a1999-10-19 20:03:34 +0000226 buffer = malloc(pagesize);
227 if (!buffer)
228 die("Out of memory");
Eric Andersencc8ed391999-10-05 16:24:54 +0000229 current_page = 0;
230 while (current_page < PAGES) {
Eric Andersene77ae3a1999-10-19 20:03:34 +0000231 if (!check) {
232 page_ok(current_page++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000233 continue;
Eric Andersencc8ed391999-10-05 16:24:54 +0000234 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000235 if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) !=
236 current_page*pagesize)
Eric Andersencc8ed391999-10-05 16:24:54 +0000237 die("seek failed in check_blocks");
Eric Andersene77ae3a1999-10-19 20:03:34 +0000238 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
239 page_bad(current_page++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000240 continue;
241 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000242 page_ok(current_page++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000243 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000244 if (badpages == 1)
245 printf("one bad page\n");
246 else if (badpages > 1)
247 printf("%d bad pages\n", badpages);
Eric Andersencc8ed391999-10-05 16:24:54 +0000248}
249
250static long valid_offset (int fd, int offset)
251{
252 char ch;
253
254 if (lseek (fd, offset, 0) < 0)
255 return 0;
256 if (read (fd, &ch, 1) < 1)
257 return 0;
258 return 1;
259}
260
Eric Andersene77ae3a1999-10-19 20:03:34 +0000261static int
262find_size (int fd)
Eric Andersencc8ed391999-10-05 16:24:54 +0000263{
Eric Andersene77ae3a1999-10-19 20:03:34 +0000264 unsigned int high, low;
Eric Andersencc8ed391999-10-05 16:24:54 +0000265
266 low = 0;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000267 for (high = 1; high > 0 && valid_offset (fd, high); high *= 2)
Eric Andersencc8ed391999-10-05 16:24:54 +0000268 low = high;
269 while (low < high - 1)
270 {
271 const int mid = (low + high) / 2;
272
273 if (valid_offset (fd, mid))
274 low = mid;
275 else
276 high = mid;
277 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000278 return (low + 1);
279}
280
Eric Andersene77ae3a1999-10-19 20:03:34 +0000281/* return size in pages, to avoid integer overflow */
282static long
283get_size(const char *file)
Eric Andersencc8ed391999-10-05 16:24:54 +0000284{
285 int fd;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000286 long size;
Eric Andersencc8ed391999-10-05 16:24:54 +0000287
Eric Andersene77ae3a1999-10-19 20:03:34 +0000288 fd = open(file, O_RDONLY);
Eric Andersencc8ed391999-10-05 16:24:54 +0000289 if (fd < 0) {
290 perror(file);
291 exit(1);
292 }
293 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
Eric Andersene77ae3a1999-10-19 20:03:34 +0000294 int sectors_per_page = pagesize/512;
295 size /= sectors_per_page;
296 } else {
297 size = find_size(fd) / pagesize;
Eric Andersencc8ed391999-10-05 16:24:54 +0000298 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000299 close(fd);
300 return size;
301}
302
Eric Andersenb0e9a701999-10-18 22:28:26 +0000303int mkswap_main(int argc, char ** argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000304{
305 char * tmp;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000306 struct stat statbuf;
307 int sz;
308 int maxpages;
309 int goodpages;
310 int offset;
311 int force = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000312
313 if (argc && *argv)
314 program_name = *argv;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000315
316 init_signature_page(); /* get pagesize */
317
318 while (argc-- > 1) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000319 argv++;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000320 if (argv[0][0] != '-') {
Eric Andersencc8ed391999-10-05 16:24:54 +0000321 if (device_name) {
Eric Andersene77ae3a1999-10-19 20:03:34 +0000322 int blocks_per_page = pagesize/1024;
323 PAGES = strtol(argv[0],&tmp,0)/blocks_per_page;
324 if (*tmp)
325 usage( mkswap_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000326 } else
327 device_name = argv[0];
Eric Andersene77ae3a1999-10-19 20:03:34 +0000328 } else {
329 switch (argv[0][1]) {
330 case 'c':
331 check=1;
332 break;
333 case 'f':
334 force=1;
335 break;
336 case 'v':
337 version=atoi(argv[0]+2);
338 break;
339 default:
340 usage( mkswap_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000341 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000342 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000343 }
Eric Andersene77ae3a1999-10-19 20:03:34 +0000344 if (!device_name) {
345 fprintf(stderr,
346 "%s: error: Nowhere to set up swap on?\n",
347 program_name);
348 usage( mkswap_usage);
349 }
350 sz = get_size(device_name);
351 if (!PAGES) {
352 PAGES = sz;
353 } else if (PAGES > sz && !force) {
354 fprintf(stderr,
355 "%s: error: "
356 "size %ld is larger than device size %d\n",
357 program_name,
358 PAGES*(pagesize/1024), sz*(pagesize/1024));
359 exit( FALSE);
360 }
361
362 if (version == -1) {
363 if (PAGES <= V0_MAX_PAGES)
364 version = 0;
365 else if (linux_version_code() < MAKE_VERSION(2,1,117))
366 version = 0;
367 else if (pagesize < 2048)
368 version = 0;
369 else
370 version = 1;
371 }
372 if (version != 0 && version != 1) {
373 fprintf(stderr, "%s: error: unknown version %d\n",
374 program_name, version);
375 usage( mkswap_usage);
376 }
377 if (PAGES < 10) {
378 fprintf(stderr,
379 "%s: error: swap area needs to be at least %ldkB\n",
380 program_name, (long)(10 * pagesize / 1024));
381 usage( mkswap_usage);
382 }
383#if 0
384 maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
385#else
386 if (!version)
387 maxpages = V0_MAX_PAGES;
388 else if (linux_version_code() >= MAKE_VERSION(2,2,1))
389 maxpages = V1_MAX_PAGES;
390 else {
391 maxpages = V1_OLD_MAX_PAGES;
392 if (maxpages > V1_MAX_PAGES)
393 maxpages = V1_MAX_PAGES;
394 }
395#endif
396 if (PAGES > maxpages) {
397 PAGES = maxpages;
398 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n",
399 program_name, PAGES * pagesize / 1024);
400 }
401
402 DEV = open(device_name,O_RDWR);
403 if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
404 perror(device_name);
405 exit( FALSE);
406 }
407 if (!S_ISBLK(statbuf.st_mode))
408 check=0;
409 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
410 die("Will not try to make swapdevice on '%s'");
411
412#ifdef __sparc__
413 if (!force && version == 0) {
414 /* Don't overwrite partition table unless forced */
415 unsigned char *buffer = (unsigned char *)signature_page;
416 unsigned short *q, sum;
417
418 if (read(DEV, buffer, 512) != 512)
419 die("fatal: first page unreadable");
420 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
421 q = (unsigned short *)(buffer + 510);
422 for (sum = 0; q >= (unsigned short *) buffer;)
423 sum ^= *q--;
424 if (!sum) {
425 fprintf(stderr, "\
426%s: Device '%s' contains a valid Sun disklabel.\n\
427This probably means creating v0 swap would destroy your partition table\n\
428No swap created. If you really want to create swap v0 on that device, use\n\
429the -f option to force it.\n",
430 program_name, device_name);
431 exit( FALSE);
432 }
433 }
434 }
435#endif
436
437 if (version == 0 || check)
438 check_blocks();
439 if (version == 0 && !bit_test_and_clear(signature_page,0))
440 die("fatal: first page unreadable");
441 if (version == 1) {
442 p->version = version;
443 p->last_page = PAGES-1;
444 p->nr_badpages = badpages;
445 }
446
447 goodpages = PAGES - badpages - 1;
448 if (goodpages <= 0)
449 die("Unable to set up swap-space: unreadable");
450 printf("Setting up swapspace version %d, size = %ld bytes\n",
451 version, (long)(goodpages*pagesize));
452 write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
453
454 offset = ((version == 0) ? 0 : 1024);
455 if (lseek(DEV, offset, SEEK_SET) != offset)
456 die("unable to rewind swap-device");
457 if (write(DEV,(char*)signature_page+offset, pagesize-offset)
458 != pagesize-offset)
459 die("unable to write signature page");
460
461 /*
462 * A subsequent swapon() will fail if the signature
463 * is not actually on disk. (This is a kernel bug.)
464 */
465 if (fsync(DEV))
466 die("fsync failed");
467 exit ( TRUE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000468}