f797d1395bef45a86a571efc369c1f0eb5a33067
[oweals/busybox.git] / util-linux / mkswap.c
1 #include "internal.h"
2 /*
3  * mkswap.c - set up a linux swap device
4  *
5  * (C) 1991 Linus Torvalds. This file may be redistributed as per
6  * the Linux copyright.
7  */
8
9 /*
10  * 20.12.91  -  time began. Got VM working yesterday by doing this by hand.
11  *
12  * Usage: mkswap [-c] device [size-in-blocks]
13  *
14  *      -c for readablility checking (use it unless you are SURE!)
15  *
16  * The device may be a block device or a image of one, but this isn't
17  * enforced (but it's not much fun on a character device :-).
18  *
19  * 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  */
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <sys/ioctl.h>
30
31 #include <asm/page.h>
32 #include <linux/fs.h>
33
34 #ifndef __linux__
35 # define volatile
36 #endif
37
38 #define TEST_BUFFER_PAGES 8
39
40 const char      mkswap_usage[] = "mkswap [-c] partition [block-count]\n"
41 "\n"
42 "\tPrepare a disk partition to be used as a swap partition.\n"
43 "\tThe default block count is the size of the entire partition.\n"
44 "\n"
45 "\t-c:\tCheck for read-ability.\n"
46 "\tblock-count\tUse only this many blocks.\n";
47
48 static const char * program_name = "mkswap";
49 static const char * device_name = NULL;
50 static int DEV = -1;
51 static long PAGES = 0;
52 static int do_check = 0;
53 static int badpages = 0;
54
55
56 static long bit_test_and_set (unsigned int *addr, unsigned int nr)
57 {
58         unsigned int r, m;
59
60         addr += nr / (8 * sizeof(int));
61         r = *addr;
62         m = 1 << (nr & (8 * sizeof(int) - 1));
63         *addr = r | m;
64         return (r & m) != 0;
65 }
66
67 static int bit_test_and_clear (unsigned int *addr, unsigned int nr)
68 {
69         unsigned int r, m;
70
71         addr += nr / (8 * sizeof(int));
72         r = *addr;
73         m = 1 << (nr & (8 * sizeof(int) - 1));
74         *addr = r & ~m;
75         return (r & m) != 0;
76 }
77
78 /*
79  * Volatile to let gcc know that this doesn't return. When trying
80  * to compile this under minix, volatile gives a warning, as
81  * exit() isn't defined as volatile under minix.
82  */
83 volatile void fatal_error(const char * fmt_string)
84 {
85         fprintf(stderr,fmt_string,program_name,device_name);
86         exit(1);
87 }
88
89 #define die(str) fatal_error("%s: " str "\n")
90
91 static void check_blocks(int * signature_page)
92 {
93         unsigned int current_page;
94         int do_seek = 1;
95         char buffer[PAGE_SIZE];
96
97         current_page = 0;
98         while (current_page < PAGES) {
99                 if (!do_check) {
100                         bit_test_and_set(signature_page,current_page++);
101                         continue;
102                 } else {
103                         printf("\r%d", current_page);
104                 }
105                 if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) !=
106                 current_page*PAGE_SIZE)
107                         die("seek failed in check_blocks");
108                 if ( (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) ) {
109                         bit_test_and_clear(signature_page,current_page++);
110                         badpages++;
111                         continue;
112                 }
113                 bit_test_and_set(signature_page,current_page++);
114         }
115         if (do_check)
116                 printf("\n");
117         if (badpages)
118                 printf("%d bad page%s\n",badpages,(badpages>1)?"s":"");
119 }
120
121 static long valid_offset (int fd, int offset)
122 {
123         char ch;
124
125         if (lseek (fd, offset, 0) < 0)
126                 return 0;
127         if (read (fd, &ch, 1) < 1)
128                 return 0;
129         return 1;
130 }
131
132 static int count_blocks (int fd)
133 {
134         int high, low;
135
136         low = 0;
137         for (high = 1; valid_offset (fd, high); high *= 2)
138                 low = high;
139         while (low < high - 1)
140         {
141                 const int mid = (low + high) / 2;
142
143                 if (valid_offset (fd, mid))
144                         low = mid;
145                 else
146                         high = mid;
147         }
148         valid_offset (fd, 0);
149         return (low + 1);
150 }
151
152 static int get_size(const char  *file)
153 {
154         int     fd;
155         int     size;
156
157         fd = open(file, O_RDWR);
158         if (fd < 0) {
159                 perror(file);
160                 exit(1);
161         }
162         if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
163                 close(fd);
164                 return (size * 512);
165         }
166                 
167         size = count_blocks(fd);
168         close(fd);
169         return size;
170 }
171
172 int
173 mkswap(char *device_name, int pages, int check)
174   {
175         struct stat statbuf;
176         int goodpages;
177         int signature_page[PAGE_SIZE/sizeof(int)];
178
179         PAGES = pages;
180         do_check = check;
181
182         memset(signature_page,0,PAGE_SIZE);
183
184         if (device_name && !PAGES) {
185                 PAGES = get_size(device_name) / PAGE_SIZE;
186         }
187         if (!device_name || PAGES<10) {
188                 fprintf(stderr,
189                         "%s: error: swap area needs to be at least %ldkB\n",
190                         program_name, 10 * PAGE_SIZE / 1024);
191                 /*              usage(mkswap_usage); */
192                 exit(1);
193         }
194         if (PAGES > 8 * (PAGE_SIZE - 10)) {
195                 PAGES = 8 * (PAGE_SIZE - 10);
196                 fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n",
197                         program_name, PAGES * PAGE_SIZE / 1024);
198         }
199         DEV = open(device_name,O_RDWR);
200         if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
201                 perror(device_name);
202                 exit(1);
203         }
204         if (!S_ISBLK(statbuf.st_mode))
205                 do_check=0;
206         else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
207                 die("Will not try to make swapdevice on '%s'");
208         check_blocks(signature_page);
209         if (!bit_test_and_clear(signature_page,0))
210                 die("fatal: first page unreadable");
211         goodpages = PAGES - badpages - 1;
212         if (goodpages <= 0)
213                 die("Unable to set up swap-space: unreadable");
214         printf("Setting up swapspace, size = %ld bytes\n",goodpages*PAGE_SIZE);
215         strncpy((char*)signature_page+PAGE_SIZE-10,"SWAP-SPACE",10);
216         if (lseek(DEV, 0, SEEK_SET))
217                 die("unable to rewind swap-device");
218         if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE))
219                 die("unable to write signature page");
220
221         close(DEV);
222         return 0;
223 }
224
225 int mkswap_main(struct FileInfo * unnecessary, int argc, char ** argv)
226 {
227         char * tmp;
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                                         exit(1);
242                                 }
243                         } else
244                                 device_name = argv[0];
245                 else while (*++argv[0])
246                         switch (argv[0][0]) {
247                                 case 'c': check=1; break;
248                                 default: usage(mkswap_usage);
249                                         exit(1);
250                         }
251         }
252         return mkswap(device_name, pages, check);
253 }