]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
mkswap: check for holes and unwanted extentd in file
authorKarel Zak <kzak@redhat.com>
Wed, 9 Sep 2020 09:00:40 +0000 (11:00 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 9 Sep 2020 09:10:51 +0000 (11:10 +0200)
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 <kzak@redhat.com>
configure.ac
disk-utils/mkswap.c

index 1e31ca3e23f4fe6a90a2ef0def6ba6263296baf3..9d4785c95c5383d1d7d9e5ba1d603c55265d4dbc 100644 (file)
@@ -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 \
index 4fb84153627845d2e62a05977774e6b96f92e7d8..06ca13bd1e4b5038fb159a64c696990c614307da 100644 (file)
 #include <limits.h>
 #include <sys/utsname.h>
 #include <sys/stat.h>
+#include <sys/ioctl.h>
 #include <errno.h>
 #include <getopt.h>
 #include <assert.h>
 #ifdef HAVE_LIBSELINUX
-#include <selinux/selinux.h>
-#include <selinux/context.h>
+# include <selinux/selinux.h>
+# include <selinux/context.h>
+#endif
+#ifdef HAVE_LINUX_FIEMAP_H
+# include <linux/fs.h>
+# include <linux/fiemap.h>
 #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);