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