From: Sven Hoexter Date: Tue, 16 Dec 2025 12:32:11 +0000 (+0100) Subject: New upstream version 1.3.1 X-Git-Tag: upstream/1.3.1^0 X-Git-Url: https://git.sven.stormbind.net/?a=commitdiff_plain;h=19e1818c37c03721c747481b886c30232755eb3e;p=sven%2Fexfatprogs.git New upstream version 1.3.1 --- diff --git a/Android.bp b/Android.bp index 285abd1..d0e2594 100644 --- a/Android.bp +++ b/Android.bp @@ -10,7 +10,6 @@ cc_library_headers { "label", "dump", "exfat2img", - "defrag", ], } diff --git a/Makefile.am b/Makefile.am index a059009..51c2c34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/Makefile.in b/Makefile.in index b1d35d1..1ca46c7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 82e7ff6..acb0b70 100644 --- 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 ====================================== diff --git a/README.md b/README.md index f5d21df..d640c06 100644 --- 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 diff --git a/configure b/configure index efcdc63..bd83be4 100755 --- 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 . # @@ -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 diff --git a/configure.ac b/configure.ac index e693393..a4bbe9d 100644 --- a/configure.ac +++ b/configure.ac @@ -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 index 1f26e6d..0000000 --- a/defrag/Android.bp +++ /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 index 9323606..0000000 --- a/defrag/Makefile.am +++ /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 index 71f5e42..0000000 --- a/defrag/Makefile.in +++ /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 index 9311cb7..0000000 --- a/defrag/defrag.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index e6d014e..0000000 --- a/defrag/defrag.h +++ /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 - -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 diff --git a/fsck/fsck.c b/fsck/fsck.c index 86c59a9..1b812b4 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -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", diff --git a/fsck/repair.c b/fsck/repair.c index f2722fe..8631d66 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -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}, diff --git a/fsck/repair.h b/fsck/repair.h index 53beb96..67c8db1 100644 --- a/fsck/repair.h +++ b/fsck/repair.h @@ -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 diff --git a/include/version.h b/include/version.h index e946897..1e07799 100644 --- a/include/version.h +++ b/include/version.h @@ -5,6 +5,6 @@ #ifndef _VERSION_H -#define EXFAT_PROGS_VERSION "1.3.0" +#define EXFAT_PROGS_VERSION "1.3.1" #endif /* !_VERSION_H */ diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 8ab7366..1bfef27 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -10,6 +10,7 @@ #include #include #include +#include #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 index 7ce9112..0000000 --- a/manpages/defrag.exfat.8 +++ /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 index 0000000..ef4bdda Binary files /dev/null and b/tests/bad_bitmap_size/exfat.img.tar.xz differ