aboutsummaryrefslogtreecommitdiff
path: root/mkswap.c
diff options
context:
space:
mode:
Diffstat (limited to 'mkswap.c')
-rw-r--r--mkswap.c448
1 files changed, 332 insertions, 116 deletions
diff --git a/mkswap.c b/mkswap.c
index 8c0c4dec7..fceeb28dd 100644
--- a/mkswap.c
+++ b/mkswap.c
@@ -1,4 +1,3 @@
1#include "internal.h"
2/* 1/*
3 * mkswap.c - set up a linux swap device 2 * mkswap.c - set up a linux swap device
4 * 3 *
@@ -9,51 +8,170 @@
9/* 8/*
10 * 20.12.91 - time began. Got VM working yesterday by doing this by hand. 9 * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
11 * 10 *
12 * Usage: mkswap [-c] device [size-in-blocks] 11 * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks]
13 * 12 *
14 * -c for readablility checking (use it unless you are SURE!) 13 * -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.
15 * 16 *
16 * The device may be a block device or a image of one, but this isn't 17 * The device may be a block device or an image of one, but this isn't
17 * enforced (but it's not much fun on a character device :-). 18 * enforced (but it's not much fun on a character device :-).
18 * 19 *
19 * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the 20 * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
20 * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995. 21 * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995.
22 *
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 *
21 */ 35 */
22 36
37#include "internal.h"
23#include <stdio.h> 38#include <stdio.h>
24#include <unistd.h> 39#include <unistd.h>
25#include <string.h> 40#include <string.h>
26#include <fcntl.h> 41#include <fcntl.h>
27#include <stdlib.h> 42#include <stdlib.h>
43#include <sys/ioctl.h> /* for _IO */
44#include <sys/utsname.h>
28#include <sys/stat.h> 45#include <sys/stat.h>
29#include <sys/ioctl.h> 46#include <asm/page.h> /* for PAGE_SIZE and PAGE_SHIFT */
47 /* we also get PAGE_SIZE via getpagesize() */
30 48
31#include <asm/page.h>
32#include <linux/fs.h>
33 49
34#ifndef __linux__ 50static const char mkswap_usage[] = "mkswap [-c] [-v0|-v1] device [block-count]\n"
35# define volatile 51"Prepare a disk partition to be used as a swap partition.\n\n"
36#endif 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";
37 56
38#define TEST_BUFFER_PAGES 8
39 57
40const char mkswap_usage[] = "mkswap [-c] partition [block-count]\n" 58#ifndef _IO
41"\n" 59/* pre-1.3.45 */
42"\tPrepare a disk partition to be used as a swap partition.\n" 60#define BLKGETSIZE 0x1260
43"\tThe default block count is the size of the entire partition.\n" 61#else
44"\n" 62/* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */
45"\t-c:\tCheck for read-ability.\n" 63#define BLKGETSIZE _IO(0x12,96)
46"\tblock-count\tUse only this many blocks.\n"; 64#endif
47 65
48static const char * program_name = "mkswap"; 66static char * program_name = "mkswap";
49static const char * device_name = NULL; 67static char * device_name = NULL;
50static int DEV = -1; 68static int DEV = -1;
51static long PAGES = 0; 69static long PAGES = 0;
52static int do_check = 0; 70static int check = 0;
53static int badpages = 0; 71static int badpages = 0;
72static int version = -1;
73
74#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
75
76static 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}
54 120
121static void
122write_signature(char *sig) {
123 char *sp = (char *) signature_page;
55 124
56static long bit_test_and_set (unsigned int *addr, unsigned int nr) 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)
57{ 175{
58 unsigned int r, m; 176 unsigned int r, m;
59 177
@@ -61,7 +179,6 @@ static long bit_test_and_set (unsigned int *addr, unsigned int nr)
61 r = *addr; 179 r = *addr;
62 m = 1 << (nr & (8 * sizeof(int) - 1)); 180 m = 1 << (nr & (8 * sizeof(int) - 1));
63 *addr = r | m; 181 *addr = r | m;
64 return (r & m) != 0;
65} 182}
66 183
67static int bit_test_and_clear (unsigned int *addr, unsigned int nr) 184static int bit_test_and_clear (unsigned int *addr, unsigned int nr)
@@ -75,47 +192,59 @@ static int bit_test_and_clear (unsigned int *addr, unsigned int nr)
75 return (r & m) != 0; 192 return (r & m) != 0;
76} 193}
77 194
78/* 195
79 * Volatile to let gcc know that this doesn't return. When trying 196void
80 * to compile this under minix, volatile gives a warning, as 197die(const char *str) {
81 * exit() isn't defined as volatile under minix. 198 fprintf(stderr, "%s: %s\n", program_name, str);
82 */ 199 exit( FALSE);
83volatile void fatal_error(const char * fmt_string)
84{
85 fprintf(stderr,fmt_string,program_name,device_name);
86 exit(FALSE);
87} 200}
88 201
89#define die(str) fatal_error("%s: " str "\n") 202void
203page_ok(int page) {
204 if (version==0)
205 bit_set(signature_page, page);
206}
90 207
91static void check_blocks(int * signature_page) 208void
92{ 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) {
93 unsigned int current_page; 222 unsigned int current_page;
94 int do_seek = 1; 223 int do_seek = 1;
95 char buffer[PAGE_SIZE]; 224 char *buffer;
96 225
226 buffer = malloc(pagesize);
227 if (!buffer)
228 die("Out of memory");
97 current_page = 0; 229 current_page = 0;
98 while (current_page < PAGES) { 230 while (current_page < PAGES) {
99 if (!do_check) { 231 if (!check) {
100 bit_test_and_set(signature_page,current_page++); 232 page_ok(current_page++);
101 continue; 233 continue;
102 } else {
103 printf("\r%d", current_page);
104 } 234 }
105 if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) != 235 if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) !=
106 current_page*PAGE_SIZE) 236 current_page*pagesize)
107 die("seek failed in check_blocks"); 237 die("seek failed in check_blocks");
108 if ( (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) ) { 238 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
109 bit_test_and_clear(signature_page,current_page++); 239 page_bad(current_page++);
110 badpages++;
111 continue; 240 continue;
112 } 241 }
113 bit_test_and_set(signature_page,current_page++); 242 page_ok(current_page++);
114 } 243 }
115 if (do_check) 244 if (badpages == 1)
116 printf("\n"); 245 printf("one bad page\n");
117 if (badpages) 246 else if (badpages > 1)
118 printf("%d bad page%s\n",badpages,(badpages>1)?"s":""); 247 printf("%d bad pages\n", badpages);
119} 248}
120 249
121static long valid_offset (int fd, int offset) 250static long valid_offset (int fd, int offset)
@@ -129,12 +258,13 @@ static long valid_offset (int fd, int offset)
129 return 1; 258 return 1;
130} 259}
131 260
132static int count_blocks (int fd) 261static int
262find_size (int fd)
133{ 263{
134 int high, low; 264 unsigned int high, low;
135 265
136 low = 0; 266 low = 0;
137 for (high = 1; valid_offset (fd, high); high *= 2) 267 for (high = 1; high > 0 && valid_offset (fd, high); high *= 2)
138 low = high; 268 low = high;
139 while (low < high - 1) 269 while (low < high - 1)
140 { 270 {
@@ -145,108 +275,194 @@ static int count_blocks (int fd)
145 else 275 else
146 high = mid; 276 high = mid;
147 } 277 }
148 valid_offset (fd, 0);
149 return (low + 1); 278 return (low + 1);
150} 279}
151 280
152static int get_size(const char *file) 281/* return size in pages, to avoid integer overflow */
282static long
283get_size(const char *file)
153{ 284{
154 int fd; 285 int fd;
155 int size; 286 long size;
156 287
157 fd = open(file, O_RDWR); 288 fd = open(file, O_RDONLY);
158 if (fd < 0) { 289 if (fd < 0) {
159 perror(file); 290 perror(file);
160 exit(1); 291 exit(1);
161 } 292 }
162 if (ioctl(fd, BLKGETSIZE, &size) >= 0) { 293 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
163 close(fd); 294 int sectors_per_page = pagesize/512;
164 return (size * 512); 295 size /= sectors_per_page;
296 } else {
297 size = find_size(fd) / pagesize;
165 } 298 }
166
167 size = count_blocks(fd);
168 close(fd); 299 close(fd);
169 return size; 300 return size;
170} 301}
171 302
172int 303int mkswap_main(int argc, char ** argv)
173mkswap(const char *device_name, int pages, int check) 304{
174 { 305 char * tmp;
175 struct stat statbuf; 306 struct stat statbuf;
307 int sz;
308 int maxpages;
176 int goodpages; 309 int goodpages;
177 int signature_page[PAGE_SIZE/sizeof(int)]; 310 int offset;
311 int force = 0;
178 312
179 PAGES = pages; 313 if (argc && *argv)
180 do_check = check; 314 program_name = *argv;
181 315
182 memset(signature_page,0,PAGE_SIZE); 316 init_signature_page(); /* get pagesize */
183 317
184 if (device_name && !PAGES) { 318 while (argc-- > 1) {
185 PAGES = get_size(device_name) / PAGE_SIZE; 319 argv++;
320 if (argv[0][0] != '-') {
321 if (device_name) {
322 int blocks_per_page = pagesize/1024;
323 PAGES = strtol(argv[0],&tmp,0)/blocks_per_page;
324 if (*tmp)
325 usage( mkswap_usage);
326 } else
327 device_name = argv[0];
328 } 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);
341 }
342 }
343 }
344 if (!device_name) {
345 fprintf(stderr,
346 "%s: error: Nowhere to set up swap on?\n",
347 program_name);
348 usage( mkswap_usage);
186 } 349 }
187 if (!device_name || PAGES<10) { 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) {
188 fprintf(stderr, 378 fprintf(stderr,
189 "%s: error: swap area needs to be at least %ldkB\n", 379 "%s: error: swap area needs to be at least %ldkB\n",
190 program_name, 10 * PAGE_SIZE / 1024); 380 program_name, (long)(10 * pagesize / 1024));
191 /* usage(mkswap_usage); */ 381 usage( mkswap_usage);
192 exit(1); 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;
193 } 394 }
194 if (PAGES > 8 * (PAGE_SIZE - 10)) { 395#endif
195 PAGES = 8 * (PAGE_SIZE - 10); 396 if (PAGES > maxpages) {
397 PAGES = maxpages;
196 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n", 398 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n",
197 program_name, PAGES * PAGE_SIZE / 1024); 399 program_name, PAGES * pagesize / 1024);
198 } 400 }
401
199 DEV = open(device_name,O_RDWR); 402 DEV = open(device_name,O_RDWR);
200 if (DEV < 0 || fstat(DEV, &statbuf) < 0) { 403 if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
201 perror(device_name); 404 perror(device_name);
202 exit(1); 405 exit( FALSE);
203 } 406 }
204 if (!S_ISBLK(statbuf.st_mode)) 407 if (!S_ISBLK(statbuf.st_mode))
205 do_check=0; 408 check=0;
206 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) 409 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
207 die("Will not try to make swapdevice on '%s'"); 410 die("Will not try to make swapdevice on '%s'");
208 check_blocks(signature_page); 411
209 if (!bit_test_and_clear(signature_page,0)) 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))
210 die("fatal: first page unreadable"); 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
211 goodpages = PAGES - badpages - 1; 447 goodpages = PAGES - badpages - 1;
212 if (goodpages <= 0) 448 if (goodpages <= 0)
213 die("Unable to set up swap-space: unreadable"); 449 die("Unable to set up swap-space: unreadable");
214 printf("Setting up swapspace, size = %ld bytes\n",goodpages*PAGE_SIZE); 450 printf("Setting up swapspace version %d, size = %ld bytes\n",
215 strncpy((char*)signature_page+PAGE_SIZE-10,"SWAP-SPACE",10); 451 version, (long)(goodpages*pagesize));
216 if (lseek(DEV, 0, SEEK_SET)) 452 write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
453
454 offset = ((version == 0) ? 0 : 1024);
455 if (lseek(DEV, offset, SEEK_SET) != offset)
217 die("unable to rewind swap-device"); 456 die("unable to rewind swap-device");
218 if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE)) 457 if (write(DEV,(char*)signature_page+offset, pagesize-offset)
458 != pagesize-offset)
219 die("unable to write signature page"); 459 die("unable to write signature page");
220 460
221 close(DEV); 461 /*
222 return (TRUE); 462 * A subsequent swapon() will fail if the signature
223} 463 * is not actually on disk. (This is a kernel bug.)
224 464 */
225int mkswap_main(int argc, char ** argv) 465 if (fsync(DEV))
226{ 466 die("fsync failed");
227 char * tmp; 467 exit ( TRUE);
228 long int pages=0;
229 int check=0;
230
231 if (argc && *argv)
232 program_name = *argv;
233 while (argc > 1) {
234 argv++;
235 argc--;
236 if (argv[0][0] != '-')
237 if (device_name) {
238 pages = strtol(argv[0],&tmp,0)>>(PAGE_SHIFT-10);
239 if (*tmp) {
240 usage (mkswap_usage);
241 }
242 } else
243 device_name = argv[0];
244 else while (*++argv[0])
245 switch (argv[0][0]) {
246 case 'c': check=1; break;
247 default: usage (mkswap_usage);
248 exit( TRUE);
249 }
250 }
251 exit( mkswap(device_name, pages, check));
252} 468}