]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/mkswap.c
Imported from util-linux-2.9i 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 * Usuage: 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 */
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <sys/ioctl.h> /* for _IO */
34 #include <sys/utsname.h>
35 #include <sys/stat.h>
36 #include <asm/page.h> /* for PAGE_SIZE and PAGE_SHIFT */
37
38 #ifndef _IO
39 /* pre-1.3.45 */
40 #define BLKGETSIZE 0x1260
41 #else
42 /* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */
43 #define BLKGETSIZE _IO(0x12,96)
44 #endif
45
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;
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;
78
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))
112
113 static void bit_set (unsigned int *addr, unsigned int nr)
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;
121 }
122
123 static int bit_test_and_clear (unsigned int *addr, unsigned int nr)
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 }
133
134 void fatal_error(const char * fmt_string)
135 {
136 fprintf(stderr,fmt_string,program_name,device_name);
137 exit(1);
138 }
139
140 #define usage() fatal_error("Usage: %s [-c] [-v0|-v1] /dev/name [blocks]\n")
141 #define die(str) fatal_error("%s: " str "\n")
142
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) {
163 unsigned int current_page;
164 int do_seek = 1;
165 char *buffer;
166
167 buffer = malloc(pagesize);
168 if (!buffer)
169 die("Out of memory");
170 current_page = 0;
171 while (current_page < PAGES) {
172 if (!check) {
173 page_ok(current_page++);
174 continue;
175 }
176 if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) !=
177 current_page*pagesize)
178 die("seek failed in check_blocks");
179 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
180 page_bad(current_page++);
181 continue;
182 }
183 page_ok(current_page++);
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
200 static int
201 find_size (int fd)
202 {
203 unsigned int high, low;
204
205 low = 0;
206 for (high = 1; high > 0 && valid_offset (fd, high); high *= 2)
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 }
217 return (low + 1);
218 }
219
220 /* return size in pages, to avoid integer overflow */
221 static int
222 get_size(const char *file)
223 {
224 int fd;
225 int size;
226
227 fd = open(file, O_RDONLY);
228 if (fd < 0) {
229 perror(file);
230 exit(1);
231 }
232 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
233 int sectors_per_page = pagesize/512;
234 size /= sectors_per_page;
235 } else {
236 size = find_size(fd) / pagesize;
237 }
238 close(fd);
239 return size;
240 }
241
242 int main(int argc, char ** argv)
243 {
244 char * tmp;
245 struct stat statbuf;
246 int maxpages;
247 int goodpages;
248 int offset;
249 int force = 0;
250
251 if (argc && *argv)
252 program_name = *argv;
253
254 init_signature_page(); /* get pagesize */
255
256 while (argc-- > 1) {
257 argv++;
258 if (argv[0][0] != '-') {
259 if (device_name) {
260 int blocks_per_page = pagesize/1024;
261 PAGES = strtol(argv[0],&tmp,0)/blocks_per_page;
262 if (*tmp)
263 usage();
264 } else
265 device_name = argv[0];
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();
279 }
280 }
281 }
282 if (!device_name) {
283 fprintf(stderr,
284 "%s: error: Nowhere to set up swap on?\n",
285 program_name);
286 usage();
287 }
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 }
307 if (PAGES < 10) {
308 fprintf(stderr,
309 "%s: error: swap area needs to be at least %ldkB\n",
310 program_name, (long)(10 * pagesize / 1024));
311 usage();
312 }
313 maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
314 if (PAGES > maxpages) {
315 PAGES = maxpages;
316 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n",
317 program_name, PAGES * pagesize / 1024);
318 }
319
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'");
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))
358 die("fatal: first page unreadable");
359 if (version == 1) {
360 p->version = version;
361 p->last_page = PAGES-1;
362 p->nr_badpages = badpages;
363 }
364
365 goodpages = PAGES - badpages - 1;
366 if (goodpages <= 0)
367 die("Unable to set up swap-space: unreadable");
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)
374 die("unable to rewind swap-device");
375 if (write(DEV,(char*)signature_page+offset, pagesize-offset)
376 != pagesize-offset)
377 die("unable to write signature page");
378
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");
385 return 0;
386 }