From 65b0f9fc39c546369f4f0f43f36891f7709b891a Mon Sep 17 00:00:00 2001 From: Sven Hoexter Date: Wed, 31 Oct 2018 22:17:38 +0100 Subject: [PATCH] New upstream version 1.3.0 --- ChangeLog | 11 +++ Makefile.in | 2 + README | 2 +- configure | 104 ++++++++++++++++++++++--- configure.ac | 8 +- fuse/Makefile.in | 2 + fuse/main.c | 167 ++++++++++++++++------------------------ fuse/mount.exfat-fuse.8 | 2 +- libexfat/Makefile.am | 1 + libexfat/Makefile.in | 8 +- libexfat/config.h.in | 3 + libexfat/exfat.h | 16 ++++ libexfat/io.c | 70 +++++++++++++++++ libexfat/log.c | 16 ++++ libexfat/mount.c | 28 +++++-- libexfat/node.c | 6 +- libexfat/platform.h | 2 +- libexfat/repair.c | 102 ++++++++++++++++++++++++ 18 files changed, 426 insertions(+), 124 deletions(-) create mode 100644 libexfat/repair.c diff --git a/ChangeLog b/ChangeLog index 8bff070..9e20bf5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +1.3.0 (2018-09-15) + +* exfatfsck can now repair some errors. +* Added experimental Android support for exfat-utils [liminghao, LongPingWEI, +Pablo Mendez Hernandez, Pierre-Hugues Husson]. +* Cleaned up FUSE code preparing for FUSE 3 support. +* Removed OpenBSD support as it does not handle -o option in fuse_main(). +* Re-introduced FreeBSD support [Oleksii Samorukov]. +* Fixed DragonFly BSD support [Tomohiro Kusumi]. +* dirent->d_type in now filled on readdir() [Mark Browning]. + 1.2.8 (2018-02-03) * Fixed new files or directories creation in the root directory: ensure there diff --git a/Makefile.in b/Makefile.in index fedf2ba..60823f6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -269,6 +269,8 @@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ +UBLIO_CFLAGS = @UBLIO_CFLAGS@ +UBLIO_LIBS = @UBLIO_LIBS@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ diff --git a/README b/README index 500d722..2b98bbc 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ Supported operating systems: * GNU/Linux * Mac OS X 10.5 or later -* OpenBSD +* FreeBSD Most GNU/Linux distributions already have fuse-exfat and exfat-utils in their repositories, so you can just install and use them. The next chapter describes how to compile them from source. diff --git a/configure b/configure index 11c45fb..7132f5c 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.69 for Free exFAT implementation 1.2.8. +# Generated by GNU Autoconf 2.69 for Free exFAT implementation 1.3.0. # # Report bugs to . # @@ -579,8 +579,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Free exFAT implementation' PACKAGE_TARNAME='fuse-exfat' -PACKAGE_VERSION='1.2.8' -PACKAGE_STRING='Free exFAT implementation 1.2.8' +PACKAGE_VERSION='1.3.0' +PACKAGE_STRING='Free exFAT implementation 1.3.0' PACKAGE_BUGREPORT='relan@users.noreply.github.com' PACKAGE_URL='https://github.com/relan/exfat' @@ -590,6 +590,8 @@ LTLIBOBJS LIBOBJS FUSE_LIBS FUSE_CFLAGS +UBLIO_LIBS +UBLIO_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG @@ -696,6 +698,8 @@ CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR +UBLIO_CFLAGS +UBLIO_LIBS FUSE_CFLAGS FUSE_LIBS' @@ -1238,7 +1242,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 Free exFAT implementation 1.2.8 to adapt to many kinds of systems. +\`configure' configures Free exFAT implementation 1.3.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1304,7 +1308,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Free exFAT implementation 1.2.8:";; + short | recursive ) echo "Configuration of Free exFAT implementation 1.3.0:";; esac cat <<\_ACEOF @@ -1333,6 +1337,9 @@ Some influential environment variables: directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path + UBLIO_CFLAGS + C compiler flags for UBLIO, overriding pkg-config + UBLIO_LIBS linker flags for UBLIO, overriding pkg-config FUSE_CFLAGS C compiler flags for FUSE, overriding pkg-config FUSE_LIBS linker flags for FUSE, overriding pkg-config @@ -1403,7 +1410,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Free exFAT implementation configure 1.2.8 +Free exFAT implementation configure 1.3.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1458,7 +1465,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 Free exFAT implementation $as_me 1.2.8, which was +It was created by Free exFAT implementation $as_me 1.3.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2321,7 +2328,7 @@ fi # Define the identity of the package. PACKAGE='fuse-exfat' - VERSION='1.2.8' + VERSION='1.3.0' cat >>confdefs.h <<_ACEOF @@ -4215,6 +4222,83 @@ $as_echo "no" >&6; } fi fi +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for UBLIO" >&5 +$as_echo_n "checking for UBLIO... " >&6; } + +if test -n "$UBLIO_CFLAGS"; then + pkg_cv_UBLIO_CFLAGS="$UBLIO_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libublio\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libublio") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_UBLIO_CFLAGS=`$PKG_CONFIG --cflags "libublio" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$UBLIO_LIBS"; then + pkg_cv_UBLIO_LIBS="$UBLIO_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libublio\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libublio") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_UBLIO_LIBS=`$PKG_CONFIG --libs "libublio" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + UBLIO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libublio" 2>&1` + else + UBLIO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libublio" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$UBLIO_PKG_ERRORS" >&5 + + : +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + : +else + UBLIO_CFLAGS=$pkg_cv_UBLIO_CFLAGS + UBLIO_LIBS=$pkg_cv_UBLIO_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + + CFLAGS="$CFLAGS $UBLIO_CFLAGS" + LIBS="$LIBS $UBLIO_LIBS" + +$as_echo "#define USE_UBLIO 1" >>confdefs.h + + +fi + pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for FUSE" >&5 $as_echo_n "checking for FUSE... " >&6; } @@ -4839,7 +4923,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 Free exFAT implementation $as_me 1.2.8, which was +This file was extended by Free exFAT implementation $as_me 1.3.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4906,7 +4990,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Free exFAT implementation config.status 1.2.8 +Free exFAT implementation config.status 1.3.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index d0626cb..d7fc141 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # AC_INIT([Free exFAT implementation], - [1.2.8], + [1.3.0], [relan@users.noreply.github.com], [fuse-exfat], [https://github.com/relan/exfat]) @@ -31,6 +31,12 @@ AC_PROG_CC_C99 AC_PROG_RANLIB AM_PROG_AR AC_SYS_LARGEFILE +PKG_CHECK_MODULES([UBLIO], [libublio], [ + CFLAGS="$CFLAGS $UBLIO_CFLAGS" + LIBS="$LIBS $UBLIO_LIBS" + AC_DEFINE([USE_UBLIO], [1], + [Define if block devices are not supported.]) +], [:]) PKG_CHECK_MODULES([FUSE], [fuse]) AC_CONFIG_HEADERS([libexfat/config.h]) AC_CONFIG_FILES([ diff --git a/fuse/Makefile.in b/fuse/Makefile.in index 7899426..d463750 100644 --- a/fuse/Makefile.in +++ b/fuse/Makefile.in @@ -267,6 +267,8 @@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ +UBLIO_CFLAGS = @UBLIO_CFLAGS@ +UBLIO_LIBS = @UBLIO_LIBS@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ diff --git a/fuse/main.c b/fuse/main.c index e5e25d9..c645390 100644 --- a/fuse/main.c +++ b/fuse/main.c @@ -42,9 +42,6 @@ #error FUSE 2.6 or later is required #endif -const char* default_options = "ro_fallback,allow_other,blkdev,big_writes," - "default_permissions"; - struct exfat ef; static struct exfat_node* get_node(const struct fuse_file_info* fi) @@ -105,6 +102,7 @@ static int fuse_exfat_readdir(const char* path, void* buffer, struct exfat_iterator it; int rc; char name[EXFAT_UTF8_NAME_BUFFER_MAX]; + struct stat stbuf; exfat_debug("[%s] %s", __func__, path); @@ -134,7 +132,8 @@ static int fuse_exfat_readdir(const char* path, void* buffer, exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__, name, node->is_contiguous ? "contiguous" : "fragmented", node->size, node->start_cluster); - filler(buffer, name, NULL, 0); + exfat_stat(&ef, node, &stbuf); + filler(buffer, name, &stbuf, 0); exfat_put_node(&ef, node); } exfat_closedir(&ef, &it); @@ -447,6 +446,12 @@ static char* add_fsname_option(char* options, const char* spec) return options; } +static char* add_ro_option(char* options, bool ro) +{ + return ro ? add_option(options, "ro", NULL) : options; +} + +#if defined(__linux__) || defined(__FreeBSD__) static char* add_user_option(char* options) { struct passwd* pw; @@ -463,7 +468,9 @@ static char* add_user_option(char* options) } return add_option(options, "user", pw->pw_name); } +#endif +#if defined(__linux__) static char* add_blksize_option(char* options, long cluster_size) { long page_size = sysconf(_SC_PAGESIZE); @@ -475,38 +482,57 @@ static char* add_blksize_option(char* options, long cluster_size) snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size)); return add_option(options, "blksize", blksize); } +#endif -static char* add_fuse_options(char* options, const char* spec) +static char* add_fuse_options(char* options, const char* spec, bool ro) { options = add_fsname_option(options, spec); if (options == NULL) return NULL; + options = add_ro_option(options, ro); + if (options == NULL) + return NULL; +#if defined(__linux__) || defined(__FreeBSD__) options = add_user_option(options); if (options == NULL) return NULL; +#endif +#if defined(__linux__) options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb)); if (options == NULL) return NULL; - +#endif return options; } +static int fuse_exfat_main(char* mount_options, char* mount_point) +{ + char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL}; + return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv, + &fuse_exfat_ops, NULL); +} + int main(int argc, char* argv[]) { - struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL); - struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL); const char* spec = NULL; - const char* mount_point = NULL; - char* mount_options; - int debug = 0; - struct fuse_chan* fc = NULL; - struct fuse* fh = NULL; + char* mount_point = NULL; + char* fuse_options; + char* exfat_options; int opt; + int rc; printf("FUSE exfat %s\n", VERSION); - mount_options = strdup(default_options); - if (mount_options == NULL) + fuse_options = strdup("allow_other," +#if defined(__linux__) || defined(__FreeBSD__) + "big_writes," +#endif +#if defined(__linux__) + "blkdev," +#endif + "default_permissions"); + exfat_options = strdup("ro_fallback"); + if (fuse_options == NULL || exfat_options == NULL) { exfat_error("failed to allocate options string"); return 1; @@ -517,122 +543,65 @@ int main(int argc, char* argv[]) switch (opt) { case 'd': - debug = 1; + fuse_options = add_option(fuse_options, "debug", NULL); + if (fuse_options == NULL) + { + free(exfat_options); + return 1; + } break; case 'n': break; case 'o': - mount_options = add_option(mount_options, optarg, NULL); - if (mount_options == NULL) + exfat_options = add_option(exfat_options, optarg, NULL); + if (exfat_options == NULL) + { + free(fuse_options); return 1; + } break; case 'V': - free(mount_options); + free(exfat_options); + free(fuse_options); puts("Copyright (C) 2010-2018 Andrew Nayenko"); return 0; case 'v': break; default: - free(mount_options); + free(exfat_options); + free(fuse_options); usage(argv[0]); break; } } if (argc - optind != 2) { - free(mount_options); + free(exfat_options); + free(fuse_options); usage(argv[0]); } spec = argv[optind]; mount_point = argv[optind + 1]; - if (exfat_mount(&ef, spec, mount_options) != 0) + if (exfat_mount(&ef, spec, exfat_options) != 0) { - free(mount_options); + free(exfat_options); + free(fuse_options); return 1; } - if (ef.ro == -1) /* read-only fallback was used */ - { - mount_options = add_option(mount_options, "ro", NULL); - if (mount_options == NULL) - { - exfat_unmount(&ef); - return 1; - } - } - - mount_options = add_fuse_options(mount_options, spec); - if (mount_options == NULL) - { - exfat_unmount(&ef); - return 1; - } + free(exfat_options); - /* create arguments for fuse_mount() */ - if (fuse_opt_add_arg(&mount_args, "exfat") != 0 || - fuse_opt_add_arg(&mount_args, "-o") != 0 || - fuse_opt_add_arg(&mount_args, mount_options) != 0) + fuse_options = add_fuse_options(fuse_options, spec, ef.ro != 0); + if (fuse_options == NULL) { exfat_unmount(&ef); - free(mount_options); return 1; } - free(mount_options); + /* let FUSE do all its wizardry */ + rc = fuse_exfat_main(fuse_options, mount_point); - /* create FUSE mount point */ - fc = fuse_mount(mount_point, &mount_args); - fuse_opt_free_args(&mount_args); - if (fc == NULL) - { - exfat_unmount(&ef); - return 1; - } - - /* create arguments for fuse_new() */ - if (fuse_opt_add_arg(&newfs_args, "") != 0 || - (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0)) - { - fuse_unmount(mount_point, fc); - exfat_unmount(&ef); - return 1; - } - - /* create new FUSE file system */ - fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops, - sizeof(struct fuse_operations), NULL); - fuse_opt_free_args(&newfs_args); - if (fh == NULL) - { - fuse_unmount(mount_point, fc); - exfat_unmount(&ef); - return 1; - } - - /* exit session on HUP, TERM and INT signals and ignore PIPE signal */ - if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0) - { - fuse_unmount(mount_point, fc); - fuse_destroy(fh); - exfat_unmount(&ef); - exfat_error("failed to set signal handlers"); - return 1; - } - - /* go to background (unless "-d" option is passed) and run FUSE - main loop */ - if (fuse_daemonize(debug) == 0) - { - if (fuse_loop(fh) != 0) - exfat_error("FUSE loop failure"); - } - else - exfat_error("failed to daemonize"); - - fuse_remove_signal_handlers(fuse_get_session(fh)); - /* note that fuse_unmount() must be called BEFORE fuse_destroy() */ - fuse_unmount(mount_point, fc); - fuse_destroy(fh); - return 0; + free(fuse_options); + return rc; } diff --git a/fuse/mount.exfat-fuse.8 b/fuse/mount.exfat-fuse.8 index 602ddc9..e0925b2 100644 --- a/fuse/mount.exfat-fuse.8 +++ b/fuse/mount.exfat-fuse.8 @@ -1,6 +1,6 @@ .\" Copyright (C) 2010-2016 Andrew Nayenko .\" -.TH EXFAT-FUSE 8 "July 2010" +.TH EXFAT-FUSE 8 "November 2015" .SH NAME mount.exfat-fuse \- mount an exFAT file system .SH SYNOPSIS diff --git a/libexfat/Makefile.am b/libexfat/Makefile.am index 4ec9df5..d639e13 100644 --- a/libexfat/Makefile.am +++ b/libexfat/Makefile.am @@ -33,6 +33,7 @@ libexfat_a_SOURCES = \ mount.c \ node.c \ platform.h \ + repair.c \ time.c \ utf.c \ utils.c diff --git a/libexfat/Makefile.in b/libexfat/Makefile.in index d039dfe..9c44323 100644 --- a/libexfat/Makefile.in +++ b/libexfat/Makefile.in @@ -127,8 +127,8 @@ am__v_AR_1 = libexfat_a_AR = $(AR) $(ARFLAGS) libexfat_a_LIBADD = am_libexfat_a_OBJECTS = cluster.$(OBJEXT) io.$(OBJEXT) log.$(OBJEXT) \ - lookup.$(OBJEXT) mount.$(OBJEXT) node.$(OBJEXT) time.$(OBJEXT) \ - utf.$(OBJEXT) utils.$(OBJEXT) + lookup.$(OBJEXT) mount.$(OBJEXT) node.$(OBJEXT) \ + repair.$(OBJEXT) time.$(OBJEXT) utf.$(OBJEXT) utils.$(OBJEXT) libexfat_a_OBJECTS = $(am_libexfat_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -236,6 +236,8 @@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ +UBLIO_CFLAGS = @UBLIO_CFLAGS@ +UBLIO_LIBS = @UBLIO_LIBS@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -293,6 +295,7 @@ libexfat_a_SOURCES = \ mount.c \ node.c \ platform.h \ + repair.c \ time.c \ utf.c \ utils.c @@ -367,6 +370,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lookup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mount.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/repair.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ diff --git a/libexfat/config.h.in b/libexfat/config.h.in index d096cd3..c3312ab 100644 --- a/libexfat/config.h.in +++ b/libexfat/config.h.in @@ -21,6 +21,9 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define if block devices are not supported. */ +#undef USE_UBLIO + /* Version number of package */ #undef VERSION diff --git a/libexfat/exfat.h b/libexfat/exfat.h index e0d33e2..2342be4 100644 --- a/libexfat/exfat.h +++ b/libexfat/exfat.h @@ -24,7 +24,10 @@ #ifndef EXFAT_H_INCLUDED #define EXFAT_H_INCLUDED +#ifndef ANDROID +/* Android.bp is used instead of autotools when targeting Android */ #include "config.h" +#endif #include "compiler.h" #include "exfatfs.h" #include @@ -61,6 +64,9 @@ #define BMAP_CLR(bitmap, index) \ ((bitmap)[BMAP_BLOCK(index)] &= ~BMAP_MASK(index)) +#define EXFAT_REPAIR(hook, ef, ...) \ + (exfat_ask_to_fix(ef) && exfat_fix_ ## hook(ef, __VA_ARGS__)) + /* The size of off_t type must be 64 bits. File systems larger than 2 GB will be corrupted with 32-bit off_t. */ STATIC_ASSERT(sizeof(off_t) == 8); @@ -119,6 +125,7 @@ struct exfat gid_t gid; int ro; bool noatime; + enum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair; }; /* in-core nodes iterator */ @@ -135,6 +142,7 @@ struct exfat_human_bytes }; extern int exfat_errors; +extern int exfat_errors_fixed; void exfat_bug(const char* format, ...) PRINTF NORETURN; void exfat_error(const char* format, ...) PRINTF; @@ -225,4 +233,12 @@ void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, uint8_t* centisec); void exfat_tzset(void); +bool exfat_ask_to_fix(const struct exfat* ef); +bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector, + uint32_t vbr_checksum); +bool exfat_fix_invalid_node_checksum(const struct exfat* ef, + struct exfat_node* node); +bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir, + const struct exfat_entry* entry, off_t offset); + #endif /* ifndef EXFAT_H_INCLUDED */ diff --git a/libexfat/io.c b/libexfat/io.c index bae0cf1..bc92c7c 100644 --- a/libexfat/io.c +++ b/libexfat/io.c @@ -38,12 +38,20 @@ #elif __linux__ #include #endif +#ifdef USE_UBLIO +#include +#include +#endif struct exfat_dev { int fd; enum exfat_mode mode; off_t size; /* in bytes */ +#ifdef USE_UBLIO + off_t pos; + ublio_filehandle_t ufh; +#endif }; static bool is_open(int fd) @@ -80,6 +88,9 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) { struct exfat_dev* dev; struct stat stbuf; +#ifdef USE_UBLIO + struct ublio_param up; +#endif /* The system allocates file descriptors sequentially. If we have been started with stdin (0), stdout (1) or stderr (2) closed, the system @@ -235,6 +246,24 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) } } +#ifdef USE_UBLIO + memset(&up, 0, sizeof(struct ublio_param)); + up.up_blocksize = 256 * 1024; + up.up_items = 64; + up.up_grace = 32; + up.up_priv = &dev->fd; + + dev->pos = 0; + dev->ufh = ublio_open(&up); + if (dev->ufh == NULL) + { + close(dev->fd); + free(dev); + exfat_error("failed to initialize ublio"); + return NULL; + } +#endif + return dev; } @@ -242,6 +271,13 @@ int exfat_close(struct exfat_dev* dev) { int rc = 0; +#ifdef USE_UBLIO + if (ublio_close(dev->ufh) != 0) + { + exfat_error("failed to close ublio"); + rc = -EIO; + } +#endif if (close(dev->fd) != 0) { exfat_error("failed to close device: %s", strerror(errno)); @@ -255,6 +291,13 @@ int exfat_fsync(struct exfat_dev* dev) { int rc = 0; +#ifdef USE_UBLIO + if (ublio_fsync(dev->ufh) != 0) + { + exfat_error("ublio fsync failed"); + rc = -EIO; + } +#endif if (fsync(dev->fd) != 0) { exfat_error("fsync failed: %s", strerror(errno)); @@ -275,29 +318,56 @@ off_t exfat_get_size(const struct exfat_dev* dev) off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence) { +#ifdef USE_UBLIO + /* XXX SEEK_CUR will be handled incorrectly */ + return dev->pos = lseek(dev->fd, offset, whence); +#else return lseek(dev->fd, offset, whence); +#endif } ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size) { +#ifdef USE_UBLIO + ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos); + if (result >= 0) + dev->pos += size; + return result; +#else return read(dev->fd, buffer, size); +#endif } ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size) { +#ifdef USE_UBLIO + ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos); + if (result >= 0) + dev->pos += size; + return result; +#else return write(dev->fd, buffer, size); +#endif } ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, off_t offset) { +#ifdef USE_UBLIO + return ublio_pread(dev->ufh, buffer, size, offset); +#else return pread(dev->fd, buffer, size, offset); +#endif } ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, off_t offset) { +#ifdef USE_UBLIO + return ublio_pwrite(dev->ufh, buffer, size, offset); +#else return pwrite(dev->fd, buffer, size, offset); +#endif } ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, diff --git a/libexfat/log.c b/libexfat/log.c index d62bf75..f29f74b 100644 --- a/libexfat/log.c +++ b/libexfat/log.c @@ -22,7 +22,11 @@ #include "exfat.h" #include +#ifdef __ANDROID__ +#include +#else #include +#endif #include int exfat_errors; @@ -43,8 +47,12 @@ void exfat_bug(const char* format, ...) va_end(ap); fputs(".\n", stderr); +#ifdef __ANDROID__ + __android_log_vprint(ANDROID_LOG_FATAL, PACKAGE, format, aq); +#else if (!isatty(STDERR_FILENO)) vsyslog(LOG_CRIT, format, aq); +#endif va_end(aq); abort(); @@ -67,8 +75,12 @@ void exfat_error(const char* format, ...) va_end(ap); fputs(".\n", stderr); +#ifdef __ANDROID__ + __android_log_vprint(ANDROID_LOG_ERROR, PACKAGE, format, aq); +#else if (!isatty(STDERR_FILENO)) vsyslog(LOG_ERR, format, aq); +#endif va_end(aq); } @@ -89,8 +101,12 @@ void exfat_warn(const char* format, ...) va_end(ap); fputs(".\n", stderr); +#ifdef __ANDROID__ + __android_log_vprint(ANDROID_LOG_WARN, PACKAGE, format, aq); +#else if (!isatty(STDERR_FILENO)) vsyslog(LOG_WARNING, format, aq); +#endif va_end(aq); } diff --git a/libexfat/mount.c b/libexfat/mount.c index 3b18b12..4284aee 100644 --- a/libexfat/mount.c +++ b/libexfat/mount.c @@ -103,15 +103,28 @@ static void parse_options(struct exfat* ef, const char* options) ef->gid = get_int_option(options, "gid", 10, getegid()); ef->noatime = match_option(options, "noatime"); + + switch (get_int_option(options, "repair", 10, 0)) + { + case 1: + ef->repair = EXFAT_REPAIR_ASK; + break; + case 2: + ef->repair = EXFAT_REPAIR_YES; + break; + default: + ef->repair = EXFAT_REPAIR_NO; + break; + } } -static bool verify_vbr_checksum(struct exfat_dev* dev, void* sector, - off_t sector_size) +static bool verify_vbr_checksum(const struct exfat* ef, void* sector) { + off_t sector_size = SECTOR_SIZE(*ef->sb); uint32_t vbr_checksum; int i; - if (exfat_pread(dev, sector, sector_size, 0) < 0) + if (exfat_pread(ef->dev, sector, sector_size, 0) < 0) { exfat_error("failed to read boot sector"); return false; @@ -119,7 +132,7 @@ static bool verify_vbr_checksum(struct exfat_dev* dev, void* sector, vbr_checksum = exfat_vbr_start_checksum(sector, sector_size); for (i = 1; i < 11; i++) { - if (exfat_pread(dev, sector, sector_size, i * sector_size) < 0) + if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0) { exfat_error("failed to read VBR sector"); return false; @@ -127,7 +140,7 @@ static bool verify_vbr_checksum(struct exfat_dev* dev, void* sector, vbr_checksum = exfat_vbr_add_checksum(sector, sector_size, vbr_checksum); } - if (exfat_pread(dev, sector, sector_size, i * sector_size) < 0) + if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0) { exfat_error("failed to read VBR checksum sector"); return false; @@ -137,7 +150,8 @@ static bool verify_vbr_checksum(struct exfat_dev* dev, void* sector, { exfat_error("invalid VBR checksum 0x%x (expected 0x%x)", le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum); - return false; + if (!EXFAT_REPAIR(invalid_vbr_checksum, ef, sector, vbr_checksum)) + return false; } return true; } @@ -252,7 +266,7 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) return -ENOMEM; } /* use zero_cluster as a temporary buffer for VBR checksum verification */ - if (!verify_vbr_checksum(ef->dev, ef->zero_cluster, SECTOR_SIZE(*ef->sb))) + if (!verify_vbr_checksum(ef, ef->zero_cluster)) { exfat_free(ef); return -EIO; diff --git a/libexfat/node.c b/libexfat/node.c index 1ccb1c9..ab1d7d6 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -223,7 +223,8 @@ static bool check_node(const struct exfat* ef, struct exfat_node* node, exfat_get_name(node, buffer); exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer, le16_to_cpu(actual_checksum), le16_to_cpu(meta1->checksum)); - ret = false; + if (!EXFAT_REPAIR(invalid_node_checksum, ef, node)) + ret = false; } /* @@ -516,7 +517,8 @@ static int readdir(struct exfat* ef, struct exfat_node* parent, break; /* deleted entry, ignore it */ exfat_error("unknown entry type %#hhx", entry.type); - return -EIO; + if (!EXFAT_REPAIR(unknown_entry, ef, parent, &entry, *offset)) + return -EIO; } *offset += sizeof(entry); } diff --git a/libexfat/platform.h b/libexfat/platform.h index e10c46b..9ab3155 100644 --- a/libexfat/platform.h +++ b/libexfat/platform.h @@ -46,7 +46,7 @@ #define EXFAT_LITTLE_ENDIAN LITTLE_ENDIAN #define EXFAT_BIG_ENDIAN BIG_ENDIAN -#elif defined(__FreeBSD__) || defined(__DragonFlyBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) #include #define exfat_bswap16(x) bswap16(x) diff --git a/libexfat/repair.c b/libexfat/repair.c new file mode 100644 index 0000000..237ab3a --- /dev/null +++ b/libexfat/repair.c @@ -0,0 +1,102 @@ +/* + repair.c (09.03.17) + exFAT file system implementation library. + + Free exFAT implementation. + Copyright (C) 2010-2018 Andrew Nayenko + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "exfat.h" +#include + +int exfat_errors_fixed; + +bool exfat_ask_to_fix(const struct exfat* ef) +{ + const char* question = "Fix (Y/N)?"; + char answer[8]; + bool yeah, nope; + + switch (ef->repair) + { + case EXFAT_REPAIR_NO: + return false; + case EXFAT_REPAIR_YES: + printf("%s %s", question, "Y\n"); + return true; + case EXFAT_REPAIR_ASK: + do + { + printf("%s ", question); + fflush(stdout); + if (fgets(answer, sizeof(answer), stdin)) + { + yeah = strcasecmp(answer, "Y\n") == 0; + nope = strcasecmp(answer, "N\n") == 0; + } + else + { + yeah = false; + nope = true; + } + } + while (!yeah && !nope); + return yeah; + } + exfat_bug("invalid repair option value: %d", ef->repair); +} + +bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector, + uint32_t vbr_checksum) +{ + size_t i; + off_t sector_size = SECTOR_SIZE(*ef->sb); + + for (i = 0; i < sector_size / sizeof(vbr_checksum); i++) + ((le32_t*) sector)[i] = cpu_to_le32(vbr_checksum); + if (exfat_pwrite(ef->dev, sector, sector_size, 11 * sector_size) < 0) + { + exfat_error("failed to write correct VBR checksum"); + return false; + } + exfat_errors_fixed++; + return true; +} + +bool exfat_fix_invalid_node_checksum(const struct exfat* ef, + struct exfat_node* node) +{ + /* checksum will be rewritten by exfat_flush_node() */ + node->is_dirty = true; + + exfat_errors_fixed++; + return true; +} + +bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir, + const struct exfat_entry* entry, off_t offset) +{ + struct exfat_entry deleted = *entry; + + deleted.type &= ~EXFAT_ENTRY_VALID; + if (exfat_generic_pwrite(ef, dir, &deleted, sizeof(struct exfat_entry), + offset) != sizeof(struct exfat_entry)) + return false; + + exfat_errors_fixed++; + return true; +} -- 2.39.5