From 195025c29e2ae7b1b203fb18d57509f675e924cd Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 9 Sep 2020 11:00:40 +0200 Subject: [PATCH] mkswap: check for holes and unwanted extentd in file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's make mkswap(8) more user-friend and report possible swapon issues already when user initialize a swap file. The extents check produces warnings, no exit with error. # dd if=/dev/zero of=img count=100 bs=1MiB # chmod 0600 img # fallocate --dig-hole --offset 64520192 --length 1MiB img   # fallocate --dig-hole --offset 84520960 --length 1MiB img  # ./mkswap img mkswap: extents check failed: - hole detected at offset 64520192 (size 1048576 bytes) - hole detected at offset 84520960 (size 1048576 bytes) file img can be rejected by kernel on swap activation. Setting up swapspace version 1, size = 100 MiB (104853504 bytes) no label, UUID=92091112-26b5-47aa-a02a-592e52528319 It checks for holes in the file, and for unknown, inline, shared and deallocated extents. Addresses: https://github.com/karelzak/util-linux/issues/1120 Signed-off-by: Karel Zak --- configure.ac | 1 + disk-utils/mkswap.c | 96 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 1e31ca3e2..9d4785c95 100644 --- a/configure.ac +++ b/configure.ac @@ -281,6 +281,7 @@ AC_CHECK_HEADERS([ \ linux/btrfs.h \ linux/cdrom.h \ linux/falloc.h \ + linux/fiemap.h \ linux/watchdog.h \ linux/fd.h \ linux/raw.h \ diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c index 4fb841536..06ca13bd1 100644 --- a/disk-utils/mkswap.c +++ b/disk-utils/mkswap.c @@ -16,12 +16,17 @@ #include #include #include +#include #include #include #include #ifdef HAVE_LIBSELINUX -#include -#include +# include +# include +#endif +#ifdef HAVE_LINUX_FIEMAP_H +# include +# include #endif #include "linux_version.h" @@ -209,6 +214,89 @@ static void check_blocks(struct mkswap_control *ctl) free(buffer); } + +#ifdef HAVE_LINUX_FIEMAP_H +static void check_extents_print_hdr(int *n) +{ + if (*n == 0) { + fputc('\n', stderr); + warnx(_("extents check failed:")); + } + ++*n; +} + +static void check_extents(struct mkswap_control *ctl) +{ + char buf[BUFSIZ] = { 0 }; + struct fiemap *fiemap = (struct fiemap *) buf; + int last = 0, nerrs = 0; + uint64_t last_logical = 0; + + memset(fiemap, 0, sizeof(struct fiemap)); + + do { + int rc; + size_t n, i; + + fiemap->fm_length = ~0ULL; + fiemap->fm_flags = 0; + fiemap->fm_extent_count = + (sizeof(buf) - sizeof(*fiemap)) / sizeof(struct fiemap_extent); + + rc = ioctl(ctl->fd, FS_IOC_FIEMAP, (unsigned long) fiemap); + if (rc < 0) { + warn(_("FIEMAP failed -- ignore extents check")); + return; + } + + n = fiemap->fm_mapped_extents; + + for (i = 0; i < n; i++) { + struct fiemap_extent *e = &fiemap->fm_extents[i]; + + if (e->fe_logical > last_logical) { + check_extents_print_hdr(&nerrs); + fprintf(stderr, (" - hole detected at offset %ju (size %ju bytes)\n"), + (uintmax_t) last_logical, + (uintmax_t) e->fe_logical - last_logical); + } + + last_logical = (e->fe_logical + e->fe_length); + + if (e->fe_flags & FIEMAP_EXTENT_LAST) + last = 1; + if (e->fe_flags & FIEMAP_EXTENT_UNKNOWN) { + check_extents_print_hdr(&nerrs); + fprintf(stderr, _(" - unknown file extent type at offset %ju\n"), + (uintmax_t) last_logical); + } + if (e->fe_flags & FIEMAP_EXTENT_DATA_INLINE){ + check_extents_print_hdr(&nerrs); + fprintf(stderr, _(" - data inline extent at offset %ju\n"), + (uintmax_t) last_logical); + } + if (e->fe_flags & FIEMAP_EXTENT_SHARED){ + check_extents_print_hdr(&nerrs); + fprintf(stderr, _(" - shared extent at offset %ju\n"), + (uintmax_t) last_logical); + } + if (e->fe_flags & FIEMAP_EXTENT_DELALLOC){ + check_extents_print_hdr(&nerrs); + fprintf(stderr, _(" - deallocated extent at offset %ju\n"), + (uintmax_t) last_logical); + } + + } + fiemap->fm_start = fiemap->fm_extents[n - 1].fe_logical + + fiemap->fm_extents[n - 1].fe_length; + } while (last == 0); + + if (nerrs) + fprintf(stderr, _("file %s can be rejected by kernel on swap activation.\n\n"), + ctl->devname); +} +#endif /* HAVE_LINUX_FIEMAP_H */ + /* return size in pages */ static unsigned long long get_size(const struct mkswap_control *ctl) { @@ -497,6 +585,10 @@ int main(int argc, char **argv) if (ctl.check) check_blocks(&ctl); +#ifdef HAVE_LINUX_FIEMAP_H + if (S_ISREG(ctl.devstat.st_mode)) + check_extents(&ctl); +#endif wipe_device(&ctl);