]>
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 | * | |
7eda085c | 11 | * Usage: 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. | |
7eda085c | 26 | * V1_MAX_PAGES fixes, jj, 990325. |
22853e4a | 27 | * sparc64 fixes, jj, 000219. |
7eda085c | 28 | * |
b50945d4 | 29 | * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL> |
7eda085c | 30 | * - added Native Language Support |
3e18b040 | 31 | * |
6dbe3af9 KZ |
32 | */ |
33 | ||
34 | #include <stdio.h> | |
35 | #include <unistd.h> | |
36 | #include <string.h> | |
37 | #include <fcntl.h> | |
38 | #include <stdlib.h> | |
99e6d525 | 39 | #include <limits.h> |
8abb4c2e | 40 | #include <mntent.h> |
5c36a0eb | 41 | #include <sys/utsname.h> |
6dbe3af9 | 42 | #include <sys/stat.h> |
3e18b040 | 43 | #include <errno.h> |
e079c4e6 | 44 | #include <getopt.h> |
3e18b040 KZ |
45 | #ifdef HAVE_LIBSELINUX |
46 | #include <selinux/selinux.h> | |
47 | #include <selinux/context.h> | |
48 | #endif | |
49 | ||
8023c83b | 50 | #include "linux_version.h" |
756bfd01 | 51 | #include "swapheader.h" |
8abcf290 | 52 | #include "strutils.h" |
66ee8158 | 53 | #include "nls.h" |
54e377b3 | 54 | #include "blkdev.h" |
fc68cd49 | 55 | #include "pathnames.h" |
ff3bed80 | 56 | #include "wholedisk.h" |
e12c9866 | 57 | #include "all-io.h" |
94a50e28 | 58 | #include "xalloc.h" |
08675263 | 59 | #include "c.h" |
45ca68ec | 60 | #include "closestream.h" |
def478cf | 61 | #include "ismounted.h" |
66ee8158 | 62 | |
766dd757 | 63 | #ifdef HAVE_LIBUUID |
7ee96990 | 64 | # include <uuid.h> |
756bfd01 KZ |
65 | #endif |
66 | ||
64d15476 | 67 | #ifdef HAVE_LIBBLKID |
566f35bc KZ |
68 | # include <blkid.h> |
69 | #endif | |
70 | ||
f2704664 | 71 | static char *device_name = NULL; |
6dbe3af9 | 72 | static int DEV = -1; |
99e6d525 | 73 | static unsigned long long PAGES = 0; |
d03dd608 | 74 | static unsigned long badpages = 0; |
6dbe3af9 | 75 | static int check = 0; |
5c36a0eb | 76 | |
3e18b040 KZ |
77 | #define SELINUX_SWAPFILE_TYPE "swapfile_t" |
78 | ||
22853e4a KZ |
79 | #ifdef __sparc__ |
80 | # ifdef __arch64__ | |
81 | # define is_sparc64() 1 | |
82 | # define is_be64() 1 | |
83 | # else /* sparc32 */ | |
84 | static int | |
f2704664 SK |
85 | is_sparc64(void) |
86 | { | |
22853e4a KZ |
87 | struct utsname un; |
88 | static int sparc64 = -1; | |
89 | ||
f2704664 SK |
90 | if (sparc64 != -1) |
91 | return sparc64; | |
22853e4a KZ |
92 | sparc64 = 0; |
93 | ||
f2704664 SK |
94 | if (uname(&un) < 0) |
95 | return 0; | |
22853e4a KZ |
96 | if (! strcmp(un.machine, "sparc64")) { |
97 | sparc64 = 1; | |
98 | return 1; | |
99 | } | |
100 | if (strcmp(un.machine, "sparc")) | |
101 | return 0; /* Should not happen */ | |
102 | ||
48d7b13a | 103 | #ifdef HAVE_PERSONALITY |
22853e4a KZ |
104 | { |
105 | extern int personality(unsigned long); | |
106 | int oldpers; | |
107 | #define PERS_LINUX 0x00000000 | |
108 | #define PERS_LINUX_32BIT 0x00800000 | |
109 | #define PERS_LINUX32 0x00000008 | |
110 | ||
111 | oldpers = personality(PERS_LINUX_32BIT); | |
112 | if (oldpers != -1) { | |
113 | if (personality(PERS_LINUX) != -1) { | |
114 | uname(&un); | |
115 | if (! strcmp(un.machine, "sparc64")) { | |
116 | sparc64 = 1; | |
117 | oldpers = PERS_LINUX32; | |
118 | } | |
119 | } | |
120 | personality(oldpers); | |
121 | } | |
122 | } | |
123 | #endif | |
124 | ||
125 | return sparc64; | |
126 | } | |
127 | # define is_be64() is_sparc64() | |
128 | # endif /* sparc32 */ | |
129 | #else /* !sparc */ | |
130 | # define is_be64() 0 | |
131 | #endif | |
132 | ||
5c36a0eb | 133 | /* |
7ac3b5b8 KZ |
134 | * The definition of the union swap_header uses the kernel constant PAGE_SIZE. |
135 | * Unfortunately, on some architectures this depends on the hardware model, and | |
136 | * can only be found at run time -- we use getpagesize(), so that we do not | |
137 | * need separate binaries e.g. for sun4, sun4c/d/m and sun4u. | |
138 | * | |
139 | * Even more unfortunately, getpagesize() does not always return the right | |
140 | * information. For example, libc4, libc5 and glibc 2.0 do not use the system | |
141 | * call but invent a value themselves (EXEC_PAGESIZE or NBPG * CLSIZE or NBPC), | |
142 | * and thus it may happen that e.g. on a sparc kernel PAGE_SIZE=4096 and | |
143 | * getpagesize() returns 8192. | |
eb63b9b8 | 144 | * |
eb63b9b8 | 145 | * What to do? Let us allow the user to specify the pagesize explicitly. |
7ac3b5b8 | 146 | * |
5c36a0eb | 147 | */ |
33fb5cfd KZ |
148 | static unsigned int user_pagesize; |
149 | static unsigned int pagesize; | |
3216beb0 | 150 | static unsigned long *signature_page = NULL; |
5c36a0eb KZ |
151 | |
152 | static void | |
f2704664 SK |
153 | init_signature_page(void) |
154 | { | |
756bfd01 | 155 | |
33fb5cfd | 156 | unsigned int kernel_pagesize = pagesize = getpagesize(); |
eb63b9b8 KZ |
157 | |
158 | if (user_pagesize) { | |
33fb5cfd KZ |
159 | if (!is_power_of_2(user_pagesize) || |
160 | user_pagesize < sizeof(struct swap_header_v1_2) + 10) | |
00a7d0d2 | 161 | errx(EXIT_FAILURE, |
33fb5cfd | 162 | _("Bad user-specified page size %u"), |
eb63b9b8 | 163 | user_pagesize); |
eb63b9b8 KZ |
164 | pagesize = user_pagesize; |
165 | } | |
166 | ||
7ac3b5b8 | 167 | if (user_pagesize && user_pagesize != kernel_pagesize) |
00a7d0d2 SK |
168 | warnx(_("Using user-specified page size %d, " |
169 | "instead of the system value %d"), | |
7ac3b5b8 | 170 | pagesize, kernel_pagesize); |
eb63b9b8 | 171 | |
94a50e28 | 172 | signature_page = (unsigned long *) xcalloc(1, pagesize); |
5c36a0eb KZ |
173 | } |
174 | ||
175 | static void | |
f2704664 SK |
176 | write_signature(char *sig) |
177 | { | |
5c36a0eb KZ |
178 | char *sp = (char *) signature_page; |
179 | ||
f2704664 | 180 | strncpy(sp + pagesize - 10, sig, 10); |
5c36a0eb KZ |
181 | } |
182 | ||
756bfd01 | 183 | static void |
f2704664 SK |
184 | write_uuid_and_label(unsigned char *uuid, char *volume_name) |
185 | { | |
756bfd01 KZ |
186 | struct swap_header_v1_2 *h; |
187 | ||
188 | /* Sanity check */ | |
189 | if (sizeof(struct swap_header_v1) != | |
190 | sizeof(struct swap_header_v1_2)) { | |
00a7d0d2 | 191 | warnx(_("Bad swap header size, no label written.")); |
756bfd01 KZ |
192 | return; |
193 | } | |
194 | ||
195 | h = (struct swap_header_v1_2 *) signature_page; | |
196 | if (uuid) | |
197 | memcpy(h->uuid, uuid, sizeof(h->uuid)); | |
198 | if (volume_name) { | |
199 | xstrncpy(h->volume_name, volume_name, sizeof(h->volume_name)); | |
200 | if (strlen(volume_name) > strlen(h->volume_name)) | |
00a7d0d2 | 201 | warnx(_("Label was truncated.")); |
756bfd01 KZ |
202 | } |
203 | if (uuid || volume_name) { | |
204 | if (volume_name) | |
205 | printf("LABEL=%s, ", h->volume_name); | |
206 | else | |
207 | printf(_("no label, ")); | |
766dd757 | 208 | #ifdef HAVE_LIBUUID |
756bfd01 KZ |
209 | if (uuid) { |
210 | char uuid_string[37]; | |
211 | uuid_unparse(uuid, uuid_string); | |
212 | printf("UUID=%s\n", uuid_string); | |
213 | } else | |
214 | #endif | |
215 | printf(_("no uuid\n")); | |
216 | } | |
217 | } | |
218 | ||
0e6f4a20 KZ |
219 | /* |
220 | * Find out what the maximum amount of swap space is that the kernel will | |
221 | * handle. This wouldn't matter if the kernel just used as much of the | |
222 | * swap space as it can handle, but until 2.3.4 it would return an error | |
223 | * to swapon() if the swapspace was too large. | |
224 | */ | |
7eda085c KZ |
225 | /* Before 2.2.0pre9 */ |
226 | #define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1) | |
0e6f4a20 | 227 | /* Since 2.2.0pre9, before 2.3.4: |
7eda085c KZ |
228 | error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL)) |
229 | with variations on | |
230 | #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) | |
231 | #define SWP_OFFSET(entry) ((entry) >> 8) | |
232 | on the various architectures. Below the result - yuk. | |
233 | ||
234 | Machine pagesize SWP_ENTRY SWP_OFFSET bound+1 oldbound+2 | |
235 | i386 2^12 o<<8 e>>8 1<<24 1<<19 | |
236 | mips 2^12 o<<15 e>>15 1<<17 1<<19 | |
237 | alpha 2^13 o<<40 e>>40 1<<24 1<<18 | |
238 | m68k 2^12 o<<12 e>>12 1<<20 1<<19 | |
239 | sparc 2^{12,13} (o&0x3ffff)<<9 (e>>9)&0x3ffff 1<<18 1<<{19,18} | |
240 | sparc64 2^13 o<<13 e>>13 1<<51 1<<18 | |
241 | ppc 2^12 o<<8 e>>8 1<<24 1<<19 | |
242 | armo 2^{13,14,15} o<<8 e>>8 1<<24 1<<{18,17,16} | |
243 | armv 2^12 o<<9 e>>9 1<<23 1<<19 | |
244 | ||
245 | assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere. | |
246 | ||
247 | The bad part is that we need to know this since the kernel will | |
248 | refuse a swap space if it is too large. | |
249 | */ | |
250 | /* patch from jj - why does this differ from the above? */ | |
22853e4a KZ |
251 | /* 32bit kernels have a second limitation of 2GB, sparc64 is limited by |
252 | the size of virtual address space allocation for vmalloc */ | |
7eda085c KZ |
253 | #if defined(__alpha__) |
254 | #define V1_MAX_PAGES ((1 << 24) - 1) | |
255 | #elif defined(__mips__) | |
256 | #define V1_MAX_PAGES ((1 << 17) - 1) | |
7eda085c | 257 | #elif defined(__sparc__) |
22853e4a | 258 | #define V1_MAX_PAGES (is_sparc64() ? ((3 << 29) - 1) : ((1 << 18) - 1)) |
1d4ad1de | 259 | #elif defined(__ia64__) |
3e18b040 | 260 | /* |
1d4ad1de KZ |
261 | * The actual size will depend on the amount of virtual address space |
262 | * available to vmalloc the swap map. | |
263 | */ | |
264 | #define V1_MAX_PAGES ((1UL << 54) - 1) | |
7eda085c KZ |
265 | #else |
266 | #define V1_MAX_PAGES V1_OLD_MAX_PAGES | |
267 | #endif | |
268 | /* man page now says: | |
269 | The maximum useful size of a swap area now depends on the architecture. | |
270 | It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips, | |
271 | 128GB on alpha and 3TB on sparc64. | |
272 | */ | |
5c36a0eb KZ |
273 | |
274 | #define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int)) | |
99e6d525 | 275 | #define MIN_GOODPAGES 10 |
6dbe3af9 | 276 | |
e079c4e6 SK |
277 | static void __attribute__ ((__noreturn__)) usage(FILE *out) |
278 | { | |
279 | fprintf(out, | |
280 | _("\nUsage:\n" | |
281 | " %s [options] device [size]\n"), | |
00a7d0d2 | 282 | program_invocation_short_name); |
e079c4e6 SK |
283 | |
284 | fprintf(out, _( | |
285 | "\nOptions:\n" | |
286 | " -c, --check check bad blocks before creating the swap area\n" | |
287 | " -f, --force allow swap size area be larger than device\n" | |
288 | " -p, --pagesize SIZE specify page size in bytes\n" | |
289 | " -L, --label LABEL specify label\n" | |
290 | " -v, --swapversion NUM specify swap-space version number\n" | |
291 | " -U, --uuid UUID specify the uuid to use\n" | |
292 | " -V, --version output version information and exit\n" | |
293 | " -h, --help display this help and exit\n\n")); | |
294 | ||
295 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
eb63b9b8 | 296 | } |
6dbe3af9 | 297 | |
22853e4a | 298 | static void |
f2704664 SK |
299 | page_bad(int page) |
300 | { | |
3e16599a KZ |
301 | struct swap_header_v1_2 *p = (struct swap_header_v1_2 *) signature_page; |
302 | ||
4c85aa3a | 303 | if (badpages == MAX_BADPAGES) |
00a7d0d2 | 304 | errx(EXIT_FAILURE, _("too many bad pages")); |
4c85aa3a | 305 | p->badpages[badpages] = page; |
5c36a0eb KZ |
306 | badpages++; |
307 | } | |
308 | ||
22853e4a | 309 | static void |
f2704664 SK |
310 | check_blocks(void) |
311 | { | |
6dbe3af9 KZ |
312 | unsigned int current_page; |
313 | int do_seek = 1; | |
5c36a0eb | 314 | char *buffer; |
6dbe3af9 | 315 | |
94a50e28 | 316 | buffer = xmalloc(pagesize); |
6dbe3af9 KZ |
317 | current_page = 0; |
318 | while (current_page < PAGES) { | |
f3b16286 KZ |
319 | |
320 | ssize_t rc; | |
321 | ||
5c36a0eb KZ |
322 | if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) != |
323 | current_page*pagesize) | |
00a7d0d2 | 324 | errx(EXIT_FAILURE, _("seek failed in check_blocks")); |
f3b16286 KZ |
325 | |
326 | rc = read(DEV, buffer, pagesize); | |
327 | do_seek = (rc < 0 || (size_t) rc != pagesize); | |
328 | if (do_seek) | |
adda7f7e | 329 | page_bad(current_page); |
adda7f7e | 330 | current_page++; |
6dbe3af9 | 331 | } |
ff21848c | 332 | printf(P_("%lu bad page\n", "%lu bad pages\n", badpages), badpages); |
3216beb0 | 333 | free(buffer); |
6dbe3af9 KZ |
334 | } |
335 | ||
99e6d525 HD |
336 | /* return size in pages */ |
337 | static unsigned long long | |
f2704664 SK |
338 | get_size(const char *file) |
339 | { | |
340 | int fd; | |
54e377b3 | 341 | unsigned long long size; |
6dbe3af9 | 342 | |
5c36a0eb | 343 | fd = open(file, O_RDONLY); |
6dbe3af9 KZ |
344 | if (fd < 0) { |
345 | perror(file); | |
a4d3e778 | 346 | exit(EXIT_FAILURE); |
6dbe3af9 | 347 | } |
54e377b3 KZ |
348 | if (blkdev_get_size(fd, &size) == 0) |
349 | size /= pagesize; | |
54e377b3 | 350 | |
6dbe3af9 KZ |
351 | close(fd); |
352 | return size; | |
353 | } | |
354 | ||
979f1dd5 KZ |
355 | #ifdef HAVE_LIBBLKID |
356 | static blkid_probe | |
357 | new_prober(int fd) | |
358 | { | |
359 | blkid_probe pr = blkid_new_probe(); | |
360 | if (!pr) | |
361 | errx(EXIT_FAILURE, _("unable to alloc new libblkid probe")); | |
362 | if (blkid_probe_set_device(pr, fd, 0, 0)) | |
363 | errx(EXIT_FAILURE, _("unable to assign device to libblkid probe")); | |
364 | return pr; | |
365 | } | |
366 | #endif | |
367 | ||
ff3bed80 | 368 | static void |
c1f1b301 | 369 | wipe_device(int fd, const char *devname, int force) |
ff3bed80 | 370 | { |
566f35bc | 371 | char *type = NULL; |
ff3bed80 | 372 | int zap = 1; |
9206b238 | 373 | #ifdef HAVE_LIBBLKID |
979f1dd5 | 374 | blkid_probe pr = NULL; |
9206b238 | 375 | #endif |
ff3bed80 KZ |
376 | if (!force) { |
377 | if (lseek(fd, 0, SEEK_SET) != 0) | |
00a7d0d2 | 378 | errx(EXIT_FAILURE, _("unable to rewind swap-device")); |
ff3bed80 | 379 | |
64d15476 | 380 | #ifdef HAVE_LIBBLKID |
c1f1b301 MB |
381 | pr = new_prober(fd); |
382 | blkid_probe_enable_partitions(pr, 1); | |
383 | blkid_probe_enable_superblocks(pr, 0); | |
384 | ||
385 | if (blkid_do_fullprobe(pr) == 0 && | |
386 | blkid_probe_lookup_value(pr, "PTTYPE", | |
387 | (const char **) &type, NULL) == 0 && type) { | |
388 | type = xstrdup(type); | |
ff3bed80 | 389 | zap = 0; |
566f35bc | 390 | } |
c1f1b301 MB |
391 | #else |
392 | /* don't zap if compiled without libblkid */ | |
393 | zap = 0; | |
394 | #endif | |
ff3bed80 KZ |
395 | } |
396 | ||
397 | if (zap) { | |
979f1dd5 KZ |
398 | /* |
399 | * Wipe boodbits | |
400 | */ | |
ff3bed80 | 401 | char buf[1024]; |
c1f1b301 | 402 | const char *data = NULL; |
ff3bed80 KZ |
403 | |
404 | if (lseek(fd, 0, SEEK_SET) != 0) | |
00a7d0d2 | 405 | errx(EXIT_FAILURE, _("unable to rewind swap-device")); |
ff3bed80 KZ |
406 | |
407 | memset(buf, 0, sizeof(buf)); | |
408 | if (write_all(fd, buf, sizeof(buf))) | |
00a7d0d2 | 409 | errx(EXIT_FAILURE, _("unable to erase bootbits sectors")); |
979f1dd5 KZ |
410 | #ifdef HAVE_LIBBLKID |
411 | /* | |
412 | * Wipe rest of the device | |
413 | */ | |
414 | if (!pr) | |
415 | pr = new_prober(fd); | |
416 | ||
417 | blkid_probe_enable_superblocks(pr, 1); | |
418 | blkid_probe_enable_partitions(pr, 0); | |
c1f1b301 | 419 | blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_TYPE); |
979f1dd5 | 420 | |
c1f1b301 MB |
421 | while (blkid_do_probe(pr) == 0) { |
422 | if (blkid_probe_lookup_value(pr, "TYPE", &data, NULL) == 0 && data) | |
423 | warnx(_("%s: warning: wiping old %s signature."), devname, data); | |
979f1dd5 | 424 | blkid_do_wipe(pr, 0); |
c1f1b301 | 425 | } |
979f1dd5 KZ |
426 | #endif |
427 | } else { | |
428 | warnx(_("%s: warning: don't erase bootbits sectors"), | |
429 | devname); | |
430 | if (type) | |
431 | fprintf(stderr, _(" (%s partition table detected). "), type); | |
979f1dd5 KZ |
432 | else |
433 | fprintf(stderr, _(" (compiled without libblkid). ")); | |
8c219bf4 | 434 | fprintf(stderr, _("Use -f to force.\n")); |
ff3bed80 | 435 | } |
979f1dd5 KZ |
436 | #ifdef HAVE_LIBBLKID |
437 | blkid_free_probe(pr); | |
438 | #endif | |
ff3bed80 KZ |
439 | } |
440 | ||
eb63b9b8 | 441 | int |
f2704664 | 442 | main(int argc, char **argv) { |
6dbe3af9 | 443 | struct stat statbuf; |
3e16599a | 444 | struct swap_header_v1_2 *hdr; |
e079c4e6 | 445 | int c; |
99e6d525 HD |
446 | unsigned long long maxpages; |
447 | unsigned long long goodpages; | |
448 | unsigned long long sz; | |
1d4ad1de | 449 | off_t offset; |
5c36a0eb | 450 | int force = 0; |
33fb5cfd | 451 | int version = 1; |
eb63b9b8 | 452 | char *block_count = 0; |
756bfd01 | 453 | char *opt_label = NULL; |
f88c89db | 454 | unsigned char *uuid = NULL; |
766dd757 | 455 | #ifdef HAVE_LIBUUID |
7b241808 | 456 | const char *opt_uuid = NULL; |
756bfd01 KZ |
457 | uuid_t uuid_dat; |
458 | #endif | |
6c7d5ae9 | 459 | static const struct option longopts[] = { |
e079c4e6 SK |
460 | { "check", no_argument, 0, 'c' }, |
461 | { "force", no_argument, 0, 'f' }, | |
462 | { "pagesize", required_argument, 0, 'p' }, | |
463 | { "label", required_argument, 0, 'L' }, | |
464 | { "swapversion", required_argument, 0, 'v' }, | |
465 | { "uuid", required_argument, 0, 'U' }, | |
466 | { "version", no_argument, 0, 'V' }, | |
467 | { "help", no_argument, 0, 'h' }, | |
468 | { NULL, 0, 0, 0 } | |
469 | }; | |
eb63b9b8 | 470 | |
7eda085c KZ |
471 | setlocale(LC_ALL, ""); |
472 | bindtextdomain(PACKAGE, LOCALEDIR); | |
473 | textdomain(PACKAGE); | |
45ca68ec | 474 | atexit(close_stdout); |
5c36a0eb | 475 | |
e079c4e6 SK |
476 | while((c = getopt_long(argc, argv, "cfp:L:v:U:Vh", longopts, NULL)) != -1) { |
477 | switch (c) { | |
478 | case 'c': | |
479 | check=1; | |
480 | break; | |
481 | case 'f': | |
482 | force=1; | |
483 | break; | |
484 | case 'p': | |
8c219bf4 | 485 | user_pagesize = strtou32_or_err(optarg, _("parsing page size failed")); |
e079c4e6 SK |
486 | break; |
487 | case 'L': | |
488 | opt_label = optarg; | |
489 | break; | |
490 | case 'v': | |
8c219bf4 | 491 | version = strtos32_or_err(optarg, _("parsing version number failed")); |
e079c4e6 SK |
492 | break; |
493 | case 'U': | |
93bf0f28 | 494 | #ifdef HAVE_LIBUUID |
e079c4e6 | 495 | opt_uuid = optarg; |
93bf0f28 | 496 | #else |
4e096801 | 497 | warnx(_("warning: ignoring -U (UUIDs are unsupported by %s)"), |
e079c4e6 | 498 | program_invocation_short_name); |
93bf0f28 | 499 | #endif |
e079c4e6 SK |
500 | break; |
501 | case 'V': | |
33fb5cfd | 502 | printf(UTIL_LINUX_VERSION); |
e079c4e6 SK |
503 | exit(EXIT_SUCCESS); |
504 | case 'h': | |
505 | usage(stdout); | |
506 | default: | |
507 | usage(stderr); | |
508 | } | |
509 | } | |
510 | if (optind < argc) | |
511 | device_name = argv[optind++]; | |
512 | if (optind < argc) | |
513 | block_count = argv[optind++]; | |
514 | if (optind != argc) { | |
8c219bf4 | 515 | warnx(_("only one device argument is currently supported")); |
e079c4e6 | 516 | usage(stderr); |
6dbe3af9 | 517 | } |
eb63b9b8 | 518 | |
f2704664 | 519 | if (version != 1) |
00a7d0d2 | 520 | errx(EXIT_FAILURE, |
4e096801 | 521 | _("swapspace version %d is not supported"), version); |
4c85aa3a | 522 | |
766dd757 | 523 | #ifdef HAVE_LIBUUID |
93bf0f28 MS |
524 | if(opt_uuid) { |
525 | if (uuid_parse(opt_uuid, uuid_dat) != 0) | |
8c219bf4 | 526 | errx(EXIT_FAILURE, _("error: parsing UUID failed")); |
93bf0f28 MS |
527 | } else |
528 | uuid_generate(uuid_dat); | |
756bfd01 KZ |
529 | uuid = uuid_dat; |
530 | #endif | |
531 | ||
eb63b9b8 KZ |
532 | init_signature_page(); /* get pagesize */ |
533 | ||
fd6b7a7f | 534 | if (!device_name) { |
00a7d0d2 | 535 | warnx(_("error: Nowhere to set up swap on?")); |
e079c4e6 | 536 | usage(stderr); |
fd6b7a7f | 537 | } |
eb63b9b8 | 538 | if (block_count) { |
20543e61 | 539 | /* this silly user specified the number of blocks explicitly */ |
33fb5cfd KZ |
540 | uint64_t blks = strtou64_or_err(block_count, |
541 | _("invalid block count argument")); | |
20543e61 | 542 | PAGES = blks / (pagesize / 1024); |
eb63b9b8 | 543 | } |
7eda085c | 544 | sz = get_size(device_name); |
f2704664 | 545 | if (!PAGES) |
7eda085c | 546 | PAGES = sz; |
f2704664 | 547 | else if (PAGES > sz && !force) { |
00a7d0d2 SK |
548 | errx(EXIT_FAILURE, |
549 | _("error: " | |
550 | "size %llu KiB is larger than device size %llu KiB"), | |
7eda085c | 551 | PAGES*(pagesize/1024), sz*(pagesize/1024)); |
5c36a0eb KZ |
552 | } |
553 | ||
99e6d525 | 554 | if (PAGES < MIN_GOODPAGES) { |
00a7d0d2 SK |
555 | warnx(_("error: swap area needs to be at least %ld KiB"), |
556 | (long)(MIN_GOODPAGES * pagesize/1024)); | |
e079c4e6 | 557 | usage(stderr); |
726f69e2 | 558 | } |
0e6f4a20 | 559 | |
ff035f67 | 560 | #ifdef __linux__ |
4c85aa3a | 561 | if (get_linux_version() >= KERNEL_VERSION(2,3,4)) |
99e6d525 | 562 | maxpages = UINT_MAX + 1ULL; |
8023c83b | 563 | else if (get_linux_version() >= KERNEL_VERSION(2,2,1)) |
7eda085c | 564 | maxpages = V1_MAX_PAGES; |
0e6f4a20 | 565 | else |
4c85aa3a | 566 | #endif |
7eda085c | 567 | maxpages = V1_OLD_MAX_PAGES; |
0e6f4a20 | 568 | |
5c36a0eb KZ |
569 | if (PAGES > maxpages) { |
570 | PAGES = maxpages; | |
00a7d0d2 SK |
571 | warnx(_("warning: truncating swap area to %llu KiB"), |
572 | PAGES * pagesize / 1024); | |
726f69e2 | 573 | } |
5c36a0eb | 574 | |
dceb1f22 PU |
575 | if (is_mounted(device_name)) |
576 | errx(EXIT_FAILURE, _("error: " | |
4e096801 | 577 | "%s is mounted; will not make swapspace"), |
dceb1f22 PU |
578 | device_name); |
579 | ||
20186299 MK |
580 | if (stat(device_name, &statbuf) < 0) { |
581 | perror(device_name); | |
a4d3e778 | 582 | exit(EXIT_FAILURE); |
20186299 MK |
583 | } |
584 | if (S_ISBLK(statbuf.st_mode)) | |
585 | DEV = open(device_name, O_RDWR | O_EXCL); | |
586 | else | |
587 | DEV = open(device_name, O_RDWR); | |
588 | ||
589 | if (DEV < 0) { | |
6dbe3af9 | 590 | perror(device_name); |
a4d3e778 | 591 | exit(EXIT_FAILURE); |
6dbe3af9 | 592 | } |
95f1bdee | 593 | |
6dbe3af9 KZ |
594 | if (!S_ISBLK(statbuf.st_mode)) |
595 | check=0; | |
95fa64da DB |
596 | else if (blkdev_is_misaligned(DEV)) |
597 | warnx(_("warning: %s is misaligned"), device_name); | |
5c36a0eb | 598 | |
4c85aa3a | 599 | if (check) |
5c36a0eb | 600 | check_blocks(); |
4c85aa3a | 601 | |
c1f1b301 | 602 | wipe_device(DEV, device_name, force); |
ff3bed80 | 603 | |
3e16599a KZ |
604 | hdr = (struct swap_header_v1_2 *) signature_page; |
605 | hdr->version = 1; | |
606 | hdr->last_page = PAGES - 1; | |
607 | hdr->nr_badpages = badpages; | |
5c36a0eb | 608 | |
99e6d525 | 609 | if (badpages > PAGES - MIN_GOODPAGES) |
00a7d0d2 | 610 | errx(EXIT_FAILURE, _("Unable to set up swap-space: unreadable")); |
4c85aa3a | 611 | |
99e6d525 | 612 | goodpages = PAGES - badpages - 1; |
4c85aa3a KZ |
613 | printf(_("Setting up swapspace version 1, size = %llu KiB\n"), |
614 | goodpages * pagesize / 1024); | |
5c36a0eb | 615 | |
4c85aa3a KZ |
616 | write_signature("SWAPSPACE2"); |
617 | write_uuid_and_label(uuid, opt_label); | |
756bfd01 | 618 | |
4c85aa3a | 619 | offset = 1024; |
5c36a0eb | 620 | if (lseek(DEV, offset, SEEK_SET) != offset) |
00a7d0d2 | 621 | errx(EXIT_FAILURE, _("unable to rewind swap-device")); |
db83e35c | 622 | if (write_all(DEV, (char *) signature_page + offset, |
00a7d0d2 SK |
623 | pagesize - offset) == -1) |
624 | err(EXIT_FAILURE, | |
625 | _("%s: unable to write signature page"), | |
626 | device_name); | |
5c36a0eb | 627 | |
3e18b040 | 628 | #ifdef HAVE_LIBSELINUX |
4ba66edf | 629 | if (S_ISREG(statbuf.st_mode) && is_selinux_enabled() > 0) { |
3e18b040 KZ |
630 | security_context_t context_string; |
631 | security_context_t oldcontext; | |
632 | context_t newcontext; | |
633 | ||
71bf7884 | 634 | if (fgetfilecon(DEV, &oldcontext) < 0) { |
00a7d0d2 SK |
635 | if (errno != ENODATA) |
636 | err(EXIT_FAILURE, | |
8d1b0fe2 | 637 | _("%s: unable to obtain selinux file label"), |
00a7d0d2 | 638 | device_name); |
71bf7884 | 639 | if (matchpathcon(device_name, statbuf.st_mode, &oldcontext)) |
00a7d0d2 | 640 | errx(EXIT_FAILURE, _("unable to matchpathcon()")); |
3e18b040 KZ |
641 | } |
642 | if (!(newcontext = context_new(oldcontext))) | |
00a7d0d2 | 643 | errx(EXIT_FAILURE, _("unable to create new selinux context")); |
3e18b040 | 644 | if (context_type_set(newcontext, SELINUX_SWAPFILE_TYPE)) |
00a7d0d2 | 645 | errx(EXIT_FAILURE, _("couldn't compute selinux context")); |
3e18b040 KZ |
646 | |
647 | context_string = context_str(newcontext); | |
648 | ||
649 | if (strcmp(context_string, oldcontext)!=0) { | |
00a7d0d2 SK |
650 | if (fsetfilecon(DEV, context_string)) |
651 | err(EXIT_FAILURE, _("unable to relabel %s to %s"), | |
652 | device_name, context_string); | |
3e18b040 KZ |
653 | } |
654 | context_free(newcontext); | |
655 | freecon(oldcontext); | |
656 | } | |
657 | #endif | |
833b7e0d SK |
658 | /* |
659 | * A subsequent swapon() will fail if the signature | |
660 | * is not actually on disk. (This is a kernel bug.) | |
661 | * The fsync() in close_fd() will take care of writing. | |
662 | */ | |
663 | if (close_fd(DEV) != 0) | |
664 | err(EXIT_FAILURE, _("write failed")); | |
a4d3e778 | 665 | return EXIT_SUCCESS; |
6dbe3af9 | 666 | } |