]>
Commit | Line | Data |
---|---|---|
6dbe3af9 KZ |
1 | /* |
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 | * | |
5c36a0eb | 11 | * Usuage: mkswap [-c] [-vN] [-f] device [size-in-blocks] |
6dbe3af9 | 12 | * |
5c36a0eb KZ |
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. | |
6dbe3af9 | 16 | * |
5c36a0eb | 17 | * The device may be a block device or an image of one, but this isn't |
6dbe3af9 KZ |
18 | * 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. | |
5c36a0eb KZ |
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. | |
6dbe3af9 KZ |
26 | */ |
27 | ||
28 | #include <stdio.h> | |
29 | #include <unistd.h> | |
30 | #include <string.h> | |
31 | #include <fcntl.h> | |
32 | #include <stdlib.h> | |
5c36a0eb KZ |
33 | #include <sys/ioctl.h> /* for _IO */ |
34 | #include <sys/utsname.h> | |
6dbe3af9 | 35 | #include <sys/stat.h> |
5c36a0eb | 36 | #include <asm/page.h> /* for PAGE_SIZE and PAGE_SHIFT */ |
6dbe3af9 | 37 | |
5c36a0eb KZ |
38 | #ifndef _IO |
39 | /* pre-1.3.45 */ | |
fd6b7a7f | 40 | #define BLKGETSIZE 0x1260 |
5c36a0eb KZ |
41 | #else |
42 | /* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */ | |
43 | #define BLKGETSIZE _IO(0x12,96) | |
6dbe3af9 KZ |
44 | #endif |
45 | ||
6dbe3af9 KZ |
46 | static char * program_name = "mkswap"; |
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; | |
5c36a0eb KZ |
52 | static int version = -1; |
53 | ||
54 | #define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r)) | |
55 | ||
56 | static int | |
57 | linux_version_code(void) { | |
58 | struct utsname my_utsname; | |
59 | int p, q, r; | |
60 | ||
61 | if (uname(&my_utsname) == 0) { | |
62 | p = atoi(strtok(my_utsname.release, ".")); | |
63 | q = atoi(strtok(NULL, ".")); | |
64 | r = atoi(strtok(NULL, ".")); | |
65 | return MAKE_VERSION(p,q,r); | |
66 | } | |
67 | return 0; | |
68 | } | |
69 | ||
70 | /* | |
71 | * The definition of the union swap_header uses the constant PAGE_SIZE. | |
72 | * Unfortunately, on some architectures this depends on the hardware model, | |
73 | * and can only be found at run time -- we use getpagesize(). | |
74 | */ | |
75 | ||
76 | static int pagesize; | |
77 | static int *signature_page; | |
6dbe3af9 | 78 | |
5c36a0eb KZ |
79 | struct swap_header_v1 { |
80 | char bootbits[1024]; /* Space for disklabel etc. */ | |
81 | unsigned int version; | |
82 | unsigned int last_page; | |
83 | unsigned int nr_badpages; | |
84 | unsigned int padding[125]; | |
85 | unsigned int badpages[1]; | |
86 | } *p; | |
87 | ||
88 | static void | |
89 | init_signature_page() { | |
90 | pagesize = getpagesize(); | |
91 | ||
92 | #ifdef PAGE_SIZE | |
93 | if (pagesize != PAGE_SIZE) | |
94 | fprintf(stderr, "Assuming pages of size %d\n", pagesize); | |
95 | #endif | |
96 | signature_page = (int *) malloc(pagesize); | |
97 | memset(signature_page,0,pagesize); | |
98 | p = (struct swap_header_v1 *) signature_page; | |
99 | } | |
100 | ||
101 | static void | |
102 | write_signature(char *sig) { | |
103 | char *sp = (char *) signature_page; | |
104 | ||
105 | strncpy(sp+pagesize-10, sig, 10); | |
106 | } | |
107 | ||
108 | #define V0_MAX_PAGES (8 * (pagesize - 10)) | |
109 | #define V1_MAX_PAGES ((0x7fffffff / pagesize) - 1) | |
110 | ||
111 | #define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int)) | |
6dbe3af9 | 112 | |
fd6b7a7f | 113 | static void bit_set (unsigned int *addr, unsigned int nr) |
726f69e2 KZ |
114 | { |
115 | unsigned int r, m; | |
116 | ||
117 | addr += nr / (8 * sizeof(int)); | |
118 | r = *addr; | |
119 | m = 1 << (nr & (8 * sizeof(int) - 1)); | |
120 | *addr = r | m; | |
6dbe3af9 KZ |
121 | } |
122 | ||
fd6b7a7f | 123 | static int bit_test_and_clear (unsigned int *addr, unsigned int nr) |
726f69e2 KZ |
124 | { |
125 | unsigned int r, m; | |
126 | ||
127 | addr += nr / (8 * sizeof(int)); | |
128 | r = *addr; | |
129 | m = 1 << (nr & (8 * sizeof(int) - 1)); | |
130 | *addr = r & ~m; | |
131 | return (r & m) != 0; | |
132 | } | |
6dbe3af9 | 133 | |
5c36a0eb | 134 | void fatal_error(const char * fmt_string) |
6dbe3af9 KZ |
135 | { |
136 | fprintf(stderr,fmt_string,program_name,device_name); | |
137 | exit(1); | |
138 | } | |
139 | ||
5c36a0eb | 140 | #define usage() fatal_error("Usage: %s [-c] [-v0|-v1] /dev/name [blocks]\n") |
6dbe3af9 KZ |
141 | #define die(str) fatal_error("%s: " str "\n") |
142 | ||
5c36a0eb KZ |
143 | void |
144 | page_ok(int page) { | |
145 | if (version==0) | |
146 | bit_set(signature_page, page); | |
147 | } | |
148 | ||
149 | void | |
150 | page_bad(int page) { | |
151 | if (version == 0) | |
152 | bit_test_and_clear(signature_page, page); | |
153 | else { | |
154 | if (badpages == MAX_BADPAGES) | |
155 | die("too many bad pages"); | |
156 | p->badpages[badpages] = page; | |
157 | } | |
158 | badpages++; | |
159 | } | |
160 | ||
161 | void | |
162 | check_blocks(void) { | |
6dbe3af9 KZ |
163 | unsigned int current_page; |
164 | int do_seek = 1; | |
5c36a0eb | 165 | char *buffer; |
6dbe3af9 | 166 | |
5c36a0eb KZ |
167 | buffer = malloc(pagesize); |
168 | if (!buffer) | |
169 | die("Out of memory"); | |
6dbe3af9 KZ |
170 | current_page = 0; |
171 | while (current_page < PAGES) { | |
172 | if (!check) { | |
5c36a0eb | 173 | page_ok(current_page++); |
6dbe3af9 KZ |
174 | continue; |
175 | } | |
5c36a0eb KZ |
176 | if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) != |
177 | current_page*pagesize) | |
6dbe3af9 | 178 | die("seek failed in check_blocks"); |
5c36a0eb KZ |
179 | if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) { |
180 | page_bad(current_page++); | |
6dbe3af9 KZ |
181 | continue; |
182 | } | |
5c36a0eb | 183 | page_ok(current_page++); |
6dbe3af9 KZ |
184 | } |
185 | if (badpages) | |
186 | printf("%d bad page%s\n",badpages,(badpages>1)?"s":""); | |
187 | } | |
188 | ||
189 | static long valid_offset (int fd, int offset) | |
190 | { | |
191 | char ch; | |
192 | ||
193 | if (lseek (fd, offset, 0) < 0) | |
194 | return 0; | |
195 | if (read (fd, &ch, 1) < 1) | |
196 | return 0; | |
197 | return 1; | |
198 | } | |
199 | ||
5c36a0eb KZ |
200 | static int |
201 | find_size (int fd) | |
6dbe3af9 | 202 | { |
5c36a0eb | 203 | unsigned int high, low; |
6dbe3af9 KZ |
204 | |
205 | low = 0; | |
5c36a0eb | 206 | for (high = 1; high > 0 && valid_offset (fd, high); high *= 2) |
6dbe3af9 KZ |
207 | low = high; |
208 | while (low < high - 1) | |
209 | { | |
210 | const int mid = (low + high) / 2; | |
211 | ||
212 | if (valid_offset (fd, mid)) | |
213 | low = mid; | |
214 | else | |
215 | high = mid; | |
216 | } | |
6dbe3af9 KZ |
217 | return (low + 1); |
218 | } | |
219 | ||
5c36a0eb KZ |
220 | /* return size in pages, to avoid integer overflow */ |
221 | static int | |
222 | get_size(const char *file) | |
6dbe3af9 KZ |
223 | { |
224 | int fd; | |
225 | int size; | |
226 | ||
5c36a0eb | 227 | fd = open(file, O_RDONLY); |
6dbe3af9 KZ |
228 | if (fd < 0) { |
229 | perror(file); | |
230 | exit(1); | |
231 | } | |
232 | if (ioctl(fd, BLKGETSIZE, &size) >= 0) { | |
5c36a0eb KZ |
233 | int sectors_per_page = pagesize/512; |
234 | size /= sectors_per_page; | |
235 | } else { | |
236 | size = find_size(fd) / pagesize; | |
6dbe3af9 | 237 | } |
6dbe3af9 KZ |
238 | close(fd); |
239 | return size; | |
240 | } | |
241 | ||
242 | int main(int argc, char ** argv) | |
243 | { | |
244 | char * tmp; | |
245 | struct stat statbuf; | |
5c36a0eb | 246 | int maxpages; |
6dbe3af9 | 247 | int goodpages; |
5c36a0eb KZ |
248 | int offset; |
249 | int force = 0; | |
6dbe3af9 | 250 | |
6dbe3af9 KZ |
251 | if (argc && *argv) |
252 | program_name = *argv; | |
5c36a0eb KZ |
253 | |
254 | init_signature_page(); /* get pagesize */ | |
255 | ||
6dbe3af9 KZ |
256 | while (argc-- > 1) { |
257 | argv++; | |
5c36a0eb | 258 | if (argv[0][0] != '-') { |
6dbe3af9 | 259 | if (device_name) { |
5c36a0eb KZ |
260 | int blocks_per_page = pagesize/1024; |
261 | PAGES = strtol(argv[0],&tmp,0)/blocks_per_page; | |
6dbe3af9 KZ |
262 | if (*tmp) |
263 | usage(); | |
264 | } else | |
265 | device_name = argv[0]; | |
5c36a0eb KZ |
266 | } else { |
267 | switch (argv[0][1]) { | |
268 | case 'c': | |
269 | check=1; | |
270 | break; | |
271 | case 'f': | |
272 | force=1; | |
273 | break; | |
274 | case 'v': | |
275 | version=atoi(argv[0]+2); | |
276 | break; | |
277 | default: | |
278 | usage(); | |
6dbe3af9 | 279 | } |
5c36a0eb | 280 | } |
6dbe3af9 | 281 | } |
fd6b7a7f KZ |
282 | if (!device_name) { |
283 | fprintf(stderr, | |
284 | "%s: error: Nowhere to set up swap on?\n", | |
285 | program_name); | |
286 | usage(); | |
287 | } | |
5c36a0eb KZ |
288 | if (!PAGES) { |
289 | PAGES = get_size(device_name); | |
290 | } | |
291 | ||
292 | if (version == -1) { | |
293 | if (PAGES <= V0_MAX_PAGES) | |
294 | version = 0; | |
295 | else if (linux_version_code() < MAKE_VERSION(2,1,117)) | |
296 | version = 0; | |
297 | else if (pagesize < 2048) | |
298 | version = 0; | |
299 | else | |
300 | version = 1; | |
301 | } | |
302 | if (version != 0 && version != 1) { | |
303 | fprintf(stderr, "%s: error: unknown version %d\n", | |
304 | program_name, version); | |
305 | usage(); | |
306 | } | |
fd6b7a7f | 307 | if (PAGES < 10) { |
726f69e2 KZ |
308 | fprintf(stderr, |
309 | "%s: error: swap area needs to be at least %ldkB\n", | |
5c36a0eb | 310 | program_name, (long)(10 * pagesize / 1024)); |
6dbe3af9 | 311 | usage(); |
726f69e2 | 312 | } |
5c36a0eb KZ |
313 | maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES); |
314 | if (PAGES > maxpages) { | |
315 | PAGES = maxpages; | |
726f69e2 | 316 | fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n", |
5c36a0eb | 317 | program_name, PAGES * pagesize / 1024); |
726f69e2 | 318 | } |
5c36a0eb | 319 | |
6dbe3af9 KZ |
320 | DEV = open(device_name,O_RDWR); |
321 | if (DEV < 0 || fstat(DEV, &statbuf) < 0) { | |
322 | perror(device_name); | |
323 | exit(1); | |
324 | } | |
325 | if (!S_ISBLK(statbuf.st_mode)) | |
326 | check=0; | |
327 | else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) | |
328 | die("Will not try to make swapdevice on '%s'"); | |
5c36a0eb KZ |
329 | |
330 | #ifdef __sparc__ | |
331 | if (!force && version == 0) { | |
332 | /* Don't overwrite partition table unless forced */ | |
333 | unsigned char *buffer = (unsigned char *)signature_page; | |
334 | unsigned short *q, sum; | |
335 | ||
336 | if (read(DEV, buffer, 512) != 512) | |
337 | die("fatal: first page unreadable"); | |
338 | if (buffer[508] == 0xDA && buffer[509] == 0xBE) { | |
339 | q = (unsigned short *)(buffer + 510); | |
340 | for (sum = 0; q >= (unsigned short *) buffer;) | |
341 | sum ^= *q--; | |
342 | if (!sum) { | |
343 | fprintf(stderr, "\ | |
344 | %s: Device '%s' contains a valid Sun disklabel.\n\ | |
345 | This probably means creating v0 swap would destroy your partition table\n\ | |
346 | No swap created. If you really want to create swap v0 on that device, use\n\ | |
347 | the -f option to force it.\n", | |
348 | program_name, device_name); | |
349 | exit(1); | |
350 | } | |
351 | } | |
352 | } | |
353 | #endif | |
354 | ||
355 | if (version == 0 || check) | |
356 | check_blocks(); | |
357 | if (version == 0 && !bit_test_and_clear(signature_page,0)) | |
6dbe3af9 | 358 | die("fatal: first page unreadable"); |
5c36a0eb KZ |
359 | if (version == 1) { |
360 | p->version = version; | |
361 | p->last_page = PAGES-1; | |
362 | p->nr_badpages = badpages; | |
363 | } | |
364 | ||
6dbe3af9 | 365 | goodpages = PAGES - badpages - 1; |
726f69e2 | 366 | if (goodpages <= 0) |
6dbe3af9 | 367 | die("Unable to set up swap-space: unreadable"); |
5c36a0eb KZ |
368 | printf("Setting up swapspace version %d, size = %ld bytes\n", |
369 | version, (long)(goodpages*pagesize)); | |
370 | write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2"); | |
371 | ||
372 | offset = ((version == 0) ? 0 : 1024); | |
373 | if (lseek(DEV, offset, SEEK_SET) != offset) | |
6dbe3af9 | 374 | die("unable to rewind swap-device"); |
5c36a0eb KZ |
375 | if (write(DEV,(char*)signature_page+offset, pagesize-offset) |
376 | != pagesize-offset) | |
6dbe3af9 | 377 | die("unable to write signature page"); |
5c36a0eb | 378 | |
2b6fc908 KZ |
379 | /* |
380 | * A subsequent swapon() will fail if the signature | |
381 | * is not actually on disk. (This is a kernel bug.) | |
382 | */ | |
383 | if (fsync(DEV)) | |
384 | die("fsync failed"); | |
6dbe3af9 KZ |
385 | return 0; |
386 | } |