]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/mkswap.c
Imported from util-linux-2.9i tarball.
[thirdparty/util-linux.git] / disk-utils / mkswap.c
CommitLineData
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
46static char * program_name = "mkswap";
47static char * device_name = NULL;
48static int DEV = -1;
49static long PAGES = 0;
50static int check = 0;
51static int badpages = 0;
5c36a0eb
KZ
52static int version = -1;
53
54#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
55
56static int
57linux_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
76static int pagesize;
77static int *signature_page;
6dbe3af9 78
5c36a0eb
KZ
79struct 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
88static void
89init_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
101static void
102write_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 113static 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 123static 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 134void 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
143void
144page_ok(int page) {
145 if (version==0)
146 bit_set(signature_page, page);
147}
148
149void
150page_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
161void
162check_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
189static 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
200static int
201find_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 */
221static int
222get_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
242int 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\
345This probably means creating v0 swap would destroy your partition table\n\
346No swap created. If you really want to create swap v0 on that device, use\n\
347the -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}