+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
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@
* 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.
#! /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 <relan@users.noreply.github.com>.
#
# 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'
LIBOBJS
FUSE_LIBS
FUSE_CFLAGS
+UBLIO_LIBS
+UBLIO_CFLAGS
PKG_CONFIG_LIBDIR
PKG_CONFIG_PATH
PKG_CONFIG
PKG_CONFIG
PKG_CONFIG_PATH
PKG_CONFIG_LIBDIR
+UBLIO_CFLAGS
+UBLIO_LIBS
FUSE_CFLAGS
FUSE_LIBS'
# 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]...
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
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
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.
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 $@
# Define the identity of the package.
PACKAGE='fuse-exfat'
- VERSION='1.2.8'
+ VERSION='1.3.0'
cat >>confdefs.h <<_ACEOF
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; }
# 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
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\\"
#
AC_INIT([Free exFAT implementation],
- [1.2.8],
+ [1.3.0],
[relan@users.noreply.github.com],
[fuse-exfat],
[https://github.com/relan/exfat])
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([
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@
#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)
struct exfat_iterator it;
int rc;
char name[EXFAT_UTF8_NAME_BUFFER_MAX];
+ struct stat stbuf;
exfat_debug("[%s] %s", __func__, path);
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);
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;
}
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);
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;
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;
}
.\" 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
mount.c \
node.c \
platform.h \
+ repair.c \
time.c \
utf.c \
utils.c
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@)
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@
mount.c \
node.c \
platform.h \
+ repair.c \
time.c \
utf.c \
utils.c
@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@
/* 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
#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 <stdio.h>
#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);
gid_t gid;
int ro;
bool noatime;
+ enum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair;
};
/* in-core nodes iterator */
};
extern int exfat_errors;
+extern int exfat_errors_fixed;
void exfat_bug(const char* format, ...) PRINTF NORETURN;
void exfat_error(const char* format, ...) PRINTF;
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 */
#elif __linux__
#include <sys/mount.h>
#endif
+#ifdef USE_UBLIO
+#include <sys/uio.h>
+#include <ublio.h>
+#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)
{
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
}
}
+#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;
}
{
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));
{
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));
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,
#include "exfat.h"
#include <stdarg.h>
+#ifdef __ANDROID__
+#include <android/log.h>
+#else
#include <syslog.h>
+#endif
#include <unistd.h>
int exfat_errors;
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();
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);
}
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);
}
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;
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;
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;
{
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;
}
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;
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;
}
/*
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);
}
#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 <sys/endian.h>
#define exfat_bswap16(x) bswap16(x)
--- /dev/null
+/*
+ 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 <strings.h>
+
+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;
+}