]> git.sven.stormbind.net Git - sven/exfatprogs.git/commitdiff
New upstream version 1.3.1 upstream upstream/1.3.1
authorSven Hoexter <sven@stormbind.net>
Tue, 16 Dec 2025 12:32:11 +0000 (13:32 +0100)
committerSven Hoexter <sven@stormbind.net>
Tue, 16 Dec 2025 12:32:11 +0000 (13:32 +0100)
19 files changed:
Android.bp
Makefile.am
Makefile.in
NEWS
README.md
configure
configure.ac
defrag/Android.bp [deleted file]
defrag/Makefile.am [deleted file]
defrag/Makefile.in [deleted file]
defrag/defrag.c [deleted file]
defrag/defrag.h [deleted file]
fsck/fsck.c
fsck/repair.c
fsck/repair.h
include/version.h
lib/exfat_dir.c
manpages/defrag.exfat.8 [deleted file]
tests/bad_bitmap_size/exfat.img.tar.xz [new file with mode: 0644]

index 285abd1daacee1a96e9adeed2e91047f1733e696..d0e2594ec1acd70feb207af6c1f7988d01fbfd1b 100644 (file)
@@ -10,7 +10,6 @@ cc_library_headers {
         "label",
         "dump",
        "exfat2img",
-        "defrag",
     ],
 }
 
index a059009a30d2a8be236080cdfa57afd07c6d87b9..51c2c34dd28a378ce58a4f181f6d918b09f0abb7 100644 (file)
@@ -2,7 +2,7 @@
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = lib mkfs fsck tune label dump exfat2img defrag
+SUBDIRS = lib mkfs fsck tune label dump exfat2img
 
 # manpages
 dist_man8_MANS =               \
@@ -11,8 +11,7 @@ dist_man8_MANS =              \
        manpages/mkfs.exfat.8   \
        manpages/exfatlabel.8   \
        manpages/dump.exfat.8   \
-       manpages/exfat2img.8    \
-       manpages/defrag.exfat.8
+       manpages/exfat2img.8
 
 # other stuff
 EXTRA_DIST =                   \
@@ -26,5 +25,4 @@ EXTRA_DIST =                  \
        label/Android.bp        \
        dump/Android.bp         \
        exfat2img/Android.bp    \
-       defrag/Android.bp   \
        README.md
index b1d35d15cf1fa690f6e657490fbfbbf003d9fddc..1ca46c77ae4a05c14971259bbaf2eff2631fb12a 100644 (file)
@@ -196,8 +196,7 @@ am__DIST_COMMON = $(dist_man8_MANS) $(srcdir)/Makefile.in \
        $(top_srcdir)/build-aux/ltmain.sh \
        $(top_srcdir)/build-aux/missing COPYING NEWS README.md \
        build-aux/compile build-aux/config.guess build-aux/config.sub \
-       build-aux/depcomp build-aux/install-sh build-aux/ltmain.sh \
-       build-aux/missing
+       build-aux/install-sh build-aux/ltmain.sh build-aux/missing
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
@@ -362,7 +361,7 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = lib mkfs fsck tune label dump exfat2img defrag
+SUBDIRS = lib mkfs fsck tune label dump exfat2img
 
 # manpages
 dist_man8_MANS = \
@@ -371,8 +370,7 @@ dist_man8_MANS = \
        manpages/mkfs.exfat.8   \
        manpages/exfatlabel.8   \
        manpages/dump.exfat.8   \
-       manpages/exfat2img.8    \
-       manpages/defrag.exfat.8
+       manpages/exfat2img.8
 
 
 # other stuff
@@ -387,7 +385,6 @@ EXTRA_DIST = \
        label/Android.bp        \
        dump/Android.bp         \
        exfat2img/Android.bp    \
-       defrag/Android.bp   \
        README.md
 
 all: config.h
diff --git a/NEWS b/NEWS
index 82e7ff6c9770a395896e64163b00dd0493701d96..acb0b70fa22cd7e33906a5c24339da7858b1a1be 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+exfatprogs 1.3.1 - released 2025-12-15
+=====================================
+
+NEW FEATURES :
+ * fsck.exfat: support repairing the allocation bitmap size.
+
+CHANGES :
+ * exfatprogs: temporarily disable building defrag.exfat due to reported
+   data loss.
+
+BUG FIXES :
+ * libexfat: fix a NULL pointer dereference in read_file_dentry_set().
+
 exfatprogs 1.3.0 - released 2025-10-15
 ======================================
 
index f5d21df399167133d28ca726e65a4884cdcf08a0..d640c0691f99a765794a51774ab3b5c3c344e55b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -96,15 +96,6 @@ Usage example:
 Usage example:
     exfat2img -o sda1.dump /dev/sda1
 
-- defrag.exfat:
-  Defragment an exFAT-formatted device, or assess its fragmentation status.
-
-Usage examples:
-  1. Defragment an exFAT-formatted device:
-        defrag.exfat /dev/sda1
-  2. Assess fragmentation status of an exFAT-formatted device:
-        defrag.exfat -a /dev/sda1
-
 ```
 
 ## Benchmarks
index efcdc637b5f26954685244b739d9883ddae9b529..bd83be45ce4b5dd5478d0068c3b6104dacd2d850 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for exfatprogs 1.3.0.
+# Generated by GNU Autoconf 2.71 for exfatprogs 1.3.1.
 #
 # Report bugs to <linkinjeon@kernel.org>.
 #
@@ -621,8 +621,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='exfatprogs'
 PACKAGE_TARNAME='exfatprogs'
-PACKAGE_VERSION='1.3.0'
-PACKAGE_STRING='exfatprogs 1.3.0'
+PACKAGE_VERSION='1.3.1'
+PACKAGE_STRING='exfatprogs 1.3.1'
 PACKAGE_BUGREPORT='linkinjeon@kernel.org'
 PACKAGE_URL='https://github.com/exfatprogs/exfatprogs'
 
@@ -1352,7 +1352,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures exfatprogs 1.3.0 to adapt to many kinds of systems.
+\`configure' configures exfatprogs 1.3.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1423,7 +1423,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of exfatprogs 1.3.0:";;
+     short | recursive ) echo "Configuration of exfatprogs 1.3.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1535,7 +1535,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-exfatprogs configure 1.3.0
+exfatprogs configure 1.3.1
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1796,7 +1796,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by exfatprogs $as_me 1.3.0, which was
+It was created by exfatprogs $as_me 1.3.1, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3071,7 +3071,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='exfatprogs'
- VERSION='1.3.0'
+ VERSION='1.3.1'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -13181,7 +13181,7 @@ printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
  esac
 
 
-ac_config_files="$ac_config_files Makefile lib/Makefile mkfs/Makefile fsck/Makefile tune/Makefile label/Makefile dump/Makefile exfat2img/Makefile defrag/Makefile"
+ac_config_files="$ac_config_files Makefile lib/Makefile mkfs/Makefile fsck/Makefile tune/Makefile label/Makefile dump/Makefile exfat2img/Makefile"
 
 
 cat >confcache <<\_ACEOF
@@ -13708,7 +13708,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by exfatprogs $as_me 1.3.0, which was
+This file was extended by exfatprogs $as_me 1.3.1, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -13777,7 +13777,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-exfatprogs config.status 1.3.0
+exfatprogs config.status 1.3.1
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
@@ -14202,7 +14202,6 @@ do
     "label/Makefile") CONFIG_FILES="$CONFIG_FILES label/Makefile" ;;
     "dump/Makefile") CONFIG_FILES="$CONFIG_FILES dump/Makefile" ;;
     "exfat2img/Makefile") CONFIG_FILES="$CONFIG_FILES exfat2img/Makefile" ;;
-    "defrag/Makefile") CONFIG_FILES="$CONFIG_FILES defrag/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
index e693393b42cf925178e2ce411f7828a7fea6e5a8..a4bbe9dd4caca4023d17f0b598254fed5d3e4586 100644 (file)
@@ -32,7 +32,6 @@ AC_CONFIG_FILES([
        label/Makefile
        dump/Makefile
        exfat2img/Makefile
-       defrag/Makefile
 ])
 
 AC_OUTPUT
diff --git a/defrag/Android.bp b/defrag/Android.bp
deleted file mode 100644 (file)
index 1f26e6d..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2021 The Android Open Source Project
-
-cc_binary {
-    name: "defrag.exfat",
-
-    srcs: [
-        "defrag.c",
-    ],
-    defaults: ["exfatprogs-defaults"],
-    static_libs: ["libexfat"],
-}
diff --git a/defrag/Makefile.am b/defrag/Makefile.am
deleted file mode 100644 (file)
index 9323606..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-AM_CFLAGS = -Wall -Wextra -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common
-defrag_exfat_LDADD = $(top_builddir)/lib/libexfat.a
-
-sbin_PROGRAMS = defrag.exfat
-
-defrag_exfat_SOURCES = defrag.c defrag.h
diff --git a/defrag/Makefile.in b/defrag/Makefile.in
deleted file mode 100644 (file)
index 71f5e42..0000000
+++ /dev/null
@@ -1,642 +0,0 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
-
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-
-VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
-am__make_running_with_option = \
-  case $${target_option-} in \
-      ?) ;; \
-      *) echo "am__make_running_with_option: internal error: invalid" \
-              "target option '$${target_option-}' specified" >&2; \
-         exit 1;; \
-  esac; \
-  has_opt=no; \
-  sane_makeflags=$$MAKEFLAGS; \
-  if $(am__is_gnu_make); then \
-    sane_makeflags=$$MFLAGS; \
-  else \
-    case $$MAKEFLAGS in \
-      *\\[\ \  ]*) \
-        bs=\\; \
-        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
-          | sed "s/$$bs$$bs[$$bs $$bs  ]*//g"`;; \
-    esac; \
-  fi; \
-  skip_next=no; \
-  strip_trailopt () \
-  { \
-    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
-  }; \
-  for flg in $$sane_makeflags; do \
-    test $$skip_next = yes && { skip_next=no; continue; }; \
-    case $$flg in \
-      *=*|--*) continue;; \
-        -*I) strip_trailopt 'I'; skip_next=yes;; \
-      -*I?*) strip_trailopt 'I';; \
-        -*O) strip_trailopt 'O'; skip_next=yes;; \
-      -*O?*) strip_trailopt 'O';; \
-        -*l) strip_trailopt 'l'; skip_next=yes;; \
-      -*l?*) strip_trailopt 'l';; \
-      -[dEDm]) skip_next=yes;; \
-      -[JT]) skip_next=yes;; \
-    esac; \
-    case $$flg in \
-      *$$target_option*) has_opt=yes; break;; \
-    esac; \
-  done; \
-  test $$has_opt = yes
-am__make_dryrun = (target_option=n; $(am__make_running_with_option))
-am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
-pkgdatadir = $(datadir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-sbin_PROGRAMS = defrag.exfat$(EXEEXT)
-subdir = defrag
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
-       $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
-       $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
-       $(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
-       $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/config.h
-CONFIG_CLEAN_FILES =
-CONFIG_CLEAN_VPATH_FILES =
-am__installdirs = "$(DESTDIR)$(sbindir)"
-PROGRAMS = $(sbin_PROGRAMS)
-am_defrag_exfat_OBJECTS = defrag.$(OBJEXT)
-defrag_exfat_OBJECTS = $(am_defrag_exfat_OBJECTS)
-defrag_exfat_DEPENDENCIES = $(top_builddir)/lib/libexfat.a
-AM_V_lt = $(am__v_lt_@AM_V@)
-am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
-am__v_lt_0 = --silent
-am__v_lt_1 = 
-AM_V_P = $(am__v_P_@AM_V@)
-am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
-am__v_P_0 = false
-am__v_P_1 = :
-AM_V_GEN = $(am__v_GEN_@AM_V@)
-am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
-am__v_GEN_0 = @echo "  GEN     " $@;
-am__v_GEN_1 = 
-AM_V_at = $(am__v_at_@AM_V@)
-am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
-am__v_at_0 = @
-am__v_at_1 = 
-DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
-depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
-am__maybe_remake_depfiles = depfiles
-am__depfiles_remade = ./$(DEPDIR)/defrag.Po
-am__mv = mv -f
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
-       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-       $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
-       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
-       $(AM_CFLAGS) $(CFLAGS)
-AM_V_CC = $(am__v_CC_@AM_V@)
-am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
-am__v_CC_0 = @echo "  CC      " $@;
-am__v_CC_1 = 
-CCLD = $(CC)
-LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-       $(AM_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_CCLD = $(am__v_CCLD_@AM_V@)
-am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
-am__v_CCLD_0 = @echo "  CCLD    " $@;
-am__v_CCLD_1 = 
-SOURCES = $(defrag_exfat_SOURCES)
-DIST_SOURCES = $(defrag_exfat_SOURCES)
-am__can_run_installinfo = \
-  case $$AM_UPDATE_INFO_DIR in \
-    n|no|NO) false;; \
-    *) (install-info --version) >/dev/null 2>&1;; \
-  esac
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
-# Read a list of newline-separated strings from the standard input,
-# and print each of them once, without duplicates.  Input order is
-# *not* preserved.
-am__uniquify_input = $(AWK) '\
-  BEGIN { nonempty = 0; } \
-  { items[$$0] = 1; nonempty = 1; } \
-  END { if (nonempty) { for (i in items) print i; }; } \
-'
-# Make sure the list of sources is unique.  This is necessary because,
-# e.g., the same source file might be shared among _SOURCES variables
-# for different programs/libraries.
-am__define_uniq_tagged_files = \
-  list='$(am__tagged_files)'; \
-  unique=`for i in $$list; do \
-    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-  done | $(am__uniquify_input)`
-am__DIST_COMMON = $(srcdir)/Makefile.in \
-       $(top_srcdir)/build-aux/depcomp
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-ACLOCAL = @ACLOCAL@
-AMTAR = @AMTAR@
-AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
-AR = @AR@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPPFLAGS = @CPPFLAGS@
-CSCOPE = @CSCOPE@
-CTAGS = @CTAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-DLLTOOL = @DLLTOOL@
-DSYMUTIL = @DSYMUTIL@
-DUMPBIN = @DUMPBIN@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-ETAGS = @ETAGS@
-EXEEXT = @EXEEXT@
-FGREP = @FGREP@
-FILECMD = @FILECMD@
-GREP = @GREP@
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-LD = @LD@
-LDFLAGS = @LDFLAGS@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LIBTOOL = @LIBTOOL@
-LIPO = @LIPO@
-LN_S = @LN_S@
-LTLIBOBJS = @LTLIBOBJS@
-LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
-MAKEINFO = @MAKEINFO@
-MANIFEST_TOOL = @MANIFEST_TOOL@
-MKDIR_P = @MKDIR_P@
-NM = @NM@
-NMEDIT = @NMEDIT@
-OBJDUMP = @OBJDUMP@
-OBJEXT = @OBJEXT@
-OTOOL = @OTOOL@
-OTOOL64 = @OTOOL64@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_URL = @PACKAGE_URL@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-RANLIB = @RANLIB@
-SED = @SED@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-STRIP = @STRIP@
-VERSION = @VERSION@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_AR = @ac_ct_AR@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_vendor = @build_vendor@
-builddir = @builddir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-exec_prefix = @exec_prefix@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-install_sh = @install_sh@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-pdfdir = @pdfdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-runstatedir = @runstatedir@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-top_build_prefix = @top_build_prefix@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-AM_CFLAGS = -Wall -Wextra -include $(top_builddir)/config.h -I$(top_srcdir)/include -fno-common
-defrag_exfat_LDADD = $(top_builddir)/lib/libexfat.a
-defrag_exfat_SOURCES = defrag.c defrag.h
-all: all-am
-
-.SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
-       @for dep in $?; do \
-         case '$(am__configure_deps)' in \
-           *$$dep*) \
-             ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
-               && { if test -f $@; then exit 0; else break; fi; }; \
-             exit 1;; \
-         esac; \
-       done; \
-       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign defrag/Makefile'; \
-       $(am__cd) $(top_srcdir) && \
-         $(AUTOMAKE) --foreign defrag/Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
-       @case '$?' in \
-         *config.status*) \
-           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
-         *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
-       esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
-       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure:  $(am__configure_deps)
-       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
-       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(am__aclocal_m4_deps):
-install-sbinPROGRAMS: $(sbin_PROGRAMS)
-       @$(NORMAL_INSTALL)
-       @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
-       if test -n "$$list"; then \
-         echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
-         $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
-       fi; \
-       for p in $$list; do echo "$$p $$p"; done | \
-       sed 's/$(EXEEXT)$$//' | \
-       while read p p1; do if test -f $$p \
-        || test -f $$p1 \
-         ; then echo "$$p"; echo "$$p"; else :; fi; \
-       done | \
-       sed -e 'p;s,.*/,,;n;h' \
-           -e 's|.*|.|' \
-           -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
-       sed 'N;N;N;s,\n, ,g' | \
-       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
-         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
-           if ($$2 == $$4) files[d] = files[d] " " $$1; \
-           else { print "f", $$3 "/" $$4, $$1; } } \
-         END { for (d in files) print "f", d, files[d] }' | \
-       while read type dir files; do \
-           if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
-           test -z "$$files" || { \
-           echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
-           $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
-           } \
-       ; done
-
-uninstall-sbinPROGRAMS:
-       @$(NORMAL_UNINSTALL)
-       @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
-       files=`for p in $$list; do echo "$$p"; done | \
-         sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-             -e 's/$$/$(EXEEXT)/' \
-       `; \
-       test -n "$$list" || exit 0; \
-       echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
-       cd "$(DESTDIR)$(sbindir)" && rm -f $$files
-
-clean-sbinPROGRAMS:
-       @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
-       echo " rm -f" $$list; \
-       rm -f $$list || exit $$?; \
-       test -n "$(EXEEXT)" || exit 0; \
-       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
-       echo " rm -f" $$list; \
-       rm -f $$list
-
-defrag.exfat$(EXEEXT): $(defrag_exfat_OBJECTS) $(defrag_exfat_DEPENDENCIES) $(EXTRA_defrag_exfat_DEPENDENCIES) 
-       @rm -f defrag.exfat$(EXEEXT)
-       $(AM_V_CCLD)$(LINK) $(defrag_exfat_OBJECTS) $(defrag_exfat_LDADD) $(LIBS)
-
-mostlyclean-compile:
-       -rm -f *.$(OBJEXT)
-
-distclean-compile:
-       -rm -f *.tab.c
-
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defrag.Po@am__quote@ # am--include-marker
-
-$(am__depfiles_remade):
-       @$(MKDIR_P) $(@D)
-       @echo '# dummy' >$@-t && $(am__mv) $@-t $@
-
-am--depfiles: $(am__depfiles_remade)
-
-.c.o:
-@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
-@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
-
-.c.obj:
-@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
-@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
-@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-
-.c.lo:
-@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
-@am__fastdepCC_TRUE@   $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
-
-mostlyclean-libtool:
-       -rm -f *.lo
-
-clean-libtool:
-       -rm -rf .libs _libs
-
-ID: $(am__tagged_files)
-       $(am__define_uniq_tagged_files); mkid -fID $$unique
-tags: tags-am
-TAGS: tags
-
-tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
-       set x; \
-       here=`pwd`; \
-       $(am__define_uniq_tagged_files); \
-       shift; \
-       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
-         test -n "$$unique" || unique=$$empty_fix; \
-         if test $$# -gt 0; then \
-           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-             "$$@" $$unique; \
-         else \
-           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-             $$unique; \
-         fi; \
-       fi
-ctags: ctags-am
-
-CTAGS: ctags
-ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
-       $(am__define_uniq_tagged_files); \
-       test -z "$(CTAGS_ARGS)$$unique" \
-         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
-            $$unique
-
-GTAGS:
-       here=`$(am__cd) $(top_builddir) && pwd` \
-         && $(am__cd) $(top_srcdir) \
-         && gtags -i $(GTAGS_ARGS) "$$here"
-cscopelist: cscopelist-am
-
-cscopelist-am: $(am__tagged_files)
-       list='$(am__tagged_files)'; \
-       case "$(srcdir)" in \
-         [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
-         *) sdir=$(subdir)/$(srcdir) ;; \
-       esac; \
-       for i in $$list; do \
-         if test -f "$$i"; then \
-           echo "$(subdir)/$$i"; \
-         else \
-           echo "$$sdir/$$i"; \
-         fi; \
-       done >> $(top_builddir)/cscope.files
-
-distclean-tags:
-       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(BUILT_SOURCES)
-       $(MAKE) $(AM_MAKEFLAGS) distdir-am
-
-distdir-am: $(DISTFILES)
-       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-       list='$(DISTFILES)'; \
-         dist_files=`for file in $$list; do echo $$file; done | \
-         sed -e "s|^$$srcdirstrip/||;t" \
-             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
-       case $$dist_files in \
-         */*) $(MKDIR_P) `echo "$$dist_files" | \
-                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
-                          sort -u` ;; \
-       esac; \
-       for file in $$dist_files; do \
-         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-         if test -d $$d/$$file; then \
-           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
-           if test -d "$(distdir)/$$file"; then \
-             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-           fi; \
-           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
-             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-           fi; \
-           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
-         else \
-           test -f "$(distdir)/$$file" \
-           || cp -p $$d/$$file "$(distdir)/$$file" \
-           || exit 1; \
-         fi; \
-       done
-check-am: all-am
-check: check-am
-all-am: Makefile $(PROGRAMS)
-installdirs:
-       for dir in "$(DESTDIR)$(sbindir)"; do \
-         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
-       done
-install: install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
-       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
-       if test -z '$(STRIP)'; then \
-         $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-           install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-             install; \
-       else \
-         $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-           install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-           "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
-       fi
-mostlyclean-generic:
-
-clean-generic:
-
-distclean-generic:
-       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-
-maintainer-clean-generic:
-       @echo "This command is intended for maintainers to use"
-       @echo "it deletes files that may require special tools to rebuild."
-clean: clean-am
-
-clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
-       mostlyclean-am
-
-distclean: distclean-am
-               -rm -f ./$(DEPDIR)/defrag.Po
-       -rm -f Makefile
-distclean-am: clean-am distclean-compile distclean-generic \
-       distclean-tags
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-html-am:
-
-info: info-am
-
-info-am:
-
-install-data-am:
-
-install-dvi: install-dvi-am
-
-install-dvi-am:
-
-install-exec-am: install-sbinPROGRAMS
-
-install-html: install-html-am
-
-install-html-am:
-
-install-info: install-info-am
-
-install-info-am:
-
-install-man:
-
-install-pdf: install-pdf-am
-
-install-pdf-am:
-
-install-ps: install-ps-am
-
-install-ps-am:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
-               -rm -f ./$(DEPDIR)/defrag.Po
-       -rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-compile mostlyclean-generic \
-       mostlyclean-libtool
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am: uninstall-sbinPROGRAMS
-
-.MAKE: install-am install-strip
-
-.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
-       clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \
-       ctags ctags-am distclean distclean-compile distclean-generic \
-       distclean-libtool distclean-tags distdir dvi dvi-am html \
-       html-am info info-am install install-am install-data \
-       install-data-am install-dvi install-dvi-am install-exec \
-       install-exec-am install-html install-html-am install-info \
-       install-info-am install-man install-pdf install-pdf-am \
-       install-ps install-ps-am install-sbinPROGRAMS install-strip \
-       installcheck installcheck-am installdirs maintainer-clean \
-       maintainer-clean-generic mostlyclean mostlyclean-compile \
-       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-       tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS
-
-.PRECIOUS: Makefile
-
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/defrag/defrag.c b/defrag/defrag.c
deleted file mode 100644 (file)
index 9311cb7..0000000
+++ /dev/null
@@ -1,1175 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/*
- * exFAT Defragmentation Tool - defrag.exfat
- * Usage: defrag.exfat [-a] /dev/sdX
- * Effect: defragment or assess the entire exFAT-formated device
- * Feature: support safe interruption via Ctrl+C
- * Copyright (C) 2025 Haodong Xia <3394797836@qq.com>
- */
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <inttypes.h>
-#include <string.h>
-#include <errno.h>
-#include <signal.h>
-#include <getopt.h>
-#include <locale.h>
-#include <sys/wait.h>
-
-#include "defrag.h"
-#include "exfat_ondisk.h"
-#include "libexfat.h"
-#include "exfat_dir.h"
-#include "exfat_fs.h"
-
-/*----------------------- Part 0: Signal Handling ---------------------*/
-
-/* Global interrupt flag */
-static sig_atomic_t interrupt_received;
-
-/* Index at which interruption occurred */
-static uint32_t interrupt_point;
-
-/* Signal handler for SIGINT/SIGTERM */
-static void sigint_handler(int sig)
-{
-       (void)sig;                      /* Suppress unused parameter warning */
-       interrupt_received = 1;         /* Mark that an interrupt has occurred */
-}
-
-/*------------ Part 1: FAT, Bitmap, and Boot Checksum I/O -------------*/
-
-/*
- * Read or write the FAT table.
- * Returns 0 on success.
- */
-static int exfat_rw_FAT(struct exfat *exfat, uint32_t *list, uint32_t list_len, bool read)
-{
-       off_t offset;
-       size_t len_1, len_2;
-       uint32_t i;
-
-       offset = (off_t)le32_to_cpu(exfat->bs->bsx.fat_offset) << exfat->bs->bsx.sect_size_bits;
-       len_1 = (size_t)(list_len * sizeof(uint32_t));
-
-       /* Read or write with endianness conversion */
-       if (read) {
-               len_2 = exfat_read(exfat->blk_dev->dev_fd, list, len_1, offset);
-               if (len_2 != len_1)
-                       return -EIO;
-               for (i = 0; i < list_len; i++)
-                       list[i] = le32_to_cpu(list[i]);
-       } else {
-               for (i = 0; i < list_len; i++)
-                       list[i] = cpu_to_le32(list[i]);
-               len_2 = exfat_write(exfat->blk_dev->dev_fd, list, len_1, offset);
-               if (len_2 != len_1)
-                       return -EIO;
-       }
-
-       return 0;
-}
-
-/*
- * Read or write the allocation bitmap.
- * Returns 0 on success.
- */
-static int exfat_rw_bitmap(struct exfat *exfat, bool read)
-{
-       ssize_t rw_len = 0;
-
-       struct exfat_dentry *dentry;
-       struct exfat_lookup_filter filter = {
-               .in.type                = EXFAT_BITMAP,
-               .in.dentry_count        = 0,
-               .in.filter              = NULL,
-               .in.param               = NULL,
-       };
-
-       /* Locate the bitmap dentry in the root directory */
-       int retval = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
-
-       if (retval != 0)
-               return retval;
-
-       /* Validate bitmap size and starting cluster */
-       dentry = filter.out.dentry_set;
-       if (le64_to_cpu(dentry->bitmap_size) < DIV_ROUND_UP(exfat->clus_count, 8)) {
-               exfat_err("invalid size of alloc_bitmap. 0x%" PRIx64 "\n",
-                         le64_to_cpu(dentry->bitmap_size));
-               return -EINVAL;
-       }
-       if (!exfat_heap_clus(exfat, le32_to_cpu(dentry->bitmap_start_clu))) {
-               exfat_err("invalid start cluster of alloc_bitmap. 0x%x\n",
-                         le32_to_cpu(dentry->bitmap_start_clu));
-               return -EINVAL;
-       }
-
-       /* Record bitmap cluster and size */
-       exfat->disk_bitmap_clus = le32_to_cpu(dentry->bitmap_start_clu);
-       exfat->disk_bitmap_size = DIV_ROUND_UP(exfat->clus_count, 8);
-
-       free(filter.out.dentry_set);
-
-       /* Perform read or write operation */
-       if (read) {
-               rw_len = exfat_read(exfat->blk_dev->dev_fd, exfat->alloc_bitmap,
-                                   exfat->disk_bitmap_size,
-                                   exfat_c2o(exfat, exfat->disk_bitmap_clus));
-               if (rw_len != exfat->disk_bitmap_size)
-                       return -EIO;
-       } else {
-               rw_len = exfat_write(exfat->blk_dev->dev_fd, exfat->alloc_bitmap,
-                                    exfat->disk_bitmap_size,
-                                    exfat_c2o(exfat, exfat->disk_bitmap_clus));
-               if (rw_len != exfat->disk_bitmap_size)
-                       return -EIO;
-       }
-
-       return 0;
-}
-
-/*
- * From libexfat.c
- * Updates the boot sector checksum.
- */
-static int exfat_update_boot_checksum(struct exfat_blk_dev *bd, bool is_backup)
-{
-       unsigned int checksum = 0;
-       int ret, sec_idx, backup_sec_idx = 0;
-       unsigned char *buf;
-
-       buf = malloc(bd->sector_size);
-       if (!buf) {
-               exfat_err("Cannot allocate pbr: out of memory\n");
-               return -1;
-       }
-
-       if (is_backup)
-               backup_sec_idx = BACKUP_BOOT_SEC_IDX;
-
-       for (sec_idx = BOOT_SEC_IDX; sec_idx < CHECKSUM_SEC_IDX; sec_idx++) {
-               bool is_boot_sec = false;
-
-               ret = exfat_read_sector(bd, buf, sec_idx + backup_sec_idx);
-               if (ret < 0) {
-                       exfat_err("sector(%d) read failed\n", sec_idx);
-                       ret = -1;
-                       goto free_buf;
-               }
-
-               if (sec_idx == BOOT_SEC_IDX)
-                       is_boot_sec = true;
-
-               boot_calc_checksum(buf, bd->sector_size, is_boot_sec, &checksum);
-       }
-
-       ret = exfat_write_checksum_sector(bd, checksum, is_backup);
-
-free_buf:
-       free(buf);
-
-       return ret;
-}
-
-/*------------------- Part 2: Managing Node Queue --------------------*/
-
-/*
- * Adds a new virtual node to set->next_clus and sets its value to 'data'.
- * Returns 0 on success.
- */
-static int add_virtual_node(struct cluster_info_set *set, uint32_t data)
-{
-       uint32_t offset = set->num_phys_clus;
-       uint32_t index = set->num_clus_chain;
-       size_t new_size;
-       uint32_t *new_next_clus;
-
-       /* Allocate memory in chunks due to unknown total file count */
-       if (index % VIRT_SPACE_ALIGN == 0) {
-               new_size = (offset + index + VIRT_SPACE_ALIGN) * sizeof(uint32_t);
-               new_next_clus = realloc(set->next_clus, new_size);
-               if (new_next_clus == NULL) {
-                       exfat_err("memory run out in %s", __func__);
-                       return -ENOMEM;
-               }
-               set->next_clus = new_next_clus;
-       }
-
-       /* Set next_clus value and increment virtual cluster count */
-       set->next_clus[offset + index] = data;
-       set->num_clus_chain++;
-
-       return 0;
-}
-
-/*
- * Sets FAT entries for a contiguous cluster chain to ensure correctness.
- */
-static int set_physical_nodes(struct cluster_info_set *set,
-                                     uint32_t begin, uint32_t count, uint32_t data)
-{
-       uint32_t i;
-
-       /* avoid the risk of out of memory */
-       if (begin < EXFAT_FIRST_CLUSTER || begin + count >= set->num_phys_clus) {
-               exfat_err("%s: invalid parameter \"begin = %u, count = %u\"\n", __func__,
-                               begin, count);
-               return -1;
-       }
-
-       for (i = begin; i < begin + count; i++)
-               set->next_clus[i] = data++;
-       set->next_clus[begin + count] = EXFAT_EOF_CLUSTER;
-       return 0;
-}
-
-/*
- * Builds the 'prev_clus' array from the 'next_clus' links.
- */
-static void next_to_prev(struct cluster_info_set *set)
-{
-       uint32_t cur_clu, last_clu;
-       uint32_t n_clus = set->num_phys_clus;
-       uint32_t n_file = set->num_clus_chain;
-       uint32_t i;
-
-       for (i = n_clus; i < n_clus + n_file; i++) {
-               cur_clu = i;
-               last_clu = EXFAT_EOF_CLUSTER;
-               while (cur_clu != EXFAT_EOF_CLUSTER) {
-                       set->prev_clus[cur_clu] = last_clu;
-                       last_clu = cur_clu;
-                       cur_clu = set->next_clus[cur_clu];
-               }
-       }
-}
-
-/*--------- Part 3: File Tree Traversal (Directory Entry I/O) ---------*/
-
-/*
- * Implementation of BFS_read_file_tree.
- * Returns 0 on success.
- */
-static int BFS_read_children(struct exfat_defrag *defrag, struct exfat_inode *inode)
-{
-       int ret;
-       uint16_t attr, checksum;
-       uint32_t first_clu, dentry_count, i, FAT_entry_count;
-       uint64_t size;
-       bool is_contiguous;
-       struct exfat_de_iter de_iter;
-       struct exfat_dentry *first_dentry = NULL, *dentry = NULL;
-       struct exfat_inode *child_inode = NULL;
-       struct cluster_info_set *set = &(defrag->clu_info_set);
-
-       /* Initialize directory entry iterator */
-       ret = exfat_de_iter_init(&de_iter, defrag->exfat, inode, defrag->dentry_buffer);
-       if (ret == EOF)
-               return 0;
-       else if (ret != 0) {
-               exfat_err("fail in exfat_de_iter_init\n");
-               return -1;
-       }
-
-       while (1) {
-               checksum = 0;
-               dentry_count = 1;
-
-               /* Get first dentry of the set */
-               ret = exfat_de_iter_get(&de_iter, 0, &first_dentry);
-               if (ret == EOF)
-                       goto success;
-               else if (ret != 0) {
-                       exfat_err("fail in exfat_de_iter_get first\n");
-                       goto fail;
-               }
-
-               switch (first_dentry->type) {
-               case EXFAT_FILE:
-                       /* File dentry */
-                       dentry_count += first_dentry->file_num_ext;
-                       attr = le16_to_cpu(first_dentry->file_attr);
-
-                       /* Stream dentry */
-                       ret = exfat_de_iter_get(&de_iter, 1, &dentry);
-                       if (ret != 0 || dentry->type != EXFAT_STREAM) {
-                               exfat_err("fail in exfat_de_iter_get second\n");
-                               goto fail;
-                       }
-                       first_clu = le32_to_cpu(dentry->stream_start_clu);
-                       is_contiguous = dentry->stream_flags & EXFAT_SF_CONTIGUOUS;
-                       size = le64_to_cpu(dentry->stream_size);
-
-                       /* Do not process empty files/directories */
-                       if (size != 0) {
-
-                               if (!exfat_heap_clus(defrag->exfat, first_clu)) {
-                                       exfat_err("%s: invalid first_clu = %u\n", __func__,
-                                                 first_clu);
-                                       goto fail;
-                               }
-
-                               /* Record first cluster in virtual node list */
-                               ret = add_virtual_node(set, first_clu);
-                               if (ret != 0)
-                                       goto fail;
-
-                               /* Fix FAT entries: if contiguous, assign correct chain */
-                               if (is_contiguous) {
-                                       FAT_entry_count =
-                                               (uint32_t)((size - 1) / defrag->exfat->clus_size);
-                                       if (set_physical_nodes(set, first_clu,
-                                                              FAT_entry_count, first_clu + 1) < 0)
-                                               goto fail;
-                               }
-
-                               /* Allocate and update inode if it's a directory */
-                               if (attr & ATTR_SUBDIR) {
-                                       child_inode = exfat_alloc_inode(attr);
-                                       if (child_inode == NULL)
-                                               goto fail;
-                                       child_inode->parent = inode;
-                                       child_inode->dentry_count = dentry_count;
-                                       child_inode->first_clus = first_clu;
-                                       child_inode->is_contiguous = is_contiguous;
-                                       child_inode->size = size;
-                                       list_add_tail(&child_inode->list, &defrag->exfat->dir_list);
-                               }
-                       }
-
-                       exfat_calc_dentry_checksum(first_dentry, &checksum, true);
-                       exfat_calc_dentry_checksum(dentry, &checksum, false);
-
-                       /* Handle potential vendor_alloc dentry in the remaining dentries */
-                       for (i = 2; i < dentry_count; i++) {
-                               ret = exfat_de_iter_get(&de_iter, i, &dentry);
-                               if (ret != 0) {
-                                       exfat_err("%s: fail in exfat_de_iter_get extended\n",
-                                                 __func__);
-                                       goto fail;
-                               }
-                               exfat_calc_dentry_checksum(dentry, &checksum, false);
-
-                               if (dentry->type != EXFAT_VENDOR_ALLOC)
-                                       continue;
-
-                               /* handle vendor_alloc dentry */
-                               first_clu = le32_to_cpu(dentry->vendor_alloc_start_clu);
-                               is_contiguous = dentry->vendor_alloc_flags & EXFAT_SF_CONTIGUOUS;
-                               size = le64_to_cpu(dentry->vendor_alloc_size);
-
-                               if (size == 0)
-                                       continue;
-
-                               if (!exfat_heap_clus(defrag->exfat, first_clu)) {
-                                       exfat_err("%s: invalid first_clu = %u\n", __func__,
-                                                 first_clu);
-                                       goto fail;
-                               }
-
-                               ret = add_virtual_node(set, first_clu);
-                               if (ret != 0)
-                                       goto fail;
-
-                               if (is_contiguous) {
-                                       FAT_entry_count =
-                                               (uint32_t)((size - 1) / defrag->exfat->clus_size);
-                                       if (set_physical_nodes(set, first_clu,
-                                                              FAT_entry_count, first_clu + 1) < 0)
-                                               goto fail;
-                               }
-                       }
-
-                       if (first_dentry->file_checksum != checksum) {
-                               exfat_err("%s: invalid checksum, please run fsck.exfat to check and repair\n",
-                                         __func__);
-                               goto fail;
-                       }
-                       break;
-
-               case EXFAT_BITMAP:
-                       first_clu = le32_to_cpu(first_dentry->bitmap_start_clu);
-                       ret = add_virtual_node(set, first_clu);
-                       if (ret != 0)
-                               goto fail;
-
-                       size = le32_to_cpu(first_dentry->bitmap_size);
-                       FAT_entry_count = (uint32_t)((size - 1) / defrag->exfat->clus_size);
-                       if (set_physical_nodes(set, first_clu, FAT_entry_count, first_clu + 1) < 0)
-                               goto fail;
-                       break;
-
-               case EXFAT_UPCASE:
-                       first_clu = le32_to_cpu(first_dentry->upcase_start_clu);
-                       ret = add_virtual_node(set, first_clu);
-                       if (ret != 0)
-                               goto fail;
-
-                       size = le32_to_cpu(first_dentry->upcase_size);
-                       FAT_entry_count = (uint32_t)((size - 1) / defrag->exfat->clus_size);
-                       if (set_physical_nodes(set, first_clu, FAT_entry_count, first_clu + 1) < 0)
-                               goto fail;
-                       break;
-
-               case EXFAT_LAST:        /* End of valid entries */
-                       goto success;
-
-               default:
-                       break;
-               }
-
-               exfat_de_iter_advance(&de_iter, dentry_count);
-       }
-
-success:
-       exfat_de_iter_flush(&de_iter);
-       return 0;
-fail:
-       exfat_de_iter_flush(&de_iter);
-       return -1;
-}
-
-/*
- * BFS traversal to read directory entries and populate virtual head nodes in next_clus.
- */
-static int BFS_read_file_tree(struct exfat_defrag *defrag)
-{
-       struct exfat *exfat = defrag->exfat;
-       struct exfat_inode *parent;
-       struct cluster_info_set *set = &(defrag->clu_info_set);
-       uint32_t i, n_clus, n_chain, tmp_clu;
-
-       list_add(&exfat->root->list, &exfat->dir_list);
-       if (add_virtual_node(set, exfat->root->first_clus) < 0)
-               goto err;
-
-       while (!list_empty(&exfat->dir_list)) {
-               parent = list_entry(exfat->dir_list.next, struct exfat_inode, list);
-
-               /* All inodes in the queue should be directories */
-               if (!(parent->attr & ATTR_SUBDIR)) {
-                       exfat_err("%s: parent_inode is not dir\n", __func__);
-                       goto err;
-               }
-
-               /* Process children */
-               if (BFS_read_children(defrag, parent) < 0)
-                       goto err;
-
-               /* Remove parent after processing */
-               list_del(&parent->list);
-               if (parent != exfat->root)
-                       exfat_free_inode(parent);
-       }
-
-       /* Check the validation of every cluster chains */
-       n_clus = set->num_phys_clus;
-       n_chain = set->num_clus_chain;
-       for (i = n_clus; i < n_clus + n_chain; i++) {
-               tmp_clu = set->next_clus[i];
-               while (1) {
-                       if (!exfat_heap_clus(exfat, tmp_clu)) {
-                               exfat_err("%s: invalid fat entry = %u\n", __func__, tmp_clu);
-                               goto err;
-                       }
-                       if (!exfat_bitmap_get(exfat->alloc_bitmap, tmp_clu)) {
-                               exfat_err("%s: cluster %u should be alloced\n", __func__, tmp_clu);
-                               goto err;
-                       }
-                       tmp_clu = set->next_clus[tmp_clu];
-                       if (tmp_clu == EXFAT_EOF_CLUSTER)
-                               break;
-               }
-       }
-
-       return 0;
-
-err:
-       exfat_free_dir_list(exfat);
-       return -1;
-}
-
-/*
- * Implementation of BFS_write_file_tree.
- * Returns 0 on success.
- */
-static int BFS_write_children(struct exfat_defrag *defrag,
-               struct exfat_inode *inode, uint32_t *index)
-{
-       int ret = 0;
-       uint16_t attr, checksum;
-       uint32_t first_clu, dentry_count, i;
-       struct exfat_de_iter de_iter;
-       struct exfat_dentry *first_dentry = NULL, *dentry = NULL;
-       struct exfat_inode *child_inode = NULL;
-       struct cluster_info_set *set = &(defrag->clu_info_set);
-
-       /* Initialize directory entry iterator */
-       ret = exfat_de_iter_init(&de_iter, defrag->exfat, inode, defrag->dentry_buffer);
-       if (ret == EOF)
-               return 0;
-       else if (ret != 0) {
-               exfat_err("fail in exfat_de_iter_init\n");
-               return -1;
-       }
-
-       while (1) {
-               first_clu = set->next_clus[(*index)];
-               checksum = 0;
-               dentry_count = 1;
-
-               /* Get first dentry of the set */
-               ret = exfat_de_iter_get_dirty(&de_iter, 0, &first_dentry);
-               if (ret == EOF)
-                       goto success;
-               else if (ret != 0) {
-                       exfat_err("fail in exfat_de_iter_get first\n");
-                       goto fail;
-               }
-
-               switch (first_dentry->type) {
-               case EXFAT_FILE:
-                       /* File dentry */
-                       dentry_count += first_dentry->file_num_ext;
-                       attr = le16_to_cpu(first_dentry->file_attr);
-
-                       /* Stream dentry */
-                       ret = exfat_de_iter_get_dirty(&de_iter, 1, &dentry);
-                       if (ret != 0 || dentry->type != EXFAT_STREAM) {
-                               exfat_err("fail in exfat_de_iter_get second\n");
-                               goto fail;
-                       }
-
-                       /* Do not process empty files/directories */
-                       if (le64_to_cpu(dentry->stream_size) != 0) {
-
-                               /* Set start_clu + contiguous_flag */
-                               dentry->stream_start_clu = cpu_to_le32(first_clu);
-                               if ((*index) <= interrupt_point)
-                                       dentry->stream_flags |= EXFAT_SF_CONTIGUOUS;
-                               else
-                                       dentry->stream_flags &= ~EXFAT_SF_CONTIGUOUS;
-                               (*index)++;
-
-                               /* Allocate and update inode if it's a directory */
-                               if (attr & ATTR_SUBDIR) {
-                                       child_inode = exfat_alloc_inode(attr);
-                                       if (child_inode == NULL)
-                                               goto fail;
-                                       child_inode->parent = inode;
-                                       child_inode->dentry_count = dentry_count;
-                                       child_inode->first_clus = first_clu;
-                                       child_inode->is_contiguous = true;
-                                       child_inode->size = le64_to_cpu(dentry->stream_size);
-                                       list_add_tail(&child_inode->list, &defrag->exfat->dir_list);
-                               }
-                       }
-
-                       exfat_calc_dentry_checksum(first_dentry, &checksum, true);
-                       exfat_calc_dentry_checksum(dentry, &checksum, false);
-
-                       for (i = 2; i < dentry_count; i++) {
-                               ret = exfat_de_iter_get_dirty(&de_iter, i, &dentry);
-                               if (ret != 0) {
-                                       exfat_err("%s: fail in exfat_de_iter_get extended\n",
-                                                 __func__);
-                                       goto fail;
-                               }
-                               exfat_calc_dentry_checksum(dentry, &checksum, false);
-
-                               if (dentry->type == EXFAT_VENDOR_ALLOC) {
-                                       /* Set start_clu + contiguous_flag */
-                                       dentry->vendor_alloc_start_clu =
-                                               cpu_to_le32(set->next_clus[(*index)]);
-                                       if ((*index) <= interrupt_point)
-                                               dentry->vendor_alloc_flags |= EXFAT_SF_CONTIGUOUS;
-                                       else
-                                               dentry->vendor_alloc_flags &= ~EXFAT_SF_CONTIGUOUS;
-                                       (*index)++;
-                               }
-
-                       }
-
-                       first_dentry->file_checksum = cpu_to_le16(checksum);
-                       break;
-
-               case EXFAT_BITMAP:
-                       first_dentry->bitmap_start_clu = cpu_to_le32(first_clu);
-                       (*index)++;
-                       break;
-
-               case EXFAT_UPCASE:
-                       first_dentry->upcase_start_clu = cpu_to_le32(first_clu);
-                       (*index)++;
-                       break;
-
-               case EXFAT_LAST:
-                       goto success;
-
-               default:
-                       break;
-               }
-
-               exfat_de_iter_advance(&de_iter, dentry_count);
-       }
-
-success:
-       exfat_de_iter_flush(&de_iter);
-       return 0;
-fail:
-       exfat_de_iter_flush(&de_iter);
-       return -1;
-}
-
-/*
- * BFS traversal to write directory entries using virtual head node info in next_clus.
- */
-static int BFS_write_file_tree(struct exfat_defrag *defrag)
-{
-       struct exfat *exfat = defrag->exfat;
-       struct exfat_inode *parent;
-       struct cluster_info_set *set = &(defrag->clu_info_set);
-       uint32_t index = set->num_phys_clus;
-
-       /* Update root cluster in memory and on disk */
-       exfat->root->first_clus = set->next_clus[index++];
-       exfat->root->is_contiguous = true;
-       list_add(&exfat->root->list, &exfat->dir_list);
-
-       exfat->bs->bsx.root_cluster = exfat->root->first_clus;
-       if (exfat_write_sector(exfat->blk_dev, exfat->bs, BOOT_SEC_IDX) < 0) {
-               exfat_err("%s: fail in update boot sector\n", __func__);
-               goto err;
-       }
-       if (exfat_write_sector(exfat->blk_dev, exfat->bs, BACKUP_BOOT_SEC_IDX) < 0) {
-               exfat_err("%s: fail in update backup boot sector\n", __func__);
-               goto err;
-       }
-       if (exfat_update_boot_checksum(exfat->blk_dev, false) < 0) {
-               exfat_err("%s: fail in update boot sector checksum\n", __func__);
-               goto err;
-       }
-       if (exfat_update_boot_checksum(exfat->blk_dev, true) < 0) {
-               exfat_err("%s: fail in update backup boot sector checksum\n", __func__);
-               goto err;
-       }
-
-       while (!list_empty(&exfat->dir_list)) {
-               parent = list_entry(exfat->dir_list.next, struct exfat_inode, list);
-
-               if (!(parent->attr & ATTR_SUBDIR)) {
-                       exfat_err("%s: parent_inode is not dir\n", __func__);
-                       goto err;
-               }
-
-               if (BFS_write_children(defrag, parent, &index) < 0)
-                       goto err;
-
-               list_del(&parent->list);
-               if (parent != exfat->root)
-                       exfat_free_inode(parent);
-       }
-       return 0;
-
-err:
-       exfat_free_dir_list(exfat);
-       return -1;
-}
-
-/*---------------------- Part 4: Cluster Swapping ---------------------*/
-
-/*
- * Read or write a single cluster.
- * Returns 0 on success.
- */
-static int exfat_rw_clu(struct exfat *exfat, void *tmp, uint32_t cluster, bool read)
-{
-       off_t offset;
-       size_t rw_len;
-
-       offset = (off_t)exfat_c2o(exfat, cluster);
-
-       if (read)
-               rw_len = exfat_read(exfat->blk_dev->dev_fd, tmp, (size_t)exfat->clus_size, offset);
-       else
-               rw_len = exfat_write(exfat->blk_dev->dev_fd, tmp, (size_t)exfat->clus_size, offset);
-
-       if (rw_len != (size_t)exfat->clus_size)
-               return -EIO;
-
-       return 0;
-}
-
-/*
- * Swap two clusters at the data level.
- * Returns 0 on success.
- */
-static int data_swap(struct exfat_defrag *defrag, uint32_t clu_1,
-               uint32_t clu_2, bool clu_1_not_free)
-{
-       struct exfat *exfat = defrag->exfat;
-       void *tmp1 = defrag->tmp_clus;
-       void *tmp2 = defrag->tmp_clus + exfat->clus_size;
-       int ret = 0;
-
-       if (clu_1_not_free) {
-               ret = exfat_rw_clu(exfat, tmp1, clu_1, true);
-               if (ret < 0)
-                       goto out;
-               ret = exfat_rw_clu(exfat, tmp2, clu_2, true);
-               if (ret < 0)
-                       goto out;
-               ret = exfat_rw_clu(exfat, tmp1, clu_2, false);
-               if (ret < 0)
-                       goto out;
-               ret = exfat_rw_clu(exfat, tmp2, clu_1, false);
-               if (ret < 0)
-                       goto out;
-       } else {
-               ret = exfat_rw_clu(exfat, tmp2, clu_2, true);
-               if (ret < 0)
-                       goto out;
-               ret = exfat_rw_clu(exfat, tmp2, clu_1, false);
-               if (ret < 0)
-                       goto out;
-       }
-out:
-       return ret;
-}
-
-/* Metadata level: Connect two clusters in the chain */
-static void clus_connect(struct cluster_info_set *set, uint32_t clu_1, uint32_t clu_2)
-{
-       set->next_clus[clu_1] = clu_2;
-       if (clu_2 != EXFAT_EOF_CLUSTER)
-               set->prev_clus[clu_2] = clu_1;
-}
-
-/* Metadata level: Insert clu_2 between clu_1_prev and clu_1_next */
-static void clus_insert(struct cluster_info_set *set, uint32_t clu_1_prev,
-               uint32_t clu_1_next, uint32_t clu_2)
-{
-       clus_connect(set, clu_1_prev, clu_2);
-       clus_connect(set, clu_2, clu_1_next);
-}
-
-/*
- * Special case for adjacent clusters during swap:
- * Before: clu_0 -> clu_1 -> clu_2 -> clu_3
- * After:  clu_0 -> clu_2 -> clu_1 -> clu_3
- */
-static void clus_exchange(struct cluster_info_set *set, uint32_t clu_0,
-               uint32_t clu_1, uint32_t clu_2, uint32_t clu_3)
-{
-       clus_connect(set, clu_1, clu_3);
-       clus_connect(set, clu_0, clu_2);
-       clus_connect(set, clu_2, clu_1);
-}
-
-/*
- * Swap two clusters at both data and metadata levels.
- * Returns 0 on success.
- */
-static int exfat_swap_clus(struct exfat_defrag *defrag, uint32_t clu_1, uint32_t clu_2)
-{
-       char *bitmap = defrag->exfat->alloc_bitmap;
-       bool clu_1_not_free = exfat_bitmap_get(bitmap, clu_1);
-
-       struct cluster_info_set *set = &(defrag->clu_info_set);
-       uint32_t clu_1_next = set->next_clus[clu_1];
-       uint32_t clu_1_prev = set->prev_clus[clu_1];
-       uint32_t clu_2_next = set->next_clus[clu_2];
-       uint32_t clu_2_prev = set->prev_clus[clu_2];
-
-       /* Data-level swap */
-       if (data_swap(defrag, clu_1, clu_2, clu_1_not_free) < 0)
-               return -EIO;
-
-       /* Metadata-level update */
-       if (clu_1_not_free) {
-               if (clu_1_next == clu_2) {
-                       clus_exchange(set, clu_1_prev, clu_1, clu_2, clu_2_next);
-               } else if (clu_2_next == clu_1) {
-                       clus_exchange(set, clu_2_prev, clu_2, clu_1, clu_1_next);
-               } else {
-                       clus_insert(set, clu_1_prev, clu_1_next, clu_2);
-                       clus_insert(set, clu_2_prev, clu_2_next, clu_1);
-               }
-       } else {
-               clus_connect(set, clu_2_prev, clu_1);
-               clus_connect(set, clu_1, clu_2_next);
-               set->next_clus[clu_2] = EXFAT_FREE_CLUSTER;
-               set->prev_clus[clu_2] = EXFAT_FREE_CLUSTER;
-               exfat_bitmap_set(bitmap, clu_1);
-               exfat_bitmap_clear(bitmap, clu_2);
-       }
-
-       return 0;
-}
-
-/*-------------- Part 5: Defragmentation and Assessment ---------------*/
-
-/*
- * Core defragmentation logic.
- * 1. exfat_defrag uses exfat_swap_clus for cluster swapping.
- * 2. exfat_swap_clus uses data_swap for data exchange.
- * 3. exfat_swap_clus uses clus_connect, clus_exchange,
- *    clus_insert for metadata update.
- * Returns 0 on success.
- */
-static int exfat_defrag(struct exfat_defrag *defrag)
-{
-       int ret = 0;
-       struct cluster_info_set *set = &(defrag->clu_info_set);
-       uint32_t n_clus = set->num_phys_clus;
-       uint32_t n_file = set->num_clus_chain;
-       /* scan_ptr moves forward linearly; exchanged_ptr follows cluster chains */
-       uint32_t scan_ptr = EXFAT_RESERVED_CLUSTERS, exchanged_ptr = 0;
-       uint32_t i;
-
-       exfat_info("Defragmentation is in progress — please keep the device connected.\n");
-       exfat_info("If you do not want to wait, just press Ctrl+C to terminate it safely.\n");
-
-       for (i = n_clus; i < n_clus + n_file; i++) {
-               exchanged_ptr = set->next_clus[i];
-               while (1) {
-                       if (exchanged_ptr == EXFAT_EOF_CLUSTER)
-                               break;
-
-                       if (scan_ptr != exchanged_ptr) {
-                               ret = exfat_swap_clus(defrag, scan_ptr, exchanged_ptr);
-                               if (ret < 0)
-                                       goto out;
-                       }
-                       exchanged_ptr = set->next_clus[scan_ptr];
-                       scan_ptr++;
-               }
-               exfat_info("\r\033[Kdefrag.exfat has processed %u / %u tasks",
-                          i - n_clus + 1, n_file);
-               fflush(stdout);
-
-               if (interrupt_received) {
-                       interrupt_point = i;
-                       exfat_info("\nReceived termination signal! Writing back metadata...\n");
-                       goto out;
-               }
-       }
-       exfat_info("\nWriting back metadata...\n");
-
-out:
-       return ret;
-}
-
-/*
- * Evaluate fragmentation level from two perspectives:
- * cluster chain contiguity and free cluster contiguity
- */
-static void exfat_defrag_assess(struct exfat_defrag *defrag)
-{
-       uint32_t i, n_chain, n_clus, cur_clu, last_clu;
-       uint32_t num_alloc_breakpoint = 0, num_free_breakpoint = 0;
-       uint32_t num_free_clus = 0, num_alloc_clus = 0;
-       double alloc_space_rate, free_space_rate;
-       double alloc_space_frag_rate = 0.0, free_space_frag_rate = 0.0, overall_frag_rate = 0.0;
-       struct exfat *exfat = defrag->exfat;
-       struct cluster_info_set *set = &(defrag->clu_info_set);
-
-       n_chain = set->num_clus_chain;
-       n_clus = set->num_phys_clus;
-
-       /* 1. Assess cluster chain contiguity */
-       for (i = n_clus; i < n_clus + n_chain; i++) {
-               cur_clu = set->next_clus[i];
-               last_clu = EXFAT_EOF_CLUSTER;
-               while (cur_clu != EXFAT_EOF_CLUSTER) {
-                       if (last_clu != EXFAT_EOF_CLUSTER && last_clu + 1 != cur_clu)
-                               num_alloc_breakpoint++;
-                       last_clu = cur_clu;
-                       cur_clu = set->next_clus[cur_clu];
-               }
-       }
-
-       /* 2. Assess free cluster contiguity */
-       last_clu = 0;
-       for (cur_clu = EXFAT_FIRST_CLUSTER; cur_clu < exfat->clus_count + EXFAT_FIRST_CLUSTER;
-            cur_clu++) {
-               if (!exfat_bitmap_get(exfat->alloc_bitmap, cur_clu)) {
-                       num_free_clus++;
-                       if (last_clu != 0 && cur_clu != last_clu + 1)
-                               num_free_breakpoint++;
-                       last_clu = cur_clu;
-               }
-       }
-       num_alloc_clus = exfat->clus_count - num_free_clus;
-
-       /* 3. Calculate and output */
-       if (exfat->clus_count == 0) {
-               exfat_err("%s: invalid cluster count == 0\n", __func__);
-               return;
-       }
-       alloc_space_rate = (double)(num_alloc_clus * 100) / exfat->clus_count;
-       free_space_rate = (double)(num_free_clus * 100) / exfat->clus_count;
-       if (num_alloc_clus > 0) {
-               alloc_space_frag_rate = (double)(num_alloc_breakpoint * 100) / num_alloc_clus;
-               overall_frag_rate += (alloc_space_frag_rate * alloc_space_rate) / 100;
-       }
-       if (num_free_clus > 0) {
-               free_space_frag_rate = (double)(num_free_breakpoint * 100) / num_free_clus;
-               overall_frag_rate += (free_space_frag_rate * free_space_rate) / 100;
-       }
-
-       exfat_info("Fragmentation Assessment Report:\n");
-       exfat_info("1. Allocated space: cluster count = %u (%.2lf%%), breakpoint count = %u, fragmentation rate = %.2lf%%\n",
-                       num_alloc_clus, alloc_space_rate, num_alloc_breakpoint,
-                       alloc_space_frag_rate);
-       exfat_info("2. Free space: cluster count = %u (%.2lf%%), breakpoint count = %u, fragmentation rate = %.2lf%%\n",
-                       num_free_clus, free_space_rate, num_free_breakpoint, free_space_frag_rate);
-       exfat_info("3. Overall weighted fragmentation rate = %.2lf%% (Reference: 0%% ~ Light ~ %u%% ~ Moderate ~ %u%% ~ Severe ~ 100%%)\n",
-                       overall_frag_rate, WATERMARK_1, WATERMARK_2);
-}
-
-/*--------------------------- Part 6: Usage information----------------------------- */
-
-static struct option opts[] = {
-       {"force",       no_argument,    NULL,   'f' },
-       {"assess",      no_argument,    NULL,   'a' },
-       {"version",     no_argument,    NULL,   'v' },
-       {"help",        no_argument,    NULL,   'h' },
-       {NULL,          0,              NULL,    0  }
-};
-
-static void usage(char *name, int exit_code)
-{
-       fprintf(stderr, "Usage: %s\n", name);
-       fprintf(stderr, "\tno option           Perform defragmentation with \"fsck warning\"\n");
-       fprintf(stderr, "\t-f | --force        Perform defragmentation\n");
-       fprintf(stderr, "\t-a | --assess       Assess fragmentation status\n");
-       fprintf(stderr, "\t-v | --version      Show version\n");
-       fprintf(stderr, "\t-h | --help         Show help\n");
-
-       exit(exit_code);
-}
-
-int main(int argc, char *argv[])
-{
-       struct exfat_defrag defrag;
-       struct exfat_user_input ui;
-       struct exfat_blk_dev bd;
-       struct cluster_info_set *set = &(defrag.clu_info_set);
-       int ret = 0, c = 0;
-       /* if no option, initial state */
-       bool only_assessment = false;
-       bool only_show_version = false;
-       bool show_fsck_warning = true;
-
-       memset(&ui, 0, sizeof(ui));
-       memset(&bd, 0, sizeof(bd));
-       bd.dev_fd = -1;
-
-       /* step-0: Parameter Processing and Mode Recognition */
-
-       if (!setlocale(LC_CTYPE, ""))
-               exfat_err("failed to init locale/codeset\n");
-
-       /*
-        * Recognizable command format:
-        * 1. defrag.exfat /dev/sdX      Perform defragmentation with "fsck warning"
-        * 2. defrag.exfat -f /dev/sdX   Perform defragmentation
-        * 3. defrag.exfat -a /dev/sdX   Assess fragmentation status
-        * 4. defrag.exfat -h            Usage information
-        * 5. defrag.exfat -v            Show version
-        */
-       while ((c = getopt_long(argc, argv, "afhv", opts, NULL)) != EOF) {
-               switch (c) {
-               case 'f':
-                       show_fsck_warning = false;
-                       break;
-               case 'a':
-                       only_assessment = true;
-                       break;
-               case 'v':
-                       only_show_version = true;
-                       break;
-               case 'h':
-                       usage(argv[0], 0);
-                       break;
-               default:
-                       exfat_err("Invalid command! Please refer to the help information below or consult the manual.\n");
-                       usage(argv[0], -1);
-               }
-       }
-
-       show_version();
-       if (only_show_version)
-               return ret;
-
-       if (optind != argc - 1) {
-               exfat_err("Invalid command! Please refer to the help information below or consult the manual.\n");
-               usage(argv[0], -1);
-       }
-
-       if ((!only_assessment) && show_fsck_warning) {
-               exfat_info("\nWARNING: To ensure data safety and consistency, we strongly recommend\n");
-               exfat_info("running \"fsck.exfat\" to check and repair the device before defragmentation.\n");
-               exfat_info("Proceed without filesystem check? [y/N]: ");
-               fflush(stdout);
-
-               while (1) {
-                       c = fgetc(stdin);
-                       if (c == 'Y' || c == 'y') {
-                               exfat_info("\n");
-                               break;
-                       } else if (c == 'N' || c == 'n') {
-                               return 0;
-                       } else if (c == '\n' || c == ' ' || c == '\t') {
-                               continue;
-                       } else {
-                               exfat_info("Sorry, please enter 'Y' or 'y' or 'N' or 'n':");
-                       }
-               }
-       }
-
-       /* Step 1: Get block device info */
-       ui.dev_name = argv[optind];
-       if (only_assessment)
-               ui.writeable = false;
-       else
-               ui.writeable = true;
-       if (exfat_get_blk_dev_info(&ui, &bd) < 0) {
-               exfat_err("fail in exfat_get_blk_dev_info\n");
-               ret = -1;
-               goto out;
-       }
-
-       /* Step 2: Initialize exfat structure */
-       memset(&defrag, 0, sizeof(defrag));
-       defrag.exfat = exfat_alloc_exfat(&bd, NULL, NULL);
-       if (defrag.exfat == NULL) {
-               exfat_err("fail in exfat_alloc_exfat\n");
-               ret = -1;
-               goto out;
-       }
-
-       /* Step 3: Allocate memory: next_clus array, temp clusters, dentry buffer */
-       set->num_phys_clus = defrag.exfat->clus_count + EXFAT_FIRST_CLUSTER;
-       set->num_clus_chain = 0;
-       set->next_clus = calloc(set->num_phys_clus, sizeof(uint32_t));
-       if (set->next_clus == NULL) {
-               exfat_err("memory run out in defrag.cluster_info_set->next_clus\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-       defrag.tmp_clus = calloc(2, defrag.exfat->clus_size);
-       if (defrag.tmp_clus == NULL) {
-               exfat_err("memory run out in defrag.tmp_clus\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-       defrag.dentry_buffer = exfat_alloc_buffer(defrag.exfat);
-       if (defrag.dentry_buffer == NULL) {
-               exfat_err("memory run out in defrag.dentry_buffer\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-
-       /* Step 4: Read bitmap and FAT */
-       if (exfat_rw_bitmap(defrag.exfat, true) < 0) {
-               exfat_err("fail in exfat_read_bitmap\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-       if (exfat_rw_FAT(defrag.exfat, set->next_clus, set->num_phys_clus, true) < 0) {
-               exfat_err("fail in exfat_read_FAT\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-
-       /* Step 5: Traverse file tree (BFS), collect first cluster info into virtual nodes */
-       if (BFS_read_file_tree(&defrag) < 0) {
-               exfat_err("fail in BFS_read_file_tree\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-
-       /* For fragmentation assessment, reaching this step is sufficient */
-       if (only_assessment) {
-               exfat_defrag_assess(&defrag);
-               goto fsycn_and_free;
-       }
-
-       /* Step 6: Allocate prev_clus array and build backward links */
-       set->prev_clus = calloc(set->num_phys_clus + set->num_clus_chain, sizeof(uint32_t));
-       if (set->prev_clus == NULL) {
-               exfat_err("memory run out in defrag.cluster_info_set->prev_clus\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-       next_to_prev(set);
-
-       /* Step 7: Perform defragmentation with interrupt support */
-       interrupt_point = 0;
-       interrupt_received = 0;
-       if (signal(SIGINT, sigint_handler) == SIG_ERR ||
-           signal(SIGTERM, sigint_handler) == SIG_ERR) {
-               exfat_err("fail in signal register\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-
-       if (exfat_defrag(&defrag) < 0) {
-               exfat_err("fail in exfat_defrag\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-
-       /* Step 8: Rewrite first cluster and contiguous flags in directory entries */
-       if (BFS_write_file_tree(&defrag) < 0) {
-               exfat_err("fail in BFS_write_file_tree\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-
-       /* Step 9: Write back updated bitmap and FAT */
-       if (exfat_rw_bitmap(defrag.exfat, false) < 0) {
-               exfat_err("fail in exfat_write_bitmap\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-       if (exfat_rw_FAT(defrag.exfat, set->next_clus, set->num_phys_clus, false) < 0) {
-               exfat_err("fail in exfat_write_FAT\n");
-               ret = -1;
-               goto fsycn_and_free;
-       }
-
-fsycn_and_free:
-       /* Step 10: fsync and free allocated resources */
-       fsync(bd.dev_fd);
-
-       if (set->next_clus)
-               free(set->next_clus);
-       if (set->prev_clus)
-               free(set->prev_clus);
-       if (defrag.tmp_clus)
-               free(defrag.tmp_clus);
-       if (defrag.dentry_buffer)
-               exfat_free_buffer(defrag.exfat, defrag.dentry_buffer);
-       if (defrag.exfat)
-               exfat_free_exfat(defrag.exfat);
-
-out:
-       if (bd.dev_fd >= 0)
-               close(bd.dev_fd);
-
-       if (ret == 0) {
-               if (only_assessment)
-                       exfat_info("Assessment has completed successfully!\n");
-               else
-                       exfat_info("Defragmentation has completed successfully!\n");
-       }
-
-       return ret;
-}
diff --git a/defrag/defrag.h b/defrag/defrag.h
deleted file mode 100644 (file)
index e6d014e..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2025 Haodong Xia <3394797836@qq.com>
- */
-
-#ifndef _DEFRAG_H
-#define _DEFRAG_H
-
-#include <stdint.h>
-
-struct exfat;
-struct buffer_desc;
-
-/*
- * This structure tracks the linkage of all cluster chains on the device.
- *
- * In exFAT, a cluster chain is typically represented by:
- *   - The 'first cluster' field in directory entries (e.g., file/directory start)
- *   - The rest of the chain maintained by FAT entries
- *
- * We model this as a linked list with two types of nodes:
- *   - Virtual Node: Represents the 'first cluster' from directory entries
- *   - Physical Node: Represents clusters linked by FAT entries
- *
- * Each chain starts with a Virtual Node, followed by zero or more Physical Nodes.
- * The entire structure is maintained as a doubly-linked list using two arrays:
- *   - 'next_clus': stores forward pointers
- *   - 'prev_clus': stores backward pointers
- *
- * Virtual Nodes are placed after Physical Nodes in the arrays.
- * Since the number of Virtual Nodes (i.e., number of files/directories)
- * is unknown until the file tree is traversed, memory for them is allocated dynamically.
- */
-struct cluster_info_set {
-       uint32_t *next_clus;      /* Forward pointers for the doubly-linked list */
-       uint32_t *prev_clus;      /* Backward pointers for the doubly-linked list */
-       uint32_t num_phys_clus;   /* Number of physical clusters (Physical Nodes) */
-       uint32_t num_clus_chain;   /*
-                                   * Number of cluster chains (Virtual Nodes),
-                                   * one per file/directory
-                                   */
-};
-
-#define VIRT_SPACE_ALIGN 4096  /* Allocate virtual nodes in chunks of 4096 */
-
-/* Core metadata for defragmentation */
-struct exfat_defrag {
-       struct exfat *exfat;                  /* File system metadata */
-       struct cluster_info_set clu_info_set; /*
-                                              * Doubly-linked list representing cluster
-                                              * chain structure
-                                              */
-       struct buffer_desc *dentry_buffer;    /* Temporary storage for directory entries */
-       uint8_t *tmp_clus;                    /* Temporary buffer for cluster swapping */
-};
-
-/* Reference for fragmentation assessment */
-#define WATERMARK_1 30
-#define WATERMARK_2 70
-
-#endif
index 86c59a9d013bb620fc0081ff2416bd5297218087..1b812b46bcb9df2d10945d2a099be26003788e10 100644 (file)
@@ -980,6 +980,7 @@ static int read_bitmap(struct exfat *exfat)
                .in.param       = NULL,
        };
        struct exfat_dentry *dentry;
+       uint64_t map_size, need_map_size;
        int retval;
 
        retval = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
@@ -991,6 +992,20 @@ static int read_bitmap(struct exfat *exfat)
                        le32_to_cpu(dentry->bitmap_start_clu),
                        le64_to_cpu(dentry->bitmap_size));
 
+       /* Validate on-disk bitmap size and required size */
+       map_size = le64_to_cpu(dentry->bitmap_size);
+       need_map_size = DIV_ROUND_UP(exfat->clus_count, 8);
+       if (map_size != need_map_size &&
+               exfat_repair_ask(&exfat_fsck, ER_DE_BITMAP,
+                               "ERROR: invalid bitmap size. %lld", map_size)) {
+               dentry->bitmap_size = cpu_to_le64(need_map_size);
+               if (pwrite(exfat->blk_dev->dev_fd, dentry, DENTRY_SIZE,
+                               filter.out.dev_offset) != DENTRY_SIZE) {
+                       exfat_err("failed to write bitmap dentry\n");
+                       return -EIO;
+               }
+       }
+
        if (le64_to_cpu(dentry->bitmap_size) <
                        DIV_ROUND_UP(exfat->clus_count, 8)) {
                exfat_err("invalid size of allocation bitmap. 0x%" PRIx64 "\n",
index f2722fe22598a7c95e97e3ae52b2b8a7d036f5bc..8631d66c8ee3f9b00de3e48c1ff47a5ef83ebfff 100644 (file)
@@ -50,6 +50,7 @@ static struct exfat_repair_problem problems[] = {
        {ER_DE_UNUSED, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
        {ER_DE_FILE, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
        {ER_DE_UPCASE, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
+       {ER_DE_BITMAP, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
        {ER_DE_SECONDARY_COUNT, ERF_PREEN_YES, ERP_FIX, 0, 0, 0},
        {ER_DE_STREAM, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
        {ER_DE_NAME, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0},
index 53beb96b3a230e82625c4ed81dd6dd93c2d638a7..67c8db11662cbfe445ea785b4c5d394cc70e1448 100644 (file)
@@ -22,6 +22,7 @@
 #define ER_DE_DUPLICATED_NAME          0x00001034
 #define ER_DE_INVALID_NAME             0x00001035
 #define ER_DE_UPCASE                   0x00001036
+#define ER_DE_BITMAP                   0x00001037
 #define ER_FILE_VALID_SIZE             0x00002001
 #define ER_FILE_INVALID_CLUS           0x00002002
 #define ER_FILE_FIRST_CLUS             0x00002003
index e9468970df0dc8fc2eb12699616f94679a693c5f..1e077999b82fafef2c4789727dd5063e9cd0bd01 100644 (file)
@@ -5,6 +5,6 @@
 
 #ifndef _VERSION_H
 
-#define EXFAT_PROGS_VERSION "1.3.0"
+#define EXFAT_PROGS_VERSION "1.3.1"
 
 #endif /* !_VERSION_H */
index 8ab7366eb4efc3798128493b2907d73b7b116720..1bfef27aeb51ca838efcffd77c32cd139123c08d 100644 (file)
@@ -10,6 +10,7 @@
 #include <fcntl.h>
 #include <string.h>
 #include <time.h>
+#include <inttypes.h>
 
 #include "exfat_ondisk.h"
 #include "libexfat.h"
@@ -261,7 +262,6 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
                        int ith, struct exfat_dentry **dentry)
 {
        off_t next_de_file_offset;
-       ssize_t ret;
        unsigned int block;
        struct buffer_desc *bd;
 
@@ -275,9 +275,12 @@ int exfat_de_iter_get(struct exfat_de_iter *iter,
 
        /* read next cluster if needed */
        if (next_de_file_offset >= iter->next_read_offset) {
-               ret = read_block(iter, block);
-               if (ret != (ssize_t)iter->read_size)
-                       return ret;
+               if (read_block(iter, block) != (ssize_t)iter->read_size) {
+                       exfat_err("failed to read from device at offset %#" PRIx64 "\n",
+                                 exfat_de_iter_device_offset(iter));
+
+                       return -EIO;
+               }
                iter->next_read_offset += iter->read_size;
        }
 
diff --git a/manpages/defrag.exfat.8 b/manpages/defrag.exfat.8
deleted file mode 100644 (file)
index 7ce9112..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-.TH defrag.exfat 8
-.SH NAME
-defrag.exfat \- Defragment or assess fragmentation on an exFAT filesystem
-
-.SH SYNOPSIS
-.B defrag.exfat
-[\fB\-f\fR | \fB\-a\fR]
-.I device
-.P
-.B defrag.exfat
-\fB\-h\fR | \fB\-\-help\fR
-.P
-.B defrag.exfat
-\fB\-v\fR | \fB\-\-version\fR
-
-.SH DESCRIPTION
-.B defrag.exfat
-is a utility to defragment files on an exFAT-formatted device or assess its current fragmentation status.
-
-Defragmentation reorganizes scattered file clusters into contiguous blocks, which may improve I/O performance — especially on mechanical storage devices.
-
-By default, when no option is specified, the tool will prompt the user with a warning recommending to run \fBfsck.exfat\fR first to ensure filesystem consistency.
-
-This safety prompt can be bypassed using the \fB\-f\fR option.
-
-The \fB\-a\fR option enables assessment mode, which analyzes fragmentation without modifying the filesystem.
-
-The target device must be \fIunmounted\fR before running this command.
-
-.SH OPTIONS
-.TP
-.B \-f, \-\-force
-Perform defragmentation without prompting the fsck warning. Use with caution.
-.TP
-.B \-a, \-\-assessment
-Assess the fragmentation status of the exFAT volume without making any changes.
-.TP
-.B \-v, \-\-version
-Display version information and exit.
-.TP
-.B \-h, \-\-help
-Show usage information and exit.
-
-.SH EXAMPLES
-.PP
-Defragment an unmounted exFAT device (with safety warning):
-.EX
-$ defrag.exfat /dev/sda1
-.EE
-
-.PP
-Defragment without prompting (force mode):
-.EX
-$ defrag.exfat -f /dev/sda1
-.EE
-
-.PP
-Assess fragmentation status (read-only):
-.EX
-$ defrag.exfat -a /dev/sda1
-.EE
-
-.PP
-Show version:
-.EX
-$ defrag.exfat -v
-.EE
-
-.PP
-Show help:
-.EX
-$ defrag.exfat -h
-.EE
-
-.SH NOTES
-It is \fBstrongly recommended\fR to run \fBfsck.exfat\fR on the device before defragmenting to ensure filesystem integrity.
-
-Always backup critical data before defragmenting.
diff --git a/tests/bad_bitmap_size/exfat.img.tar.xz b/tests/bad_bitmap_size/exfat.img.tar.xz
new file mode 100644 (file)
index 0000000..ef4bdda
Binary files /dev/null and b/tests/bad_bitmap_size/exfat.img.tar.xz differ