]>
Commit | Line | Data |
---|---|---|
6dbe3af9 | 1 | /* |
9e95aa12 KZ |
2 | * SPDX-License-Identifier: GPL-2.0-or-later |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
6dbe3af9 KZ |
9 | * mkswap.c - set up a linux swap device |
10 | * | |
8591859c KZ |
11 | * Copyright (C) 1991 Linus Torvalds |
12 | * 20.12.91 - time began. Got VM working yesterday by doing this by hand. | |
3e18b040 | 13 | * |
8591859c KZ |
14 | * Copyright (C) 1999 Jakub Jelinek <jj@ultra.linux.cz> |
15 | * Copyright (C) 2007-2014 Karel Zak <kzak@redhat.com> | |
6dbe3af9 KZ |
16 | */ |
17 | ||
18 | #include <stdio.h> | |
19 | #include <unistd.h> | |
20 | #include <string.h> | |
21 | #include <fcntl.h> | |
22 | #include <stdlib.h> | |
99e6d525 | 23 | #include <limits.h> |
5c36a0eb | 24 | #include <sys/utsname.h> |
6dbe3af9 | 25 | #include <sys/stat.h> |
195025c2 | 26 | #include <sys/ioctl.h> |
3e18b040 | 27 | #include <errno.h> |
e079c4e6 | 28 | #include <getopt.h> |
99f78758 | 29 | #include <assert.h> |
3e18b040 | 30 | #ifdef HAVE_LIBSELINUX |
195025c2 KZ |
31 | # include <selinux/selinux.h> |
32 | # include <selinux/context.h> | |
b105446e | 33 | # include "selinux-utils.h" |
195025c2 KZ |
34 | #endif |
35 | #ifdef HAVE_LINUX_FIEMAP_H | |
36 | # include <linux/fs.h> | |
37 | # include <linux/fiemap.h> | |
3e18b040 KZ |
38 | #endif |
39 | ||
8023c83b | 40 | #include "linux_version.h" |
756bfd01 | 41 | #include "swapheader.h" |
8abcf290 | 42 | #include "strutils.h" |
66ee8158 | 43 | #include "nls.h" |
54e377b3 | 44 | #include "blkdev.h" |
fc68cd49 | 45 | #include "pathnames.h" |
e12c9866 | 46 | #include "all-io.h" |
94a50e28 | 47 | #include "xalloc.h" |
08675263 | 48 | #include "c.h" |
45ca68ec | 49 | #include "closestream.h" |
def478cf | 50 | #include "ismounted.h" |
1f17eefb | 51 | #include "optutils.h" |
2a80192f | 52 | #include "bitops.h" |
66ee8158 | 53 | |
766dd757 | 54 | #ifdef HAVE_LIBUUID |
7ee96990 | 55 | # include <uuid.h> |
756bfd01 KZ |
56 | #endif |
57 | ||
64d15476 | 58 | #ifdef HAVE_LIBBLKID |
566f35bc KZ |
59 | # include <blkid.h> |
60 | #endif | |
61 | ||
de3822c3 | 62 | #define MIN_GOODPAGES 10 |
5c36a0eb | 63 | |
3e18b040 KZ |
64 | #define SELINUX_SWAPFILE_TYPE "swapfile_t" |
65 | ||
2a80192f TW |
66 | enum ENDIANNESS { |
67 | ENDIANNESS_NATIVE, | |
68 | ENDIANNESS_LITTLE, | |
69 | ENDIANNESS_BIG, | |
70 | }; | |
71 | ||
de3822c3 | 72 | struct mkswap_control { |
99f78758 | 73 | struct swap_header_v1_2 *hdr; /* swap header */ |
8591859c KZ |
74 | void *signature_page;/* buffer with swap header */ |
75 | ||
fabf29f4 | 76 | char *devname; /* device or file name */ |
b8671fe7 | 77 | const char *lockmode; /* as specified by --lock */ |
fabf29f4 KZ |
78 | struct stat devstat; /* stat() result */ |
79 | int fd; /* swap file descriptor */ | |
8591859c KZ |
80 | |
81 | unsigned long long npages; /* number of pages */ | |
82 | unsigned long nbadpages; /* number of bad pages */ | |
83 | ||
84 | int user_pagesize; /* --pagesize */ | |
85 | int pagesize; /* final pagesize used for the header */ | |
84ec6f99 | 86 | off_t offset; /* offset of the header in the target */ |
8591859c KZ |
87 | |
88 | char *opt_label; /* LABEL as specified on command line */ | |
89 | unsigned char *uuid; /* UUID parsed by libbuuid */ | |
90 | ||
7377b0d9 VP |
91 | unsigned long long filesz; /* desired swap file size */ |
92 | ||
701f0385 KZ |
93 | size_t nbad_extents; |
94 | ||
2a80192f TW |
95 | enum ENDIANNESS endianness; |
96 | ||
8591859c | 97 | unsigned int check:1, /* --check */ |
701f0385 | 98 | verbose:1, /* --verbose */ |
1f17eefb | 99 | quiet:1, /* --quiet */ |
7377b0d9 VP |
100 | force:1, /* --force */ |
101 | file:1; /* --file */ | |
de3822c3 | 102 | }; |
5c36a0eb | 103 | |
2a80192f TW |
104 | static uint32_t cpu32_to_endianness(uint32_t v, enum ENDIANNESS e) |
105 | { | |
106 | switch (e) { | |
107 | case ENDIANNESS_NATIVE: return v; | |
108 | case ENDIANNESS_LITTLE: return cpu_to_le32(v); | |
109 | case ENDIANNESS_BIG: return cpu_to_be32(v); | |
110 | } | |
111 | abort(); | |
112 | } | |
113 | ||
99f78758 | 114 | static void init_signature_page(struct mkswap_control *ctl) |
f2704664 | 115 | { |
076ba5a6 | 116 | const int kernel_pagesize = getpagesize(); |
eb63b9b8 | 117 | |
de3822c3 SK |
118 | if (ctl->user_pagesize) { |
119 | if (ctl->user_pagesize < 0 || !is_power_of_2(ctl->user_pagesize) || | |
120 | (size_t) ctl->user_pagesize < sizeof(struct swap_header_v1_2) + 10) | |
00a7d0d2 | 121 | errx(EXIT_FAILURE, |
076ba5a6 SK |
122 | _("Bad user-specified page size %u"), |
123 | ctl->user_pagesize); | |
1f17eefb | 124 | if (!ctl->quiet && ctl->user_pagesize != kernel_pagesize) |
076ba5a6 | 125 | warnx(_("Using user-specified page size %d, " |
00a7d0d2 | 126 | "instead of the system value %d"), |
4850e972 | 127 | ctl->user_pagesize, kernel_pagesize); |
076ba5a6 SK |
128 | ctl->pagesize = ctl->user_pagesize; |
129 | } else | |
130 | ctl->pagesize = kernel_pagesize; | |
99f78758 | 131 | |
fea1cbf7 | 132 | ctl->signature_page = xcalloc(1, ctl->pagesize); |
99f78758 KZ |
133 | ctl->hdr = (struct swap_header_v1_2 *) ctl->signature_page; |
134 | } | |
135 | ||
136 | static void deinit_signature_page(struct mkswap_control *ctl) | |
137 | { | |
138 | free(ctl->signature_page); | |
139 | ||
140 | ctl->hdr = NULL; | |
141 | ctl->signature_page = NULL; | |
5c36a0eb KZ |
142 | } |
143 | ||
3ba01c14 | 144 | static void set_signature(const struct mkswap_control *ctl) |
f2704664 | 145 | { |
de3822c3 | 146 | char *sp = (char *) ctl->signature_page; |
5c36a0eb | 147 | |
fabf29f4 | 148 | assert(sp); |
d2f265d6 | 149 | memcpy(sp + ctl->pagesize - SWAP_SIGNATURE_SZ, SWAP_SIGNATURE, SWAP_SIGNATURE_SZ); |
5c36a0eb KZ |
150 | } |
151 | ||
3ba01c14 | 152 | static void set_uuid_and_label(const struct mkswap_control *ctl) |
f2704664 | 153 | { |
99f78758 KZ |
154 | assert(ctl); |
155 | assert(ctl->hdr); | |
756bfd01 | 156 | |
99f78758 | 157 | /* set UUID */ |
de3822c3 | 158 | if (ctl->uuid) |
99f78758 KZ |
159 | memcpy(ctl->hdr->uuid, ctl->uuid, sizeof(ctl->hdr->uuid)); |
160 | ||
161 | /* set LABEL */ | |
de3822c3 | 162 | if (ctl->opt_label) { |
99f78758 KZ |
163 | xstrncpy(ctl->hdr->volume_name, |
164 | ctl->opt_label, sizeof(ctl->hdr->volume_name)); | |
1f17eefb KZ |
165 | if (!ctl->quiet |
166 | && strlen(ctl->opt_label) > strlen(ctl->hdr->volume_name)) | |
00a7d0d2 | 167 | warnx(_("Label was truncated.")); |
756bfd01 | 168 | } |
99f78758 | 169 | |
9e930041 | 170 | /* report results */ |
1f17eefb | 171 | if (!ctl->quiet && (ctl->uuid || ctl->opt_label)) { |
de3822c3 | 172 | if (ctl->opt_label) |
99f78758 | 173 | printf("LABEL=%s, ", ctl->hdr->volume_name); |
756bfd01 KZ |
174 | else |
175 | printf(_("no label, ")); | |
766dd757 | 176 | #ifdef HAVE_LIBUUID |
de3822c3 | 177 | if (ctl->uuid) { |
b443c177 | 178 | char uuid_string[UUID_STR_LEN]; |
de3822c3 | 179 | uuid_unparse(ctl->uuid, uuid_string); |
756bfd01 KZ |
180 | printf("UUID=%s\n", uuid_string); |
181 | } else | |
182 | #endif | |
183 | printf(_("no uuid\n")); | |
184 | } | |
185 | } | |
186 | ||
6e1eda6f | 187 | static void __attribute__((__noreturn__)) usage(void) |
e079c4e6 | 188 | { |
6e1eda6f | 189 | FILE *out = stdout; |
e270d980 KZ |
190 | |
191 | fputs(USAGE_HEADER, out); | |
192 | fprintf(out, _(" %s [options] device [size]\n"), program_invocation_short_name); | |
e079c4e6 | 193 | |
451dbcfa BS |
194 | fputs(USAGE_SEPARATOR, out); |
195 | fputs(_("Set up a Linux swap area.\n"), out); | |
196 | ||
e270d980 KZ |
197 | fputs(USAGE_OPTIONS, out); |
198 | fputs(_(" -c, --check check bad blocks before creating the swap area\n"), out); | |
199 | fputs(_(" -f, --force allow swap size area be larger than device\n"), out); | |
1f17eefb | 200 | fputs(_(" -q, --quiet suppress output and warning messages\n"), out); |
e270d980 KZ |
201 | fputs(_(" -p, --pagesize SIZE specify page size in bytes\n"), out); |
202 | fputs(_(" -L, --label LABEL specify label\n"), out); | |
203 | fputs(_(" -v, --swapversion NUM specify swap-space version number\n"), out); | |
204 | fputs(_(" -U, --uuid UUID specify the uuid to use\n"), out); | |
2a80192f TW |
205 | fprintf(out, |
206 | _(" -e, --endianness=<value> specify the endianness to use " | |
207 | "(%s, %s or %s)\n"), "native", "little", "big"); | |
84ec6f99 | 208 | fputs(_(" -o, --offset OFFSET specify the offset in the device\n"), out); |
7377b0d9 VP |
209 | fputs(_(" -s, --size SIZE specify the size of a swap file in bytes\n"), out); |
210 | fputs(_(" -F, --file create a swap file\n"), out); | |
701f0385 | 211 | fputs(_(" --verbose verbose output\n"), out); |
e270d980 | 212 | |
b8671fe7 KZ |
213 | fprintf(out, |
214 | _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock"); | |
54ef08ed | 215 | |
bad4c729 | 216 | fprintf(out, USAGE_HELP_OPTIONS(27)); |
e079c4e6 | 217 | |
bad4c729 | 218 | fprintf(out, USAGE_MAN_TAIL("mkswap(8)")); |
6e1eda6f | 219 | exit(EXIT_SUCCESS); |
eb63b9b8 | 220 | } |
6dbe3af9 | 221 | |
99f78758 | 222 | static void page_bad(struct mkswap_control *ctl, unsigned int page) |
f2704664 | 223 | { |
99f78758 KZ |
224 | const unsigned long max_badpages = |
225 | (ctl->pagesize - 1024 - 128 * sizeof(int) - 10) / sizeof(int); | |
3e16599a | 226 | |
8591859c | 227 | if (ctl->nbadpages == max_badpages) |
d68f402c | 228 | errx(EXIT_FAILURE, _("too many bad pages: %lu"), max_badpages); |
99f78758 KZ |
229 | |
230 | ctl->hdr->badpages[ctl->nbadpages] = page; | |
8591859c | 231 | ctl->nbadpages++; |
5c36a0eb KZ |
232 | } |
233 | ||
99f78758 | 234 | static void check_blocks(struct mkswap_control *ctl) |
f2704664 | 235 | { |
d68f402c | 236 | unsigned int current_page = 0; |
6dbe3af9 | 237 | int do_seek = 1; |
5c36a0eb | 238 | char *buffer; |
6dbe3af9 | 239 | |
99f78758 KZ |
240 | assert(ctl); |
241 | assert(ctl->fd > -1); | |
242 | ||
de3822c3 | 243 | buffer = xmalloc(ctl->pagesize); |
8591859c | 244 | while (current_page < ctl->npages) { |
f3b16286 | 245 | ssize_t rc; |
a6a24f18 | 246 | off_t offset = (off_t) current_page * ctl->pagesize; |
f3b16286 | 247 | |
a6a24f18 | 248 | if (do_seek && lseek(ctl->fd, offset, SEEK_SET) != offset) |
00a7d0d2 | 249 | errx(EXIT_FAILURE, _("seek failed in check_blocks")); |
f3b16286 | 250 | |
de3822c3 SK |
251 | rc = read(ctl->fd, buffer, ctl->pagesize); |
252 | do_seek = (rc < 0 || rc != ctl->pagesize); | |
f3b16286 | 253 | if (do_seek) |
de3822c3 | 254 | page_bad(ctl, current_page); |
adda7f7e | 255 | current_page++; |
6dbe3af9 | 256 | } |
1f17eefb KZ |
257 | |
258 | if (!ctl->quiet) | |
259 | printf(P_("%lu bad page\n", "%lu bad pages\n", ctl->nbadpages), ctl->nbadpages); | |
3216beb0 | 260 | free(buffer); |
6dbe3af9 KZ |
261 | } |
262 | ||
195025c2 KZ |
263 | |
264 | #ifdef HAVE_LINUX_FIEMAP_H | |
701f0385 | 265 | static void warn_extent(struct mkswap_control *ctl, const char *msg, uint64_t off) |
195025c2 | 266 | { |
701f0385 KZ |
267 | if (ctl->nbad_extents == 0) { |
268 | fputc('\n', stderr); | |
269 | fprintf(stderr, _( | |
270 | ||
271 | "mkswap: %s contains holes or other unsupported extents.\n" | |
272 | " This swap file can be rejected by kernel on swap activation!\n"), | |
273 | ctl->devname); | |
274 | ||
275 | if (ctl->verbose) | |
276 | fputc('\n', stderr); | |
277 | else | |
278 | fprintf(stderr, _( | |
279 | " Use --verbose for more details.\n")); | |
280 | ||
281 | } | |
282 | if (ctl->verbose) { | |
283 | fputs(" - ", stderr); | |
284 | fprintf(stderr, msg, off); | |
195025c2 | 285 | fputc('\n', stderr); |
195025c2 | 286 | } |
701f0385 | 287 | ctl->nbad_extents++; |
195025c2 KZ |
288 | } |
289 | ||
290 | static void check_extents(struct mkswap_control *ctl) | |
291 | { | |
292 | char buf[BUFSIZ] = { 0 }; | |
293 | struct fiemap *fiemap = (struct fiemap *) buf; | |
701f0385 | 294 | int last = 0; |
195025c2 KZ |
295 | uint64_t last_logical = 0; |
296 | ||
297 | memset(fiemap, 0, sizeof(struct fiemap)); | |
298 | ||
299 | do { | |
300 | int rc; | |
301 | size_t n, i; | |
302 | ||
303 | fiemap->fm_length = ~0ULL; | |
29b68b9c | 304 | fiemap->fm_flags = FIEMAP_FLAG_SYNC; |
195025c2 KZ |
305 | fiemap->fm_extent_count = |
306 | (sizeof(buf) - sizeof(*fiemap)) / sizeof(struct fiemap_extent); | |
307 | ||
308 | rc = ioctl(ctl->fd, FS_IOC_FIEMAP, (unsigned long) fiemap); | |
199cd674 | 309 | if (rc < 0) |
195025c2 | 310 | return; |
195025c2 KZ |
311 | |
312 | n = fiemap->fm_mapped_extents; | |
8a3a7416 KZ |
313 | if (n == 0) |
314 | break; | |
195025c2 KZ |
315 | |
316 | for (i = 0; i < n; i++) { | |
317 | struct fiemap_extent *e = &fiemap->fm_extents[i]; | |
318 | ||
701f0385 KZ |
319 | if (e->fe_logical > last_logical) |
320 | warn_extent(ctl, _("hole detected at offset %ju"), | |
321 | (uintmax_t) last_logical); | |
195025c2 KZ |
322 | |
323 | last_logical = (e->fe_logical + e->fe_length); | |
324 | ||
325 | if (e->fe_flags & FIEMAP_EXTENT_LAST) | |
326 | last = 1; | |
701f0385 KZ |
327 | if (e->fe_flags & FIEMAP_EXTENT_DATA_INLINE) |
328 | warn_extent(ctl, _("data inline extent at offset %ju"), | |
29b68b9c | 329 | (uintmax_t) e->fe_logical); |
701f0385 | 330 | if (e->fe_flags & FIEMAP_EXTENT_SHARED) |
1f17eefb | 331 | warn_extent(ctl, _("shared extent at offset %ju"), |
29b68b9c | 332 | (uintmax_t) e->fe_logical); |
701f0385 KZ |
333 | if (e->fe_flags & FIEMAP_EXTENT_DELALLOC) |
334 | warn_extent(ctl, _("unallocated extent at offset %ju"), | |
29b68b9c | 335 | (uintmax_t) e->fe_logical); |
195025c2 | 336 | |
701f0385 KZ |
337 | if (!ctl->verbose && ctl->nbad_extents) |
338 | goto done; | |
195025c2 KZ |
339 | } |
340 | fiemap->fm_start = fiemap->fm_extents[n - 1].fe_logical | |
341 | + fiemap->fm_extents[n - 1].fe_length; | |
342 | } while (last == 0); | |
343 | ||
701f0385 KZ |
344 | if (last_logical < (uint64_t) ctl->devstat.st_size) |
345 | warn_extent(ctl, _("hole detected at offset %ju"), | |
346 | (uintmax_t) last_logical); | |
347 | done: | |
348 | if (ctl->nbad_extents) | |
349 | fputc('\n', stderr); | |
195025c2 KZ |
350 | } |
351 | #endif /* HAVE_LINUX_FIEMAP_H */ | |
352 | ||
99e6d525 | 353 | /* return size in pages */ |
fabf29f4 | 354 | static unsigned long long get_size(const struct mkswap_control *ctl) |
f2704664 | 355 | { |
54e377b3 | 356 | unsigned long long size; |
6dbe3af9 | 357 | |
7377b0d9 VP |
358 | if (ctl->file && ctl->filesz) |
359 | size = ctl->filesz; | |
360 | else { | |
361 | int fd = open(ctl->devname, O_RDONLY); | |
362 | if (fd < 0) | |
363 | err(EXIT_FAILURE, _("cannot open %s"), ctl->devname); | |
364 | if (blkdev_get_size(fd, &size) < 0) | |
365 | err(EXIT_FAILURE, _("cannot determine size of %s"), ctl->devname); | |
366 | if ((unsigned long long) ctl->offset > size) | |
367 | errx(EXIT_FAILURE, _("offset larger than file size")); | |
368 | close(fd); | |
369 | } | |
84ec6f99 | 370 | size -= ctl->offset; |
6c88722c | 371 | size /= ctl->pagesize; |
6dbe3af9 KZ |
372 | return size; |
373 | } | |
374 | ||
979f1dd5 | 375 | #ifdef HAVE_LIBBLKID |
fabf29f4 | 376 | static blkid_probe new_prober(const struct mkswap_control *ctl) |
979f1dd5 KZ |
377 | { |
378 | blkid_probe pr = blkid_new_probe(); | |
379 | if (!pr) | |
380 | errx(EXIT_FAILURE, _("unable to alloc new libblkid probe")); | |
6af18227 | 381 | if (blkid_probe_set_device(pr, ctl->fd, 0, 0)) |
979f1dd5 KZ |
382 | errx(EXIT_FAILURE, _("unable to assign device to libblkid probe")); |
383 | return pr; | |
384 | } | |
385 | #endif | |
386 | ||
fabf29f4 KZ |
387 | static void open_device(struct mkswap_control *ctl) |
388 | { | |
389 | assert(ctl); | |
390 | assert(ctl->devname); | |
391 | ||
7377b0d9 VP |
392 | if (ctl->file) { |
393 | if (stat(ctl->devname, &ctl->devstat) == 0) { | |
394 | if (!S_ISREG(ctl->devstat.st_mode)) | |
395 | err(EXIT_FAILURE, _("cannot create swap file %s: node isn't regular file"), ctl->devname); | |
396 | if (chmod(ctl->devname, 0600) < 9) | |
397 | err(EXIT_FAILURE, _("cannot set permissions on swap file %s"), ctl->devname); | |
398 | } | |
399 | ctl->fd = open(ctl->devname, O_RDWR | O_CREAT, 0600); | |
400 | } else { | |
401 | if (stat(ctl->devname, &ctl->devstat) < 0) | |
402 | err(EXIT_FAILURE, _("stat of %s failed"), ctl->devname); | |
403 | ctl->fd = open_blkdev_or_file(&ctl->devstat, ctl->devname, O_RDWR); | |
404 | } | |
fabf29f4 KZ |
405 | if (ctl->fd < 0) |
406 | err(EXIT_FAILURE, _("cannot open %s"), ctl->devname); | |
7377b0d9 VP |
407 | if (ctl->file && ctl->filesz) { |
408 | if (ftruncate(ctl->fd, ctl->filesz) < 0) | |
409 | err(EXIT_FAILURE, _("couldn't allocate swap file %s"), ctl->devname); | |
410 | #ifdef HAVE_POSIX_FALLOCATE | |
411 | errno = posix_fallocate(ctl->fd, 0, ctl->filesz); | |
412 | if (errno) | |
413 | err(EXIT_FAILURE, _("couldn't allocate swap file %s"), ctl->devname); | |
414 | #elif defined(HAVE_FALLOCATE) | |
415 | if (fallocate(ctl->fd, 0, 0, ctl->filesz) < 0) | |
416 | err(EXIT_FAILURE, _("couldn't allocate swap file %s"), ctl->devname); | |
417 | #endif | |
418 | } | |
b8671fe7 KZ |
419 | |
420 | if (blkdev_lock(ctl->fd, ctl->devname, ctl->lockmode) != 0) | |
421 | exit(EXIT_FAILURE); | |
422 | ||
80c320fa SK |
423 | if (ctl->check && S_ISREG(ctl->devstat.st_mode)) { |
424 | ctl->check = 0; | |
1f17eefb KZ |
425 | if (!ctl->quiet) |
426 | warnx(_("warning: checking bad blocks from swap file is not supported: %s"), | |
427 | ctl->devname); | |
80c320fa | 428 | } |
fabf29f4 KZ |
429 | } |
430 | ||
431 | static void wipe_device(struct mkswap_control *ctl) | |
ff3bed80 | 432 | { |
566f35bc | 433 | char *type = NULL; |
ff3bed80 | 434 | int zap = 1; |
9206b238 | 435 | #ifdef HAVE_LIBBLKID |
979f1dd5 | 436 | blkid_probe pr = NULL; |
3773bb15 | 437 | const char *v = NULL; |
9206b238 | 438 | #endif |
6af18227 SK |
439 | if (!ctl->force) { |
440 | if (lseek(ctl->fd, 0, SEEK_SET) != 0) | |
00a7d0d2 | 441 | errx(EXIT_FAILURE, _("unable to rewind swap-device")); |
ff3bed80 | 442 | |
64d15476 | 443 | #ifdef HAVE_LIBBLKID |
6af18227 | 444 | pr = new_prober(ctl); |
c1f1b301 MB |
445 | blkid_probe_enable_partitions(pr, 1); |
446 | blkid_probe_enable_superblocks(pr, 0); | |
447 | ||
448 | if (blkid_do_fullprobe(pr) == 0 && | |
f11157e8 KZ |
449 | blkid_probe_lookup_value(pr, "PTTYPE", &v, NULL) == 0 && v) { |
450 | type = xstrdup(v); | |
ff3bed80 | 451 | zap = 0; |
566f35bc | 452 | } |
c1f1b301 MB |
453 | #else |
454 | /* don't zap if compiled without libblkid */ | |
455 | zap = 0; | |
456 | #endif | |
ff3bed80 KZ |
457 | } |
458 | ||
459 | if (zap) { | |
979f1dd5 | 460 | /* |
d68f402c | 461 | * Wipe bootbits |
979f1dd5 | 462 | */ |
d68f402c | 463 | char buf[1024] = { '\0' }; |
ff3bed80 | 464 | |
6af18227 | 465 | if (lseek(ctl->fd, 0, SEEK_SET) != 0) |
00a7d0d2 | 466 | errx(EXIT_FAILURE, _("unable to rewind swap-device")); |
ff3bed80 | 467 | |
6af18227 | 468 | if (write_all(ctl->fd, buf, sizeof(buf))) |
00a7d0d2 | 469 | errx(EXIT_FAILURE, _("unable to erase bootbits sectors")); |
979f1dd5 KZ |
470 | #ifdef HAVE_LIBBLKID |
471 | /* | |
472 | * Wipe rest of the device | |
473 | */ | |
474 | if (!pr) | |
6af18227 | 475 | pr = new_prober(ctl); |
979f1dd5 KZ |
476 | |
477 | blkid_probe_enable_superblocks(pr, 1); | |
478 | blkid_probe_enable_partitions(pr, 0); | |
c1f1b301 | 479 | blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_TYPE); |
979f1dd5 | 480 | |
c1f1b301 | 481 | while (blkid_do_probe(pr) == 0) { |
a0b42dc3 KZ |
482 | const char *data = NULL; |
483 | ||
1f17eefb KZ |
484 | if (!ctl->quiet |
485 | && blkid_probe_lookup_value(pr, "TYPE", &data, NULL) == 0 && data) | |
fabf29f4 | 486 | warnx(_("%s: warning: wiping old %s signature."), ctl->devname, data); |
979f1dd5 | 487 | blkid_do_wipe(pr, 0); |
c1f1b301 | 488 | } |
979f1dd5 | 489 | #endif |
1f17eefb | 490 | } else if (!ctl->quiet) { |
979f1dd5 | 491 | warnx(_("%s: warning: don't erase bootbits sectors"), |
fabf29f4 | 492 | ctl->devname); |
979f1dd5 KZ |
493 | if (type) |
494 | fprintf(stderr, _(" (%s partition table detected). "), type); | |
979f1dd5 KZ |
495 | else |
496 | fprintf(stderr, _(" (compiled without libblkid). ")); | |
8c219bf4 | 497 | fprintf(stderr, _("Use -f to force.\n")); |
ff3bed80 | 498 | } |
acec6eec | 499 | free(type); |
979f1dd5 KZ |
500 | #ifdef HAVE_LIBBLKID |
501 | blkid_free_probe(pr); | |
502 | #endif | |
ff3bed80 KZ |
503 | } |
504 | ||
3ba01c14 KZ |
505 | #define SIGNATURE_OFFSET 1024 |
506 | ||
507 | static void write_header_to_device(struct mkswap_control *ctl) | |
508 | { | |
84ec6f99 TW |
509 | off_t offset; |
510 | ||
3ba01c14 KZ |
511 | assert(ctl); |
512 | assert(ctl->fd > -1); | |
513 | assert(ctl->signature_page); | |
514 | ||
84ec6f99 TW |
515 | offset = SIGNATURE_OFFSET + ctl->offset; |
516 | ||
517 | if (lseek(ctl->fd, offset, SEEK_SET) != offset) | |
3ba01c14 KZ |
518 | errx(EXIT_FAILURE, _("unable to rewind swap-device")); |
519 | ||
520 | if (write_all(ctl->fd, (char *) ctl->signature_page + SIGNATURE_OFFSET, | |
521 | ctl->pagesize - SIGNATURE_OFFSET) == -1) | |
522 | err(EXIT_FAILURE, | |
523 | _("%s: unable to write signature page"), | |
524 | ctl->devname); | |
525 | } | |
526 | ||
9a83b03c KZ |
527 | int main(int argc, char **argv) |
528 | { | |
2a80192f | 529 | struct mkswap_control ctl = { .fd = -1, .endianness = ENDIANNESS_NATIVE }; |
cc706d9f | 530 | int c, permMask; |
9a83b03c | 531 | uint64_t sz; |
8a101b14 | 532 | int version = SWAP_VERSION; |
9a83b03c | 533 | char *block_count = NULL, *strsz = NULL; |
766dd757 | 534 | #ifdef HAVE_LIBUUID |
7b241808 | 535 | const char *opt_uuid = NULL; |
756bfd01 KZ |
536 | uuid_t uuid_dat; |
537 | #endif | |
b8671fe7 KZ |
538 | enum { |
539 | OPT_LOCK = CHAR_MAX + 1, | |
701f0385 | 540 | OPT_VERBOSE |
b8671fe7 | 541 | }; |
6c7d5ae9 | 542 | static const struct option longopts[] = { |
87918040 SK |
543 | { "check", no_argument, NULL, 'c' }, |
544 | { "force", no_argument, NULL, 'f' }, | |
1f17eefb | 545 | { "quiet", no_argument, NULL, 'q' }, |
87918040 SK |
546 | { "pagesize", required_argument, NULL, 'p' }, |
547 | { "label", required_argument, NULL, 'L' }, | |
548 | { "swapversion", required_argument, NULL, 'v' }, | |
549 | { "uuid", required_argument, NULL, 'U' }, | |
2a80192f | 550 | { "endianness", required_argument, NULL, 'e' }, |
84ec6f99 | 551 | { "offset", required_argument, NULL, 'o' }, |
7377b0d9 VP |
552 | { "size", required_argument, NULL, 's' }, |
553 | { "file", no_argument, NULL, 'F' }, | |
87918040 SK |
554 | { "version", no_argument, NULL, 'V' }, |
555 | { "help", no_argument, NULL, 'h' }, | |
b8671fe7 | 556 | { "lock", optional_argument, NULL, OPT_LOCK }, |
701f0385 | 557 | { "verbose", no_argument, NULL, OPT_VERBOSE }, |
87918040 | 558 | { NULL, 0, NULL, 0 } |
e079c4e6 | 559 | }; |
eb63b9b8 | 560 | |
1f17eefb KZ |
561 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
562 | { 'c', 'q' }, | |
563 | { 0 } | |
564 | }; | |
565 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
566 | ||
7eda085c KZ |
567 | setlocale(LC_ALL, ""); |
568 | bindtextdomain(PACKAGE, LOCALEDIR); | |
569 | textdomain(PACKAGE); | |
2c308875 | 570 | close_stdout_atexit(); |
5c36a0eb | 571 | |
7377b0d9 | 572 | while((c = getopt_long(argc, argv, "cfp:qL:v:U:e:o:s:FVh", longopts, NULL)) != -1) { |
1f17eefb KZ |
573 | |
574 | err_exclusive_options(c, longopts, excl, excl_st); | |
575 | ||
e079c4e6 SK |
576 | switch (c) { |
577 | case 'c': | |
de3822c3 | 578 | ctl.check = 1; |
e079c4e6 SK |
579 | break; |
580 | case 'f': | |
de3822c3 | 581 | ctl.force = 1; |
e079c4e6 SK |
582 | break; |
583 | case 'p': | |
de3822c3 | 584 | ctl.user_pagesize = strtou32_or_err(optarg, _("parsing page size failed")); |
e079c4e6 | 585 | break; |
1f17eefb KZ |
586 | case 'q': |
587 | ctl.quiet = 1; | |
588 | break; | |
e079c4e6 | 589 | case 'L': |
de3822c3 | 590 | ctl.opt_label = optarg; |
e079c4e6 SK |
591 | break; |
592 | case 'v': | |
8c219bf4 | 593 | version = strtos32_or_err(optarg, _("parsing version number failed")); |
de3822c3 SK |
594 | if (version != SWAP_VERSION) |
595 | errx(EXIT_FAILURE, | |
596 | _("swapspace version %d is not supported"), version); | |
e079c4e6 SK |
597 | break; |
598 | case 'U': | |
93bf0f28 | 599 | #ifdef HAVE_LIBUUID |
e079c4e6 | 600 | opt_uuid = optarg; |
93bf0f28 | 601 | #else |
4e096801 | 602 | warnx(_("warning: ignoring -U (UUIDs are unsupported by %s)"), |
e079c4e6 | 603 | program_invocation_short_name); |
93bf0f28 | 604 | #endif |
e079c4e6 | 605 | break; |
2a80192f TW |
606 | case 'e': |
607 | if (strcmp(optarg, "native") == 0) { | |
608 | ctl.endianness = ENDIANNESS_NATIVE; | |
609 | } else if (strcmp(optarg, "little") == 0) { | |
610 | ctl.endianness = ENDIANNESS_LITTLE; | |
611 | } else if (strcmp(optarg, "big") == 0) { | |
612 | ctl.endianness = ENDIANNESS_BIG; | |
613 | } else { | |
614 | errx(EXIT_FAILURE, | |
615 | _("invalid endianness %s is not supported"), optarg); | |
616 | } | |
617 | break; | |
84ec6f99 TW |
618 | case 'o': |
619 | ctl.offset = str2unum_or_err(optarg, | |
620 | 10, _("Invalid offset"), SINT_MAX(off_t)); | |
621 | break; | |
7377b0d9 VP |
622 | case 's': |
623 | ctl.filesz = strtosize_or_err(optarg, _("Invalid size")); | |
624 | break; | |
625 | case 'F': | |
626 | ctl.file = 1; | |
627 | break; | |
e079c4e6 | 628 | case 'V': |
2c308875 | 629 | print_version(EXIT_SUCCESS); |
b8671fe7 KZ |
630 | break; |
631 | case OPT_LOCK: | |
632 | ctl.lockmode = "1"; | |
633 | if (optarg) { | |
634 | if (*optarg == '=') | |
635 | optarg++; | |
636 | ctl.lockmode = optarg; | |
637 | } | |
638 | break; | |
701f0385 KZ |
639 | case OPT_VERBOSE: |
640 | ctl.verbose = 1; | |
641 | break; | |
e079c4e6 | 642 | case 'h': |
6e1eda6f | 643 | usage(); |
e079c4e6 | 644 | default: |
677ec86c | 645 | errtryhelp(EXIT_FAILURE); |
e079c4e6 SK |
646 | } |
647 | } | |
3ba01c14 | 648 | |
e079c4e6 | 649 | if (optind < argc) |
fabf29f4 | 650 | ctl.devname = argv[optind++]; |
e079c4e6 SK |
651 | if (optind < argc) |
652 | block_count = argv[optind++]; | |
653 | if (optind != argc) { | |
8c219bf4 | 654 | warnx(_("only one device argument is currently supported")); |
6e1eda6f | 655 | errtryhelp(EXIT_FAILURE); |
6dbe3af9 | 656 | } |
eb63b9b8 | 657 | |
766dd757 | 658 | #ifdef HAVE_LIBUUID |
93bf0f28 | 659 | if(opt_uuid) { |
54ef08ed KZ |
660 | if (strcmp(opt_uuid, "clear") == 0) |
661 | uuid_clear(uuid_dat); | |
662 | else if (strcmp(opt_uuid, "random") == 0) | |
663 | uuid_generate_random(uuid_dat); | |
664 | else if (strcmp(opt_uuid, "time") == 0) | |
665 | uuid_generate_time(uuid_dat); | |
666 | else if (uuid_parse(opt_uuid, uuid_dat) != 0) | |
8c219bf4 | 667 | errx(EXIT_FAILURE, _("error: parsing UUID failed")); |
93bf0f28 MS |
668 | } else |
669 | uuid_generate(uuid_dat); | |
de3822c3 | 670 | ctl.uuid = uuid_dat; |
756bfd01 KZ |
671 | #endif |
672 | ||
de3822c3 | 673 | init_signature_page(&ctl); /* get pagesize and allocate signature page */ |
eb63b9b8 | 674 | |
fabf29f4 | 675 | if (!ctl.devname) { |
00a7d0d2 | 676 | warnx(_("error: Nowhere to set up swap on?")); |
6e1eda6f | 677 | errtryhelp(EXIT_FAILURE); |
fd6b7a7f | 678 | } |
eb63b9b8 | 679 | if (block_count) { |
20543e61 | 680 | /* this silly user specified the number of blocks explicitly */ |
33fb5cfd KZ |
681 | uint64_t blks = strtou64_or_err(block_count, |
682 | _("invalid block count argument")); | |
8591859c | 683 | ctl.npages = blks / (ctl.pagesize / 1024); |
eb63b9b8 | 684 | } |
3ba01c14 | 685 | |
de3822c3 | 686 | sz = get_size(&ctl); |
8591859c KZ |
687 | if (!ctl.npages) |
688 | ctl.npages = sz; | |
689 | else if (ctl.npages > sz && !ctl.force) | |
00a7d0d2 SK |
690 | errx(EXIT_FAILURE, |
691 | _("error: " | |
fdbd7bb9 | 692 | "size %llu KiB is larger than device size %"PRIu64" KiB"), |
8591859c | 693 | ctl.npages * (ctl.pagesize / 1024), sz * (ctl.pagesize / 1024)); |
5c36a0eb | 694 | |
8591859c | 695 | if (ctl.npages < MIN_GOODPAGES) |
793a05f8 SK |
696 | errx(EXIT_FAILURE, |
697 | _("error: swap area needs to be at least %ld KiB"), | |
de3822c3 | 698 | (long)(MIN_GOODPAGES * ctl.pagesize / 1024)); |
8591859c | 699 | if (ctl.npages > UINT32_MAX) { |
a1466ab2 | 700 | /* true when swap is bigger than 17.59 terabytes */ |
8591859c | 701 | ctl.npages = UINT32_MAX; |
1f17eefb KZ |
702 | if (!ctl.quiet) |
703 | warnx(_("warning: truncating swap area to %llu KiB"), | |
704 | ctl.npages * ctl.pagesize / 1024); | |
726f69e2 | 705 | } |
5c36a0eb | 706 | |
fabf29f4 | 707 | if (is_mounted(ctl.devname)) |
dceb1f22 | 708 | errx(EXIT_FAILURE, _("error: " |
4e096801 | 709 | "%s is mounted; will not make swapspace"), |
fabf29f4 KZ |
710 | ctl.devname); |
711 | ||
712 | open_device(&ctl); | |
cc706d9f | 713 | permMask = S_ISBLK(ctl.devstat.st_mode) ? 07007 : 07077; |
1f17eefb | 714 | if (!ctl.quiet && (ctl.devstat.st_mode & permMask) != 0) |
8d687723 | 715 | warnx(_("%s: insecure permissions %04o, fix with: chmod %04o %s"), |
cc706d9f | 716 | ctl.devname, ctl.devstat.st_mode & 07777, |
8d687723 | 717 | ~permMask & 0666, ctl.devname); |
1f17eefb KZ |
718 | if (!ctl.quiet |
719 | && getuid() == 0 && S_ISREG(ctl.devstat.st_mode) && ctl.devstat.st_uid != 0) | |
8d687723 SK |
720 | warnx(_("%s: insecure file owner %d, fix with: chown 0:0 %s"), |
721 | ctl.devname, ctl.devstat.st_uid, ctl.devname); | |
cc706d9f | 722 | |
dceb1f22 | 723 | |
de3822c3 SK |
724 | if (ctl.check) |
725 | check_blocks(&ctl); | |
195025c2 | 726 | #ifdef HAVE_LINUX_FIEMAP_H |
1f17eefb | 727 | if (!ctl.quiet && S_ISREG(ctl.devstat.st_mode)) |
195025c2 KZ |
728 | check_extents(&ctl); |
729 | #endif | |
4c85aa3a | 730 | |
6af18227 | 731 | wipe_device(&ctl); |
ff3bed80 | 732 | |
99f78758 | 733 | assert(ctl.hdr); |
2a80192f TW |
734 | ctl.hdr->version = cpu32_to_endianness(version, ctl.endianness); |
735 | ctl.hdr->last_page = cpu32_to_endianness(ctl.npages - 1, ctl.endianness); | |
736 | ctl.hdr->nr_badpages = cpu32_to_endianness(ctl.nbadpages, ctl.endianness); | |
5c36a0eb | 737 | |
8591859c | 738 | if ((ctl.npages - MIN_GOODPAGES) < ctl.nbadpages) |
00a7d0d2 | 739 | errx(EXIT_FAILURE, _("Unable to set up swap-space: unreadable")); |
4c85aa3a | 740 | |
9a83b03c KZ |
741 | sz = (ctl.npages - ctl.nbadpages - 1) * ctl.pagesize; |
742 | strsz = size_to_human_string(SIZE_SUFFIX_SPACE | SIZE_SUFFIX_3LETTER, sz); | |
743 | ||
1f17eefb KZ |
744 | if (!ctl.quiet) |
745 | printf(_("Setting up swapspace version %d, size = %s (%"PRIu64" bytes)\n"), | |
746 | version, strsz, sz); | |
acec6eec | 747 | free(strsz); |
5c36a0eb | 748 | |
3ba01c14 KZ |
749 | set_signature(&ctl); |
750 | set_uuid_and_label(&ctl); | |
756bfd01 | 751 | |
3ba01c14 | 752 | write_header_to_device(&ctl); |
99f78758 KZ |
753 | |
754 | deinit_signature_page(&ctl); | |
755 | ||
3e18b040 | 756 | #ifdef HAVE_LIBSELINUX |
fabf29f4 | 757 | if (S_ISREG(ctl.devstat.st_mode) && is_selinux_enabled() > 0) { |
4bbc219b TW |
758 | const char *context_string; |
759 | char *oldcontext; | |
3e18b040 KZ |
760 | context_t newcontext; |
761 | ||
de3822c3 | 762 | if (fgetfilecon(ctl.fd, &oldcontext) < 0) { |
00a7d0d2 SK |
763 | if (errno != ENODATA) |
764 | err(EXIT_FAILURE, | |
8d1b0fe2 | 765 | _("%s: unable to obtain selinux file label"), |
fabf29f4 | 766 | ctl.devname); |
b105446e KZ |
767 | if (ul_selinux_get_default_context(ctl.devname, |
768 | ctl.devstat.st_mode, &oldcontext)) | |
769 | errx(EXIT_FAILURE, | |
770 | _("%s: unable to obtain default selinux file label"), | |
771 | ctl.devname); | |
3e18b040 KZ |
772 | } |
773 | if (!(newcontext = context_new(oldcontext))) | |
00a7d0d2 | 774 | errx(EXIT_FAILURE, _("unable to create new selinux context")); |
3e18b040 | 775 | if (context_type_set(newcontext, SELINUX_SWAPFILE_TYPE)) |
00a7d0d2 | 776 | errx(EXIT_FAILURE, _("couldn't compute selinux context")); |
3e18b040 KZ |
777 | |
778 | context_string = context_str(newcontext); | |
779 | ||
780 | if (strcmp(context_string, oldcontext)!=0) { | |
d97dc0ee | 781 | if (fsetfilecon(ctl.fd, context_string) && errno != ENOTSUP) |
00a7d0d2 | 782 | err(EXIT_FAILURE, _("unable to relabel %s to %s"), |
fabf29f4 | 783 | ctl.devname, context_string); |
3e18b040 KZ |
784 | } |
785 | context_free(newcontext); | |
786 | freecon(oldcontext); | |
787 | } | |
788 | #endif | |
833b7e0d SK |
789 | /* |
790 | * A subsequent swapon() will fail if the signature | |
791 | * is not actually on disk. (This is a kernel bug.) | |
792 | * The fsync() in close_fd() will take care of writing. | |
793 | */ | |
de3822c3 | 794 | if (close_fd(ctl.fd) != 0) |
833b7e0d | 795 | err(EXIT_FAILURE, _("write failed")); |
a4d3e778 | 796 | return EXIT_SUCCESS; |
6dbe3af9 | 797 | } |