mkswap: check for holes and unwanted extentd in file

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>
This commit is contained in:
Karel Zak 2020-09-09 11:00:40 +02:00
parent 933f095feb
commit 195025c29e
2 changed files with 95 additions and 2 deletions

View 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 \

View File

@ -16,12 +16,17 @@
#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);