summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorXinyu Chen <xinyu.chen@freescale.com>2012-11-07 17:29:11 +0800
committerMatthias Rabe <matthias.rabe@sigma-chemnitz.de>2015-09-01 15:36:16 +0200
commit4a903df0ce3dc73f6d3de94aa61d6962592e11cd (patch)
treefc6383c0be58fbe300ccd04948f19a73e789dc89 /src
parentbc91ed70cad43bc4a6d0b3dd734c48c7e5668de2 (diff)
downloadntfs-3g-4a903df0ce3dc73f6d3de94aa61d6962592e11cd.tar.bz2
ntfs-3g-4a903df0ce3dc73f6d3de94aa61d6962592e11cd.zip
Initial version of ntfs-3g 2012.1.15
Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am86
-rw-r--r--src/Makefile.in848
-rw-r--r--src/lowntfs-3g.c3943
-rw-r--r--src/ntfs-3g.8.in428
-rw-r--r--src/ntfs-3g.c3862
-rw-r--r--src/ntfs-3g.probe.8.in81
-rw-r--r--src/ntfs-3g.probe.c163
-rw-r--r--src/ntfs-3g.secaudit.8.in171
-rw-r--r--src/ntfs-3g.usermap.8.in96
-rw-r--r--src/ntfs-3g_common.c690
-rw-r--r--src/ntfs-3g_common.h183
-rw-r--r--src/secaudit.c7284
-rw-r--r--src/secaudit.h736
-rw-r--r--src/usermap.c1353
14 files changed, 19924 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..89ac5ce
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,86 @@
+
+EXTRA_DIST = secaudit.h ntfs-3g_common.h
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+if FUSE_INTERNAL
+FUSE_CFLAGS = -I$(top_srcdir)/include/fuse-lite
+FUSE_LIBS = $(top_builddir)/libfuse-lite/libfuse-lite.la
+else
+FUSE_CFLAGS = $(FUSE_MODULE_CFLAGS)
+FUSE_LIBS = $(FUSE_MODULE_LIBS)
+endif
+
+if ENABLE_NTFS_3G
+
+bin_PROGRAMS = ntfs-3g.probe \
+ ntfs-3g.usermap \
+ ntfs-3g.secaudit
+rootbin_PROGRAMS = ntfs-3g lowntfs-3g
+rootsbin_DATA = #Create directory
+man_MANS = ntfs-3g.8 ntfs-3g.probe.8 \
+ ntfs-3g.usermap.8 \
+ ntfs-3g.secaudit.8
+
+ntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la
+if REALLYSTATIC
+ntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static
+endif
+ntfs_3g_CFLAGS = \
+ $(AM_CFLAGS) \
+ -DFUSE_USE_VERSION=26 \
+ $(FUSE_CFLAGS) \
+ -I$(top_srcdir)/include/ntfs-3g
+ntfs_3g_SOURCES = ntfs-3g.c ntfs-3g_common.c
+
+lowntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la
+if REALLYSTATIC
+lowntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static
+endif
+lowntfs_3g_CFLAGS = \
+ $(AM_CFLAGS) \
+ -DFUSE_USE_VERSION=26 \
+ $(FUSE_CFLAGS) \
+ -I$(top_srcdir)/include/ntfs-3g
+lowntfs_3g_SOURCES = lowntfs-3g.c ntfs-3g_common.c
+
+ntfs_3g_probe_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la
+ntfs_3g_usermap_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la
+ntfs_3g_secaudit_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la
+if REALLYSTATIC
+ntfs_3g_probe_LDFLAGS = $(AM_LDFLAGS) -all-static
+ntfs_3g_usermap_LDFLAGS = $(AM_LDFLAGS) -all-static
+ntfs_3g_secaudit_LDFLAGS = $(AM_LDFLAGS) -all-static
+endif
+ntfs_3g_probe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g
+ntfs_3g_usermap_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g
+ntfs_3g_secaudit_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g
+ntfs_3g_probe_SOURCES = ntfs-3g.probe.c
+ntfs_3g_usermap_SOURCES = usermap.c
+ntfs_3g_secaudit_SOURCES = secaudit.c
+
+drivers : $(FUSE_LIBS) ntfs-3g lowntfs-3g
+
+if RUN_LDCONFIG
+install-exec-hook:
+ $(LDCONFIG)
+endif
+
+if ENABLE_MOUNT_HELPER
+install-exec-local: install-rootbinPROGRAMS
+ $(MKDIR_P) "$(DESTDIR)/sbin"
+ $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)/sbin/mount.ntfs-3g"
+ $(LN_S) -f "$(rootbindir)/lowntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g"
+endif
+
+install-data-local: install-man8
+ $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8"
+ $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.lowntfs-3g.8"
+
+uninstall-local:
+ $(RM) -f "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8"
+if ENABLE_MOUNT_HELPER
+ $(RM) -f "$(DESTDIR)/sbin/mount.ntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g"
+endif
+
+endif # ENABLE_NTFS_3G
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..b987c66
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,848 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 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@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@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@
+target_triplet = @target@
+@ENABLE_NTFS_3G_TRUE@bin_PROGRAMS = ntfs-3g.probe$(EXEEXT) \
+@ENABLE_NTFS_3G_TRUE@ ntfs-3g.usermap$(EXEEXT) \
+@ENABLE_NTFS_3G_TRUE@ ntfs-3g.secaudit$(EXEEXT)
+@ENABLE_NTFS_3G_TRUE@rootbin_PROGRAMS = ntfs-3g$(EXEEXT) \
+@ENABLE_NTFS_3G_TRUE@ lowntfs-3g$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/ntfs-3g.8.in $(srcdir)/ntfs-3g.probe.8.in \
+ $(srcdir)/ntfs-3g.secaudit.8.in $(srcdir)/ntfs-3g.usermap.8.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = ntfs-3g.8 ntfs-3g.probe.8 ntfs-3g.usermap.8 \
+ ntfs-3g.secaudit.8
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(rootbindir)" \
+ "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(rootsbindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+rootbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS) $(rootbin_PROGRAMS)
+am__lowntfs_3g_SOURCES_DIST = lowntfs-3g.c ntfs-3g_common.c
+@ENABLE_NTFS_3G_TRUE@am_lowntfs_3g_OBJECTS = \
+@ENABLE_NTFS_3G_TRUE@ lowntfs_3g-lowntfs-3g.$(OBJEXT) \
+@ENABLE_NTFS_3G_TRUE@ lowntfs_3g-ntfs-3g_common.$(OBJEXT)
+lowntfs_3g_OBJECTS = $(am_lowntfs_3g_OBJECTS)
+am__DEPENDENCIES_1 =
+@FUSE_INTERNAL_FALSE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+@FUSE_INTERNAL_TRUE@am__DEPENDENCIES_2 = $(top_builddir)/libfuse-lite/libfuse-lite.la
+@ENABLE_NTFS_3G_TRUE@lowntfs_3g_DEPENDENCIES = $(am__DEPENDENCIES_2) \
+@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la
+lowntfs_3g_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(lowntfs_3g_CFLAGS) \
+ $(CFLAGS) $(lowntfs_3g_LDFLAGS) $(LDFLAGS) -o $@
+am__ntfs_3g_SOURCES_DIST = ntfs-3g.c ntfs-3g_common.c
+@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_OBJECTS = ntfs_3g-ntfs-3g.$(OBJEXT) \
+@ENABLE_NTFS_3G_TRUE@ ntfs_3g-ntfs-3g_common.$(OBJEXT)
+ntfs_3g_OBJECTS = $(am_ntfs_3g_OBJECTS)
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_DEPENDENCIES = $(am__DEPENDENCIES_2) \
+@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la
+ntfs_3g_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(ntfs_3g_CFLAGS) $(CFLAGS) \
+ $(ntfs_3g_LDFLAGS) $(LDFLAGS) -o $@
+am__ntfs_3g_probe_SOURCES_DIST = ntfs-3g.probe.c
+@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_probe_OBJECTS = \
+@ENABLE_NTFS_3G_TRUE@ ntfs_3g_probe-ntfs-3g.probe.$(OBJEXT)
+ntfs_3g_probe_OBJECTS = $(am_ntfs_3g_probe_OBJECTS)
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_DEPENDENCIES = \
+@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la
+ntfs_3g_probe_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ntfs_3g_probe_CFLAGS) \
+ $(CFLAGS) $(ntfs_3g_probe_LDFLAGS) $(LDFLAGS) -o $@
+am__ntfs_3g_secaudit_SOURCES_DIST = secaudit.c
+@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_secaudit_OBJECTS = \
+@ENABLE_NTFS_3G_TRUE@ ntfs_3g_secaudit-secaudit.$(OBJEXT)
+ntfs_3g_secaudit_OBJECTS = $(am_ntfs_3g_secaudit_OBJECTS)
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_DEPENDENCIES = \
+@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la
+ntfs_3g_secaudit_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ntfs_3g_secaudit_CFLAGS) \
+ $(CFLAGS) $(ntfs_3g_secaudit_LDFLAGS) $(LDFLAGS) -o $@
+am__ntfs_3g_usermap_SOURCES_DIST = usermap.c
+@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_usermap_OBJECTS = \
+@ENABLE_NTFS_3G_TRUE@ ntfs_3g_usermap-usermap.$(OBJEXT)
+ntfs_3g_usermap_OBJECTS = $(am_ntfs_3g_usermap_OBJECTS)
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_DEPENDENCIES = \
+@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la
+ntfs_3g_usermap_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ntfs_3g_usermap_CFLAGS) \
+ $(CFLAGS) $(ntfs_3g_usermap_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(lowntfs_3g_SOURCES) $(ntfs_3g_SOURCES) \
+ $(ntfs_3g_probe_SOURCES) $(ntfs_3g_secaudit_SOURCES) \
+ $(ntfs_3g_usermap_SOURCES)
+DIST_SOURCES = $(am__lowntfs_3g_SOURCES_DIST) \
+ $(am__ntfs_3g_SOURCES_DIST) $(am__ntfs_3g_probe_SOURCES_DIST) \
+ $(am__ntfs_3g_secaudit_SOURCES_DIST) \
+ $(am__ntfs_3g_usermap_SOURCES_DIST)
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+rootsbinDATA_INSTALL = $(INSTALL_DATA)
+DATA = $(rootsbin_DATA)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@
+FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDCONFIG = @LDCONFIG@
+LDFLAGS = @LDFLAGS@
+LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@
+MKNTFS_LIBS = @MKNTFS_LIBS@
+MV = @MV@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+OUTPUT_FORMAT = @OUTPUT_FORMAT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+RM = @RM@
+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_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+all_includes = @all_includes@
+all_libraries = @all_libraries@
+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@
+ntfs3gincludedir = @ntfs3gincludedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rootbindir = @rootbindir@
+rootlibdir = @rootlibdir@
+rootsbindir = @rootsbindir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = secaudit.h ntfs-3g_common.h
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+@FUSE_INTERNAL_FALSE@FUSE_CFLAGS = $(FUSE_MODULE_CFLAGS)
+@FUSE_INTERNAL_TRUE@FUSE_CFLAGS = -I$(top_srcdir)/include/fuse-lite
+@FUSE_INTERNAL_FALSE@FUSE_LIBS = $(FUSE_MODULE_LIBS)
+@FUSE_INTERNAL_TRUE@FUSE_LIBS = $(top_builddir)/libfuse-lite/libfuse-lite.la
+@ENABLE_NTFS_3G_TRUE@rootsbin_DATA = #Create directory
+@ENABLE_NTFS_3G_TRUE@man_MANS = ntfs-3g.8 ntfs-3g.probe.8 \
+@ENABLE_NTFS_3G_TRUE@ ntfs-3g.usermap.8 \
+@ENABLE_NTFS_3G_TRUE@ ntfs-3g.secaudit.8
+
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la
+@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_CFLAGS = \
+@ENABLE_NTFS_3G_TRUE@ $(AM_CFLAGS) \
+@ENABLE_NTFS_3G_TRUE@ -DFUSE_USE_VERSION=26 \
+@ENABLE_NTFS_3G_TRUE@ $(FUSE_CFLAGS) \
+@ENABLE_NTFS_3G_TRUE@ -I$(top_srcdir)/include/ntfs-3g
+
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_SOURCES = ntfs-3g.c ntfs-3g_common.c
+@ENABLE_NTFS_3G_TRUE@lowntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la
+@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@lowntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static
+@ENABLE_NTFS_3G_TRUE@lowntfs_3g_CFLAGS = \
+@ENABLE_NTFS_3G_TRUE@ $(AM_CFLAGS) \
+@ENABLE_NTFS_3G_TRUE@ -DFUSE_USE_VERSION=26 \
+@ENABLE_NTFS_3G_TRUE@ $(FUSE_CFLAGS) \
+@ENABLE_NTFS_3G_TRUE@ -I$(top_srcdir)/include/ntfs-3g
+
+@ENABLE_NTFS_3G_TRUE@lowntfs_3g_SOURCES = lowntfs-3g.c ntfs-3g_common.c
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la
+@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_probe_LDFLAGS = $(AM_LDFLAGS) -all-static
+@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_usermap_LDFLAGS = $(AM_LDFLAGS) -all-static
+@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_secaudit_LDFLAGS = $(AM_LDFLAGS) -all-static
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_SOURCES = ntfs-3g.probe.c
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_SOURCES = usermap.c
+@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_SOURCES = secaudit.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+ntfs-3g.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.8.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+ntfs-3g.probe.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.probe.8.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+ntfs-3g.usermap.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.usermap.8.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+ntfs-3g.secaudit.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.secaudit.8.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+install-rootbinPROGRAMS: $(rootbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(rootbindir)" || $(MKDIR_P) "$(DESTDIR)$(rootbindir)"
+ @list='$(rootbin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(rootbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(rootbindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(rootbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(rootbindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-rootbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(rootbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(rootbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(rootbindir)/$$f"; \
+ done
+
+clean-rootbinPROGRAMS:
+ @list='$(rootbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+lowntfs-3g$(EXEEXT): $(lowntfs_3g_OBJECTS) $(lowntfs_3g_DEPENDENCIES)
+ @rm -f lowntfs-3g$(EXEEXT)
+ $(lowntfs_3g_LINK) $(lowntfs_3g_OBJECTS) $(lowntfs_3g_LDADD) $(LIBS)
+ntfs-3g$(EXEEXT): $(ntfs_3g_OBJECTS) $(ntfs_3g_DEPENDENCIES)
+ @rm -f ntfs-3g$(EXEEXT)
+ $(ntfs_3g_LINK) $(ntfs_3g_OBJECTS) $(ntfs_3g_LDADD) $(LIBS)
+ntfs-3g.probe$(EXEEXT): $(ntfs_3g_probe_OBJECTS) $(ntfs_3g_probe_DEPENDENCIES)
+ @rm -f ntfs-3g.probe$(EXEEXT)
+ $(ntfs_3g_probe_LINK) $(ntfs_3g_probe_OBJECTS) $(ntfs_3g_probe_LDADD) $(LIBS)
+ntfs-3g.secaudit$(EXEEXT): $(ntfs_3g_secaudit_OBJECTS) $(ntfs_3g_secaudit_DEPENDENCIES)
+ @rm -f ntfs-3g.secaudit$(EXEEXT)
+ $(ntfs_3g_secaudit_LINK) $(ntfs_3g_secaudit_OBJECTS) $(ntfs_3g_secaudit_LDADD) $(LIBS)
+ntfs-3g.usermap$(EXEEXT): $(ntfs_3g_usermap_OBJECTS) $(ntfs_3g_usermap_DEPENDENCIES)
+ @rm -f ntfs-3g.usermap$(EXEEXT)
+ $(ntfs_3g_usermap_LINK) $(ntfs_3g_usermap_OBJECTS) $(ntfs_3g_usermap_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lowntfs_3g-lowntfs-3g.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lowntfs_3g-ntfs-3g_common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g-ntfs-3g.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g-ntfs-3g_common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g_secaudit-secaudit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g_usermap-usermap.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+lowntfs_3g-lowntfs-3g.o: lowntfs-3g.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-lowntfs-3g.o -MD -MP -MF $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo -c -o lowntfs_3g-lowntfs-3g.o `test -f 'lowntfs-3g.c' || echo '$(srcdir)/'`lowntfs-3g.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo $(DEPDIR)/lowntfs_3g-lowntfs-3g.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lowntfs-3g.c' object='lowntfs_3g-lowntfs-3g.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-lowntfs-3g.o `test -f 'lowntfs-3g.c' || echo '$(srcdir)/'`lowntfs-3g.c
+
+lowntfs_3g-lowntfs-3g.obj: lowntfs-3g.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-lowntfs-3g.obj -MD -MP -MF $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo -c -o lowntfs_3g-lowntfs-3g.obj `if test -f 'lowntfs-3g.c'; then $(CYGPATH_W) 'lowntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/lowntfs-3g.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo $(DEPDIR)/lowntfs_3g-lowntfs-3g.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lowntfs-3g.c' object='lowntfs_3g-lowntfs-3g.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-lowntfs-3g.obj `if test -f 'lowntfs-3g.c'; then $(CYGPATH_W) 'lowntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/lowntfs-3g.c'; fi`
+
+lowntfs_3g-ntfs-3g_common.o: ntfs-3g_common.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-ntfs-3g_common.o -MD -MP -MF $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo -c -o lowntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g_common.c' object='lowntfs_3g-ntfs-3g_common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c
+
+lowntfs_3g-ntfs-3g_common.obj: ntfs-3g_common.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-ntfs-3g_common.obj -MD -MP -MF $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo -c -o lowntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g_common.c' object='lowntfs_3g-ntfs-3g_common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi`
+
+ntfs_3g-ntfs-3g.o: ntfs-3g.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g.o -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo -c -o ntfs_3g-ntfs-3g.o `test -f 'ntfs-3g.c' || echo '$(srcdir)/'`ntfs-3g.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g.c' object='ntfs_3g-ntfs-3g.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g.o `test -f 'ntfs-3g.c' || echo '$(srcdir)/'`ntfs-3g.c
+
+ntfs_3g-ntfs-3g.obj: ntfs-3g.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g.obj -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo -c -o ntfs_3g-ntfs-3g.obj `if test -f 'ntfs-3g.c'; then $(CYGPATH_W) 'ntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g.c' object='ntfs_3g-ntfs-3g.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g.obj `if test -f 'ntfs-3g.c'; then $(CYGPATH_W) 'ntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.c'; fi`
+
+ntfs_3g-ntfs-3g_common.o: ntfs-3g_common.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g_common.o -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo -c -o ntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g_common.c' object='ntfs_3g-ntfs-3g_common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c
+
+ntfs_3g-ntfs-3g_common.obj: ntfs-3g_common.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g_common.obj -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo -c -o ntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g_common.c' object='ntfs_3g-ntfs-3g_common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi`
+
+ntfs_3g_probe-ntfs-3g.probe.o: ntfs-3g.probe.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -MT ntfs_3g_probe-ntfs-3g.probe.o -MD -MP -MF $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo -c -o ntfs_3g_probe-ntfs-3g.probe.o `test -f 'ntfs-3g.probe.c' || echo '$(srcdir)/'`ntfs-3g.probe.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g.probe.c' object='ntfs_3g_probe-ntfs-3g.probe.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -c -o ntfs_3g_probe-ntfs-3g.probe.o `test -f 'ntfs-3g.probe.c' || echo '$(srcdir)/'`ntfs-3g.probe.c
+
+ntfs_3g_probe-ntfs-3g.probe.obj: ntfs-3g.probe.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -MT ntfs_3g_probe-ntfs-3g.probe.obj -MD -MP -MF $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo -c -o ntfs_3g_probe-ntfs-3g.probe.obj `if test -f 'ntfs-3g.probe.c'; then $(CYGPATH_W) 'ntfs-3g.probe.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.probe.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntfs-3g.probe.c' object='ntfs_3g_probe-ntfs-3g.probe.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -c -o ntfs_3g_probe-ntfs-3g.probe.obj `if test -f 'ntfs-3g.probe.c'; then $(CYGPATH_W) 'ntfs-3g.probe.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.probe.c'; fi`
+
+ntfs_3g_secaudit-secaudit.o: secaudit.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -MT ntfs_3g_secaudit-secaudit.o -MD -MP -MF $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo -c -o ntfs_3g_secaudit-secaudit.o `test -f 'secaudit.c' || echo '$(srcdir)/'`secaudit.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo $(DEPDIR)/ntfs_3g_secaudit-secaudit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='secaudit.c' object='ntfs_3g_secaudit-secaudit.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -c -o ntfs_3g_secaudit-secaudit.o `test -f 'secaudit.c' || echo '$(srcdir)/'`secaudit.c
+
+ntfs_3g_secaudit-secaudit.obj: secaudit.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -MT ntfs_3g_secaudit-secaudit.obj -MD -MP -MF $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo -c -o ntfs_3g_secaudit-secaudit.obj `if test -f 'secaudit.c'; then $(CYGPATH_W) 'secaudit.c'; else $(CYGPATH_W) '$(srcdir)/secaudit.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo $(DEPDIR)/ntfs_3g_secaudit-secaudit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='secaudit.c' object='ntfs_3g_secaudit-secaudit.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -c -o ntfs_3g_secaudit-secaudit.obj `if test -f 'secaudit.c'; then $(CYGPATH_W) 'secaudit.c'; else $(CYGPATH_W) '$(srcdir)/secaudit.c'; fi`
+
+ntfs_3g_usermap-usermap.o: usermap.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -MT ntfs_3g_usermap-usermap.o -MD -MP -MF $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo -c -o ntfs_3g_usermap-usermap.o `test -f 'usermap.c' || echo '$(srcdir)/'`usermap.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo $(DEPDIR)/ntfs_3g_usermap-usermap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='usermap.c' object='ntfs_3g_usermap-usermap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -c -o ntfs_3g_usermap-usermap.o `test -f 'usermap.c' || echo '$(srcdir)/'`usermap.c
+
+ntfs_3g_usermap-usermap.obj: usermap.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -MT ntfs_3g_usermap-usermap.obj -MD -MP -MF $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo -c -o ntfs_3g_usermap-usermap.obj `if test -f 'usermap.c'; then $(CYGPATH_W) 'usermap.c'; else $(CYGPATH_W) '$(srcdir)/usermap.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo $(DEPDIR)/ntfs_3g_usermap-usermap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='usermap.c' object='ntfs_3g_usermap-usermap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -c -o ntfs_3g_usermap-usermap.obj `if test -f 'usermap.c'; then $(CYGPATH_W) 'usermap.c'; else $(CYGPATH_W) '$(srcdir)/usermap.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+install-rootsbinDATA: $(rootsbin_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(rootsbindir)" || $(MKDIR_P) "$(DESTDIR)$(rootsbindir)"
+ @list='$(rootsbin_DATA)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(rootsbinDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(rootsbindir)/$$f'"; \
+ $(rootsbinDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(rootsbindir)/$$f"; \
+ done
+
+uninstall-rootsbinDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(rootsbin_DATA)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(rootsbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(rootsbindir)/$$f"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(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 $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$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) $(MANS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(rootbindir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(rootsbindir)"; 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:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+@ENABLE_NTFS_3G_FALSE@uninstall-local:
+@ENABLE_NTFS_3G_FALSE@install-data-local:
+@ENABLE_MOUNT_HELPER_FALSE@install-exec-local:
+@ENABLE_NTFS_3G_FALSE@install-exec-local:
+@ENABLE_NTFS_3G_FALSE@install-exec-hook:
+@RUN_LDCONFIG_FALSE@install-exec-hook:
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool \
+ clean-rootbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-man \
+ install-rootbinPROGRAMS install-rootsbinDATA
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-binPROGRAMS install-exec-local
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -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-binPROGRAMS uninstall-local uninstall-man \
+ uninstall-rootbinPROGRAMS uninstall-rootsbinDATA
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-exec-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool clean-rootbinPROGRAMS ctags \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-data-local install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-hook \
+ install-exec-local install-html install-html-am install-info \
+ install-info-am install-man install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am \
+ install-rootbinPROGRAMS install-rootsbinDATA 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 uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-local uninstall-man uninstall-man8 \
+ uninstall-rootbinPROGRAMS uninstall-rootsbinDATA
+
+
+@ENABLE_NTFS_3G_TRUE@drivers : $(FUSE_LIBS) ntfs-3g lowntfs-3g
+
+@ENABLE_NTFS_3G_TRUE@@RUN_LDCONFIG_TRUE@install-exec-hook:
+@ENABLE_NTFS_3G_TRUE@@RUN_LDCONFIG_TRUE@ $(LDCONFIG)
+
+@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@install-exec-local: install-rootbinPROGRAMS
+@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(MKDIR_P) "$(DESTDIR)/sbin"
+@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)/sbin/mount.ntfs-3g"
+@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f "$(rootbindir)/lowntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g"
+
+@ENABLE_NTFS_3G_TRUE@install-data-local: install-man8
+@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8"
+@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.lowntfs-3g.8"
+
+@ENABLE_NTFS_3G_TRUE@uninstall-local:
+@ENABLE_NTFS_3G_TRUE@ $(RM) -f "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8"
+@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(RM) -f "$(DESTDIR)/sbin/mount.ntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g"
+# 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/src/lowntfs-3g.c b/src/lowntfs-3g.c
new file mode 100644
index 0000000..a281da6
--- /dev/null
+++ b/src/lowntfs-3g.c
@@ -0,0 +1,3943 @@
+/**
+ * ntfs-3g - Third Generation NTFS Driver
+ *
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ * Copyright (c) 2005 Yuval Fledel
+ * Copyright (c) 2006-2009 Szabolcs Szakacsits
+ * Copyright (c) 2007-2011 Jean-Pierre Andre
+ * Copyright (c) 2009 Erik Larsson
+ *
+ * This file is originated from the Linux-NTFS project.
+ *
+ * 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <fuse.h>
+#include <fuse_lowlevel.h>
+
+#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
+#error "***********************************************************"
+#error "* *"
+#error "* Compilation requires at least FUSE version 2.6.0! *"
+#error "* *"
+#error "***********************************************************"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <signal.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <syslog.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#if defined(__APPLE__) || defined(__DARWIN__)
+#include <sys/dirent.h>
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+
+#include "compat.h"
+#include "attrib.h"
+#include "inode.h"
+#include "volume.h"
+#include "dir.h"
+#include "unistr.h"
+#include "layout.h"
+#include "index.h"
+#include "ntfstime.h"
+#include "security.h"
+#include "reparse.h"
+#include "object_id.h"
+#include "efs.h"
+#include "logging.h"
+#include "xattrs.h"
+#include "misc.h"
+
+#include "ntfs-3g_common.h"
+
+/*
+ * The following permission checking modes are governed by
+ * the LPERMSCONFIG value in param.h
+ */
+
+/* ACLS may be checked by kernel (requires a fuse patch) or here */
+#define KERNELACLS ((LPERMSCONFIG > 6) & (LPERMSCONFIG < 10))
+/* basic permissions may be checked by kernel or here */
+#define KERNELPERMS (((LPERMSCONFIG - 1) % 6) < 3)
+/* may want to use fuse/kernel cacheing */
+#define CACHEING (!(LPERMSCONFIG % 3))
+
+#if KERNELACLS & !KERNELPERMS
+#error "Incompatible options KERNELACLS and KERNELPERMS"
+#endif
+
+#if CACHEING & (KERNELACLS | !KERNELPERMS)
+#warning "Fuse cacheing is only usable with basic permissions checked by kernel"
+#endif
+
+#if !CACHEING
+#define ATTR_TIMEOUT 0.0
+#define ENTRY_TIMEOUT 0.0
+#else
+ /*
+ * FUSE cacheing is only usable with basic permissions
+ * checked by the kernel with external fuse >= 2.8
+ */
+#define ATTR_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 1.0 : 0.0)
+#define ENTRY_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 1.0 : 0.0)
+#endif
+#define GHOSTLTH 40 /* max length of a ghost file name - see ghostformat */
+
+ /* sometimes the kernel cannot check access */
+#define ntfs_real_allowed_access(scx, ni, type) ntfs_allowed_access(scx, ni, type)
+#if POSIXACLS & KERNELPERMS & !KERNELACLS
+ /* short-circuit if PERMS checked by kernel and ACLs by fs */
+#define ntfs_allowed_access(scx, ni, type) \
+ ((scx)->vol->secure_flags & (1 << SECURITY_DEFAULT) \
+ ? 1 : ntfs_allowed_access(scx, ni, type))
+#endif
+
+#define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE
+#define INODE(ino) ((ino) == 1 ? (MFT_REF)FILE_root : (MFT_REF)(ino))
+
+typedef enum {
+ FSTYPE_NONE,
+ FSTYPE_UNKNOWN,
+ FSTYPE_FUSE,
+ FSTYPE_FUSEBLK
+} fuse_fstype;
+
+typedef struct fill_item {
+ struct fill_item *next;
+ size_t bufsize;
+ size_t off;
+ char buf[0];
+} ntfs_fuse_fill_item_t;
+
+typedef struct fill_context {
+ struct fill_item *first;
+ struct fill_item *last;
+ fuse_req_t req;
+ fuse_ino_t ino;
+ BOOL filled;
+} ntfs_fuse_fill_context_t;
+
+struct open_file {
+ struct open_file *next;
+ struct open_file *previous;
+ long long ghost;
+ fuse_ino_t ino;
+ fuse_ino_t parent;
+ int state;
+} ;
+
+enum {
+ CLOSE_GHOST = 1,
+ CLOSE_COMPRESSED = 2,
+ CLOSE_ENCRYPTED = 4,
+ CLOSE_DMTIME = 8
+};
+
+static struct ntfs_options opts;
+
+const char *EXEC_NAME = "lowntfs-3g";
+
+static ntfs_fuse_context_t *ctx;
+static u32 ntfs_sequence;
+static const char ghostformat[] = ".ghost-ntfs-3g-%020llu";
+
+static const char *usage_msg =
+"\n"
+"%s %s %s %d - Third Generation NTFS Driver\n"
+"\t\tConfiguration type %d, "
+#ifdef HAVE_SETXATTR
+"XATTRS are on, "
+#else
+"XATTRS are off, "
+#endif
+#if POSIXACLS
+"POSIX ACLS are on\n"
+#else
+"POSIX ACLS are off\n"
+#endif
+"\n"
+"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
+"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
+"Copyright (C) 2007-2011 Jean-Pierre Andre\n"
+"Copyright (C) 2009 Erik Larsson\n"
+"\n"
+"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
+"\n"
+"Options: ro (read-only mount), remove_hiberfile, uid=, gid=,\n"
+" umask=, fmask=, dmask=, streams_interface=.\n"
+" Please see the details in the manual (type: man ntfs-3g).\n"
+"\n"
+"Example: ntfs-3g /dev/sda1 /mnt/windows\n"
+"\n"
+"%s";
+
+static const char ntfs_bad_reparse[] = "unsupported reparse point";
+
+#ifdef FUSE_INTERNAL
+int drop_privs(void);
+int restore_privs(void);
+#else
+/*
+ * setuid and setgid root ntfs-3g denies to start with external FUSE,
+ * therefore the below functions are no-op in such case.
+ */
+static int drop_privs(void) { return 0; }
+#if defined(linux) || defined(__uClinux__)
+static int restore_privs(void) { return 0; }
+#endif
+
+static const char *setuid_msg =
+"Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n"
+"external FUSE library. Either remove the setuid/setgid bit from the binary\n"
+"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n"
+"Please see more information at\n"
+"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
+
+static const char *unpriv_fuseblk_msg =
+"Unprivileged user can not mount NTFS block devices using the external FUSE\n"
+"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n"
+"FUSE support and make it setuid root. Please see more information at\n"
+"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
+#endif
+
+
+static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask)
+{
+ if (ctx->atime == ATIME_DISABLED)
+ mask &= ~NTFS_UPDATE_ATIME;
+ else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME &&
+ (le64_to_cpu(ni->last_access_time)
+ >= le64_to_cpu(ni->last_data_change_time)) &&
+ (le64_to_cpu(ni->last_access_time)
+ >= le64_to_cpu(ni->last_mft_change_time)))
+ return;
+ ntfs_inode_update_times(ni, mask);
+}
+
+static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol)
+{
+ ntfs_attr *na = vol->mftbmp_na;
+ s64 nr_free = ntfs_attr_get_free_bits(na);
+
+ if (nr_free >= 0)
+ nr_free += (na->allocated_size - na->data_size) << 3;
+ return nr_free;
+}
+
+/*
+ * Fill a security context as needed by security functions
+ * returns TRUE if there is a user mapping,
+ * FALSE if there is none
+ * This is not an error and the context is filled anyway,
+ * it is used for implicit Windows-like inheritance
+ */
+
+static BOOL ntfs_fuse_fill_security_context(fuse_req_t req,
+ struct SECURITY_CONTEXT *scx)
+{
+ const struct fuse_ctx *fusecontext;
+
+ scx->vol = ctx->vol;
+ scx->mapping[MAPUSERS] = ctx->security.mapping[MAPUSERS];
+ scx->mapping[MAPGROUPS] = ctx->security.mapping[MAPGROUPS];
+ scx->pseccache = &ctx->seccache;
+ if (req) {
+ fusecontext = fuse_req_ctx(req);
+ scx->uid = fusecontext->uid;
+ scx->gid = fusecontext->gid;
+ scx->tid = fusecontext->pid;
+#ifdef FUSE_CAP_DONT_MASK
+ /* the umask can be processed by the file system */
+ scx->umask = fusecontext->umask;
+#else
+ /* the umask if forced by fuse on creation */
+ scx->umask = 0;
+#endif
+
+ } else {
+ scx->uid = 0;
+ scx->gid = 0;
+ scx->tid = 0;
+ scx->umask = 0;
+ }
+ return (ctx->security.mapping[MAPUSERS] != (struct MAPPING*)NULL);
+}
+
+static u64 ntfs_fuse_inode_lookup(fuse_ino_t parent, const char *name)
+{
+ u64 ino = (u64)-1;
+ u64 inum;
+ ntfs_inode *dir_ni;
+
+ /* Open target directory. */
+ dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
+ if (dir_ni) {
+ /* Lookup file */
+ inum = ntfs_inode_lookup_by_mbsname(dir_ni, name);
+ /* never return inodes 0 and 1 */
+ if (MREF(inum) <= 1) {
+ inum = (u64)-1;
+ errno = ENOENT;
+ }
+ if (ntfs_inode_close(dir_ni)
+ || (inum == (u64)-1))
+ ino = (u64)-1;
+ else
+ ino = MREF(inum);
+ }
+ return (ino);
+}
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+
+/*
+ * Check access to parent directory
+ *
+ * file inode is only opened when not fed in and S_ISVTX is requested,
+ * when already open and S_ISVTX, it *HAS TO* be fed in.
+ *
+ * returns 1 if allowed,
+ * 0 if not allowed or some error occurred (errno tells why)
+ */
+
+static int ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
+ ntfs_inode *dir_ni, fuse_ino_t ino,
+ ntfs_inode *ni, mode_t accesstype)
+{
+ int allowed;
+ ntfs_inode *ni2;
+ struct stat stbuf;
+
+ allowed = ntfs_allowed_access(scx, dir_ni, accesstype);
+ /*
+ * for an not-owned sticky directory, have to
+ * check whether file itself is owned
+ */
+ if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
+ && (allowed == 2)) {
+ if (ni)
+ ni2 = ni;
+ else
+ ni2 = ntfs_inode_open(ctx->vol, INODE(ino));
+ allowed = 0;
+ if (ni2) {
+ allowed = (ntfs_get_owner_mode(scx,ni2,&stbuf) >= 0)
+ && (stbuf.st_uid == scx->uid);
+ if (!ni)
+ ntfs_inode_close(ni2);
+ }
+ }
+ return (allowed);
+}
+
+#endif /* !KERNELPERMS | (POSIXACLS & !KERNELACLS) */
+
+/**
+ * ntfs_fuse_statfs - return information about mounted NTFS volume
+ * @path: ignored (but fuse requires it)
+ * @sfs: statfs structure in which to return the information
+ *
+ * Return information about the mounted NTFS volume @sb in the statfs structure
+ * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is
+ * called). We interpret the values to be correct of the moment in time at
+ * which we are called. Most values are variable otherwise and this isn't just
+ * the free values but the totals as well. For example we can increase the
+ * total number of file nodes if we run out and we can keep doing this until
+ * there is no more space on the volume left at all.
+ *
+ * This code based on ntfs_statfs from ntfs kernel driver.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+
+static void ntfs_fuse_statfs(fuse_req_t req,
+ fuse_ino_t ino __attribute__((unused)))
+{
+ struct statvfs sfs;
+ s64 size;
+ int delta_bits;
+ ntfs_volume *vol;
+
+ vol = ctx->vol;
+ if (vol) {
+ /*
+ * File system block size. Used to calculate used/free space by df.
+ * Incorrectly documented as "optimal transfer block size".
+ */
+ sfs.f_bsize = vol->cluster_size;
+
+ /* Fundamental file system block size, used as the unit. */
+ sfs.f_frsize = vol->cluster_size;
+
+ /*
+ * Total number of blocks on file system in units of f_frsize.
+ * Since inodes are also stored in blocks ($MFT is a file) hence
+ * this is the number of clusters on the volume.
+ */
+ sfs.f_blocks = vol->nr_clusters;
+
+ /* Free blocks available for all and for non-privileged processes. */
+ size = vol->free_clusters;
+ if (size < 0)
+ size = 0;
+ sfs.f_bavail = sfs.f_bfree = size;
+
+ /* Free inodes on the free space */
+ delta_bits = vol->cluster_size_bits - vol->mft_record_size_bits;
+ if (delta_bits >= 0)
+ size <<= delta_bits;
+ else
+ size >>= -delta_bits;
+
+ /* Number of inodes at this point in time. */
+ sfs.f_files = (vol->mftbmp_na->allocated_size << 3) + size;
+
+ /* Free inodes available for all and for non-privileged processes. */
+ size += vol->free_mft_records;
+ if (size < 0)
+ size = 0;
+ sfs.f_ffree = sfs.f_favail = size;
+
+ /* Maximum length of filenames. */
+ sfs.f_namemax = NTFS_MAX_NAME_LEN;
+ fuse_reply_statfs(req, &sfs);
+ } else
+ fuse_reply_err(req, ENODEV);
+
+}
+
+static void set_fuse_error(int *err)
+{
+ if (!*err)
+ *err = -errno;
+}
+
+#if 0 && (defined(__APPLE__) || defined(__DARWIN__)) /* Unfinished. */
+static int ntfs_macfuse_getxtimes(const char *org_path,
+ struct timespec *bkuptime, struct timespec *crtime)
+{
+ int res = 0;
+ ntfs_inode *ni;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len;
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ memset(bkuptime, 0, sizeof(struct timespec));
+ memset(crtime, 0, sizeof(struct timespec));
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+
+ /* We have no backup timestamp in NTFS. */
+ crtime->tv_sec = ni->creation_time;
+exit:
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+int ntfs_macfuse_setcrtime(const char *path, const struct timespec *tv)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ if (tv) {
+ ni->creation_time = tv->tv_sec;
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ }
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+int ntfs_macfuse_setbkuptime(const char *path, const struct timespec *tv)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ /*
+ * Only pretending to set backup time successfully to please the APIs of
+ * Mac OS X. In reality, NTFS has no backup time.
+ */
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ ni = ntfs_pathname_to_inode(ctx-&gt;vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ if (tv) {
+ ni-&gt;last_mft_change_time = tv-&gt;tv_sec;
+ ntfs_fuse_update_times(ni, 0);
+ }
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&amp;res);
+ return res;
+}
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+
+#if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \
+ || (defined(__APPLE__) || defined(__DARWIN__))
+static void ntfs_init(void *userdata __attribute__((unused)),
+ struct fuse_conn_info *conn)
+{
+#if defined(__APPLE__) || defined(__DARWIN__)
+ FUSE_ENABLE_XTIMES(conn);
+#endif
+#ifdef FUSE_CAP_DONT_MASK
+ /* request umask not to be enforced by fuse */
+ conn->want |= FUSE_CAP_DONT_MASK;
+#endif /* defined FUSE_CAP_DONT_MASK */
+#ifdef FUSE_CAP_BIG_WRITES
+ if (ctx->big_writes
+ && ((ctx->vol->nr_clusters << ctx->vol->cluster_size_bits)
+ >= SAFE_CAPACITY_FOR_BIG_WRITES))
+ conn->want |= FUSE_CAP_BIG_WRITES;
+#endif
+}
+#endif /* defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) */
+
+static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
+ ntfs_inode *ni, struct stat *stbuf)
+{
+ int res = 0;
+ ntfs_attr *na;
+ BOOL withusermapping;
+
+ memset(stbuf, 0, sizeof(struct stat));
+ withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL);
+ if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
+ || (ni->flags & FILE_ATTR_REPARSE_POINT)) {
+ if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+ char *target;
+ int attr_size;
+
+ errno = 0;
+ target = ntfs_make_symlink(ni, ctx->abs_mnt_point,
+ &attr_size);
+ /*
+ * If the reparse point is not a valid
+ * directory junction, and there is no error
+ * we still display as a symlink
+ */
+ if (target || (errno == EOPNOTSUPP)) {
+ /* returning attribute size */
+ if (target)
+ stbuf->st_size = attr_size;
+ else
+ stbuf->st_size =
+ sizeof(ntfs_bad_reparse);
+ stbuf->st_blocks =
+ (ni->allocated_size + 511) >> 9;
+ stbuf->st_nlink =
+ le16_to_cpu(ni->mrec->link_count);
+ stbuf->st_mode = S_IFLNK;
+ free(target);
+ } else {
+ res = -errno;
+ goto exit;
+ }
+ } else {
+ /* Directory. */
+ stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
+ /* get index size, if not known */
+ if (!test_nino_flag(ni, KnownSize)) {
+ na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION,
+ NTFS_INDEX_I30, 4);
+ if (na) {
+ ni->data_size = na->data_size;
+ ni->allocated_size = na->allocated_size;
+ set_nino_flag(ni, KnownSize);
+ ntfs_attr_close(na);
+ }
+ }
+ stbuf->st_size = ni->data_size;
+ stbuf->st_blocks = ni->allocated_size >> 9;
+ stbuf->st_nlink = 1; /* Make find(1) work */
+ }
+ } else {
+ /* Regular or Interix (INTX) file. */
+ stbuf->st_mode = S_IFREG;
+ stbuf->st_size = ni->data_size;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /*
+ * return data size rounded to next 512 byte boundary for
+ * encrypted files to include padding required for decryption
+ * also include 2 bytes for padding info
+ */
+ if (ctx->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED)
+ && ni->data_size)
+ stbuf->st_size = ((ni->data_size + 511) & ~511) + 2;
+#endif /* HAVE_SETXATTR */
+ /*
+ * Temporary fix to make ActiveSync work via Samba 3.0.
+ * See more on the ntfs-3g-devel list.
+ */
+ stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
+ stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
+ if (ni->flags & FILE_ATTR_SYSTEM) {
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ stbuf->st_ino = ni->mft_no;
+ goto nodata;
+ }
+ /* Check whether it's Interix FIFO or socket. */
+ if (!(ni->flags & FILE_ATTR_HIDDEN)) {
+ /* FIFO. */
+ if (na->data_size == 0)
+ stbuf->st_mode = S_IFIFO;
+ /* Socket link. */
+ if (na->data_size == 1)
+ stbuf->st_mode = S_IFSOCK;
+ }
+ /*
+ * Check whether it's Interix symbolic link, block or
+ * character device.
+ */
+ if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)
+ + sizeof(ntfschar) * PATH_MAX
+ && (size_t)na->data_size >
+ sizeof(INTX_FILE_TYPES)) {
+ INTX_FILE *intx_file;
+
+ intx_file =
+ (INTX_FILE*)ntfs_malloc(na->data_size);
+ if (!intx_file) {
+ res = -errno;
+ ntfs_attr_close(na);
+ goto exit;
+ }
+ if (ntfs_attr_pread(na, 0, na->data_size,
+ intx_file) != na->data_size) {
+ res = -errno;
+ free(intx_file);
+ ntfs_attr_close(na);
+ goto exit;
+ }
+ if (intx_file->magic == INTX_BLOCK_DEVICE &&
+ na->data_size == (s64)offsetof(
+ INTX_FILE, device_end)) {
+ stbuf->st_mode = S_IFBLK;
+ stbuf->st_rdev = makedev(le64_to_cpu(
+ intx_file->major),
+ le64_to_cpu(
+ intx_file->minor));
+ }
+ if (intx_file->magic == INTX_CHARACTER_DEVICE &&
+ na->data_size == (s64)offsetof(
+ INTX_FILE, device_end)) {
+ stbuf->st_mode = S_IFCHR;
+ stbuf->st_rdev = makedev(le64_to_cpu(
+ intx_file->major),
+ le64_to_cpu(
+ intx_file->minor));
+ }
+ if (intx_file->magic == INTX_SYMBOLIC_LINK)
+ stbuf->st_mode = S_IFLNK;
+ free(intx_file);
+ }
+ ntfs_attr_close(na);
+ }
+ stbuf->st_mode |= (0777 & ~ctx->fmask);
+ }
+ if (withusermapping) {
+ if (ntfs_get_owner_mode(scx,ni,stbuf) < 0)
+ set_fuse_error(&res);
+ } else {
+ stbuf->st_uid = ctx->uid;
+ stbuf->st_gid = ctx->gid;
+ }
+ if (S_ISLNK(stbuf->st_mode))
+ stbuf->st_mode |= 0777;
+nodata :
+ stbuf->st_ino = ni->mft_no;
+#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC
+ stbuf->st_atimespec = ntfs2timespec(ni->last_access_time);
+ stbuf->st_ctimespec = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_mtimespec = ntfs2timespec(ni->last_data_change_time);
+#elif defined(HAVE_STRUCT_STAT_ST_ATIM)
+ stbuf->st_atim = ntfs2timespec(ni->last_access_time);
+ stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time);
+#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
+ {
+ struct timespec ts;
+
+ ts = ntfs2timespec(ni->last_access_time);
+ stbuf->st_atime = ts.tv_sec;
+ stbuf->st_atimensec = ts.tv_nsec;
+ ts = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_ctime = ts.tv_sec;
+ stbuf->st_ctimensec = ts.tv_nsec;
+ ts = ntfs2timespec(ni->last_data_change_time);
+ stbuf->st_mtime = ts.tv_sec;
+ stbuf->st_mtimensec = ts.tv_nsec;
+ }
+#else
+#warning "No known way to set nanoseconds in struct stat !"
+ {
+ struct timespec ts;
+
+ ts = ntfs2timespec(ni->last_access_time);
+ stbuf->st_atime = ts.tv_sec;
+ ts = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_ctime = ts.tv_sec;
+ ts = ntfs2timespec(ni->last_data_change_time);
+ stbuf->st_mtime = ts.tv_sec;
+ }
+#endif
+exit:
+ return (res);
+}
+
+static void ntfs_fuse_getattr(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ int res;
+ ntfs_inode *ni;
+ struct stat stbuf;
+ struct SECURITY_CONTEXT security;
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ res = -errno;
+ else {
+ ntfs_fuse_fill_security_context(req, &security);
+ res = ntfs_fuse_getstat(&security, ni, &stbuf);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+ if (!res)
+ fuse_reply_attr(req, &stbuf, ATTR_TIMEOUT);
+ else
+ fuse_reply_err(req, -res);
+}
+
+static __inline__ BOOL ntfs_fuse_fillstat(struct SECURITY_CONTEXT *scx,
+ struct fuse_entry_param *pentry, u64 iref)
+{
+ ntfs_inode *ni;
+ BOOL ok = FALSE;
+
+ pentry->ino = MREF(iref);
+ ni = ntfs_inode_open(ctx->vol, pentry->ino);
+ if (ni) {
+ if (!ntfs_fuse_getstat(scx, ni, &pentry->attr)) {
+ pentry->generation = 1;
+ pentry->attr_timeout = ATTR_TIMEOUT;
+ pentry->entry_timeout = ENTRY_TIMEOUT;
+ ok = TRUE;
+ }
+ if (ntfs_inode_close(ni))
+ ok = FALSE;
+ }
+ return (ok);
+}
+
+
+static void ntfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent,
+ const char *name)
+{
+ struct SECURITY_CONTEXT security;
+ struct fuse_entry_param entry;
+ ntfs_inode *dir_ni;
+ u64 iref;
+ BOOL ok = FALSE;
+
+ if (strlen(name) < 256) {
+ dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
+ if (dir_ni) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * make sure the parent directory is searchable
+ */
+ if (ntfs_fuse_fill_security_context(req, &security)
+ && !ntfs_allowed_access(&security,dir_ni,S_IEXEC)) {
+ ntfs_inode_close(dir_ni);
+ errno = EACCES;
+ } else {
+#else
+ ntfs_fuse_fill_security_context(req, &security);
+#endif
+ iref = ntfs_inode_lookup_by_mbsname(dir_ni,
+ name);
+ /* never return inodes 0 and 1 */
+ if (MREF(iref) <= 1) {
+ iref = (u64)-1;
+ errno = ENOENT;
+ }
+ ok = !ntfs_inode_close(dir_ni)
+ && (iref != (u64)-1)
+ && ntfs_fuse_fillstat(
+ &security,&entry,iref);
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ }
+#endif
+ }
+ } else
+ errno = ENAMETOOLONG;
+ if (!ok)
+ fuse_reply_err(req, errno);
+ else
+ fuse_reply_entry(req, &entry);
+}
+
+static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino)
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ INTX_FILE *intx_file = NULL;
+ char *buf = (char*)NULL;
+ int res = 0;
+
+ /* Get inode. */
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ /*
+ * Reparse point : analyze as a junction point
+ */
+ if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+ int attr_size;
+
+ errno = 0;
+ res = 0;
+ buf = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
+ if (!buf) {
+ if (errno == EOPNOTSUPP)
+ buf = strdup(ntfs_bad_reparse);
+ if (!buf)
+ res = -errno;
+ }
+ goto exit;
+ }
+ /* Sanity checks. */
+ if (!(ni->flags & FILE_ATTR_SYSTEM)) {
+ res = -EINVAL;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)) {
+ res = -EINVAL;
+ goto exit;
+ }
+ if ((size_t)na->data_size > sizeof(INTX_FILE_TYPES) +
+ sizeof(ntfschar) * PATH_MAX) {
+ res = -ENAMETOOLONG;
+ goto exit;
+ }
+ /* Receive file content. */
+ intx_file = (INTX_FILE*)ntfs_malloc(na->data_size);
+ if (!intx_file) {
+ res = -errno;
+ goto exit;
+ }
+ if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) {
+ res = -errno;
+ goto exit;
+ }
+ /* Sanity check. */
+ if (intx_file->magic != INTX_SYMBOLIC_LINK) {
+ res = -EINVAL;
+ goto exit;
+ }
+ /* Convert link from unicode to local encoding. */
+ if (ntfs_ucstombs(intx_file->target, (na->data_size -
+ offsetof(INTX_FILE, target)) / sizeof(ntfschar),
+ &buf, 0) < 0) {
+ res = -errno;
+ goto exit;
+ }
+exit:
+ if (intx_file)
+ free(intx_file);
+ if (na)
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_readlink(req, buf);
+ if (buf != ntfs_bad_reparse)
+ free(buf);
+}
+
+static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
+ const ntfschar *name, const int name_len, const int name_type,
+ const s64 pos __attribute__((unused)), const MFT_REF mref,
+ const unsigned dt_type __attribute__((unused)))
+{
+ char *filename = NULL;
+ int ret = 0;
+ int filenamelen = -1;
+ size_t sz;
+ ntfs_fuse_fill_item_t *current;
+ ntfs_fuse_fill_item_t *newone;
+
+ if (name_type == FILE_NAME_DOS)
+ return 0;
+
+ if ((filenamelen = ntfs_ucstombs(name, name_len, &filename, 0)) < 0) {
+ ntfs_log_perror("Filename decoding failed (inode %llu)",
+ (unsigned long long)MREF(mref));
+ return -1;
+ }
+ /* never return inodes 0 and 1 */
+ if (MREF(mref) > 1) {
+ struct stat st = { .st_ino = MREF(mref) };
+
+ if (dt_type == NTFS_DT_REG)
+ st.st_mode = S_IFREG | (0777 & ~ctx->fmask);
+ else if (dt_type == NTFS_DT_DIR)
+ st.st_mode = S_IFDIR | (0777 & ~ctx->dmask);
+
+#if defined(__APPLE__) || defined(__DARWIN__)
+ /*
+ * Returning file names larger than MAXNAMLEN (255) bytes
+ * causes Darwin/Mac OS X to bug out and skip the entry.
+ */
+ if (filenamelen > MAXNAMLEN) {
+ ntfs_log_debug("Truncating %d byte filename to "
+ "%d bytes.\n", filenamelen, MAXNAMLEN);
+ ntfs_log_debug(" before: '%s'\n", filename);
+ memset(filename + MAXNAMLEN, 0, filenamelen - MAXNAMLEN);
+ ntfs_log_debug(" after: '%s'\n", filename);
+ }
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+
+ current = fill_ctx->last;
+ sz = fuse_add_direntry(fill_ctx->req,
+ &current->buf[current->off],
+ current->bufsize - current->off,
+ filename, &st, current->off);
+ if (!sz || ((current->off + sz) > current->bufsize)) {
+ newone = (ntfs_fuse_fill_item_t*)ntfs_malloc
+ (sizeof(ntfs_fuse_fill_item_t)
+ + current->bufsize);
+ if (newone) {
+ newone->off = 0;
+ newone->bufsize = current->bufsize;
+ newone->next = (ntfs_fuse_fill_item_t*)NULL;
+ current->next = newone;
+ fill_ctx->last = newone;
+ current = newone;
+ sz = fuse_add_direntry(fill_ctx->req,
+ current->buf,
+ current->bufsize - current->off,
+ filename, &st, current->off);
+ if (!sz) {
+ errno = EIO;
+ ntfs_log_error("Could not add a"
+ " directory entry (inode %lld)\n",
+ (unsigned long long)MREF(mref));
+ }
+ } else {
+ sz = 0;
+ errno = ENOMEM;
+ }
+ }
+ if (sz) {
+ current->off += sz;
+ } else {
+ ret = -1;
+ }
+ }
+
+ free(filename);
+ return ret;
+}
+
+static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ int res = 0;
+ ntfs_inode *ni;
+ int accesstype;
+ ntfs_fuse_fill_context_t *fill;
+ struct SECURITY_CONTEXT security;
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (ni) {
+ if (ntfs_fuse_fill_security_context(req, &security)) {
+ if (fi->flags & O_WRONLY)
+ accesstype = S_IWRITE;
+ else
+ if (fi->flags & O_RDWR)
+ accesstype = S_IWRITE | S_IREAD;
+ else
+ accesstype = S_IREAD;
+ if (!ntfs_allowed_access(&security,ni,accesstype))
+ res = -EACCES;
+ }
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ if (!res) {
+ fill = (ntfs_fuse_fill_context_t*)
+ ntfs_malloc(sizeof(ntfs_fuse_fill_context_t));
+ if (!fill)
+ res = -errno;
+ else {
+ fill->first = fill->last
+ = (ntfs_fuse_fill_item_t*)NULL;
+ fill->filled = FALSE;
+ fill->ino = ino;
+ }
+ fi->fh = (long)fill;
+ }
+ } else
+ res = -errno;
+ if (!res)
+ fuse_reply_open(req, fi);
+ else
+ fuse_reply_err(req, -res);
+}
+
+
+static void ntfs_fuse_releasedir(fuse_req_t req,
+ fuse_ino_t ino __attribute__((unused)),
+ struct fuse_file_info *fi)
+{
+ ntfs_fuse_fill_context_t *fill;
+ ntfs_fuse_fill_item_t *current;
+
+ fill = (ntfs_fuse_fill_context_t*)(long)fi->fh;
+ /* make sure to clear results */
+ current = fill->first;
+ while (current) {
+ current = current->next;
+ free(fill->first);
+ fill->first = current;
+ }
+ free(fill);
+ fuse_reply_err(req, 0);
+}
+
+static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off __attribute__((unused)),
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ ntfs_fuse_fill_item_t *first;
+ ntfs_fuse_fill_item_t *current;
+ ntfs_fuse_fill_context_t *fill;
+ ntfs_inode *ni;
+ s64 pos = 0;
+ int err = 0;
+
+ fill = (ntfs_fuse_fill_context_t*)(long)fi->fh;
+ if (fill) {
+ if (!fill->filled) {
+ /* initial call : build the full list */
+ first = (ntfs_fuse_fill_item_t*)ntfs_malloc
+ (sizeof(ntfs_fuse_fill_item_t) + size);
+ if (first) {
+ first->bufsize = size;
+ first->off = 0;
+ first->next = (ntfs_fuse_fill_item_t*)NULL;
+ fill->req = req;
+ fill->first = first;
+ fill->last = first;
+ ni = ntfs_inode_open(ctx->vol,INODE(ino));
+ if (!ni)
+ err = -errno;
+ else {
+ if (ntfs_readdir(ni, &pos, fill,
+ (ntfs_filldir_t)
+ ntfs_fuse_filler))
+ err = -errno;
+ fill->filled = TRUE;
+ ntfs_fuse_update_times(ni,
+ NTFS_UPDATE_ATIME);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&err);
+ }
+ if (!err)
+ fuse_reply_buf(req, first->buf,
+ first->off);
+ /* reply sent, now must exit with no error */
+ fill->first = first->next;
+ free(first);
+ } else
+ err = -errno;
+ } else {
+ /* subsequent call : return next non-empty buffer */
+ current = fill->first;
+ while (current && !current->off) {
+ current = current->next;
+ free(fill->first);
+ fill->first = current;
+ }
+ if (current) {
+ fuse_reply_buf(req, current->buf, current->off);
+ fill->first = current->next;
+ free(current);
+ } else {
+ fuse_reply_buf(req, (char*)NULL, 0);
+ /* reply sent, now must exit with no error */
+ }
+ }
+ } else {
+ errno = EIO;
+ err = -errno;
+ ntfs_log_error("Uninitialized fuse_readdir()\n");
+ }
+ if (err)
+ fuse_reply_err(req, -err);
+}
+
+static void ntfs_fuse_open(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ struct open_file *of;
+ int state = 0;
+ char *path = NULL;
+ int res = 0;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ int accesstype;
+ struct SECURITY_CONTEXT security;
+#endif
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (ni) {
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (na) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ if (ntfs_fuse_fill_security_context(req, &security)) {
+ if (fi->flags & O_WRONLY)
+ accesstype = S_IWRITE;
+ else
+ if (fi->flags & O_RDWR)
+ accesstype = S_IWRITE | S_IREAD;
+ else
+ accesstype = S_IREAD;
+ /* check whether requested access is allowed */
+ if (!ntfs_allowed_access(&security,
+ ni,accesstype))
+ res = -EACCES;
+ }
+#endif
+ if ((res >= 0)
+ && (fi->flags & (O_WRONLY | O_RDWR))) {
+ /* mark a future need to compress the last chunk */
+ if (na->data_flags & ATTR_COMPRESSION_MASK)
+ state |= CLOSE_COMPRESSED;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* mark a future need to fixup encrypted inode */
+ if (ctx->efs_raw
+ && !(na->data_flags & ATTR_IS_ENCRYPTED)
+ && (ni->flags & FILE_ATTR_ENCRYPTED))
+ state |= CLOSE_ENCRYPTED;
+#endif /* HAVE_SETXATTR */
+ /* mark a future need to update the mtime */
+ if (ctx->dmtime)
+ state |= CLOSE_DMTIME;
+ /* deny opening metadata files for writing */
+ if (ino < FILE_first_user)
+ res = -EPERM;
+ }
+ ntfs_attr_close(na);
+ } else
+ res = -errno;
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ free(path);
+ if (res >= 0) {
+ of = (struct open_file*)malloc(sizeof(struct open_file));
+ if (of) {
+ of->parent = 0;
+ of->ino = ino;
+ of->state = state;
+ of->next = ctx->open_files;
+ of->previous = (struct open_file*)NULL;
+ if (ctx->open_files)
+ ctx->open_files->previous = of;
+ ctx->open_files = of;
+ fi->fh = (long)of;
+ }
+ }
+ if (res)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_open(req, fi);
+}
+
+static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t offset,
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ int res;
+ char *buf = (char*)NULL;
+ s64 total = 0;
+ s64 max_read;
+
+ if (!size) {
+ res = 0;
+ goto exit;
+ }
+ buf = (char*)ntfs_malloc(size);
+ if (!buf) {
+ res = -errno;
+ goto exit;
+ }
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ max_read = na->data_size;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* limit reads at next 512 byte boundary for encrypted attributes */
+ if (ctx->efs_raw
+ && max_read
+ && (na->data_flags & ATTR_IS_ENCRYPTED)
+ && NAttrNonResident(na)) {
+ max_read = ((na->data_size+511) & ~511) + 2;
+ }
+#endif /* HAVE_SETXATTR */
+ if (offset + (off_t)size > max_read) {
+ if (max_read < offset)
+ goto ok;
+ size = max_read - offset;
+ }
+ while (size > 0) {
+ s64 ret = ntfs_attr_pread(na, offset, size, buf + total);
+ if (ret != (s64)size)
+ ntfs_log_perror("ntfs_attr_pread error reading inode %lld at "
+ "offset %lld: %lld <> %lld", (long long)ni->mft_no,
+ (long long)offset, (long long)size, (long long)ret);
+ if (ret <= 0 || ret > (s64)size) {
+ res = (ret < 0) ? -errno : -EIO;
+ goto exit;
+ }
+ size -= ret;
+ offset += ret;
+ total += ret;
+ }
+ok:
+ ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME);
+ res = total;
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_buf(req, buf, res);
+ free(buf);
+}
+
+static void ntfs_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, off_t offset,
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ int res, total = 0;
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ while (size) {
+ s64 ret = ntfs_attr_pwrite(na, offset, size, buf + total);
+ if (ret <= 0) {
+ res = -errno;
+ goto exit;
+ }
+ size -= ret;
+ offset += ret;
+ total += ret;
+ }
+ res = total;
+ if ((res > 0) && !ctx->dmtime)
+ ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ if (total)
+ set_archive(ni);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_write(req, res);
+}
+
+static int ntfs_fuse_chmod(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
+ mode_t mode, struct stat *stbuf)
+{
+ int res = 0;
+ ntfs_inode *ni;
+
+ /* return unsupported if no user mapping has been defined */
+ if (!scx->mapping[MAPUSERS] && !ctx->silent) {
+ res = -EOPNOTSUPP;
+ } else {
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ res = -errno;
+ else {
+ if (scx->mapping[MAPUSERS]) {
+ if (ntfs_set_mode(scx, ni, mode))
+ res = -errno;
+ else {
+ ntfs_fuse_update_times(ni,
+ NTFS_UPDATE_CTIME);
+ /*
+ * Must return updated times, and
+ * inode has been updated, so hope
+ * we get no further errors
+ */
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ }
+ NInoSetDirty(ni);
+ } else
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+ }
+ return res;
+}
+
+static int ntfs_fuse_chown(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
+ uid_t uid, gid_t gid, struct stat *stbuf)
+{
+ ntfs_inode *ni;
+ int res;
+
+ if (!scx->mapping[MAPUSERS]
+ && !ctx->silent
+ && ((uid != ctx->uid) || (gid != ctx->gid)))
+ res = -EOPNOTSUPP;
+ else {
+ res = 0;
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ res = -errno;
+ else {
+ if (scx->mapping[MAPUSERS]
+ && (((int)uid != -1) || ((int)gid != -1))) {
+ if (ntfs_set_owner(scx, ni, uid, gid))
+ res = -errno;
+ else {
+ ntfs_fuse_update_times(ni,
+ NTFS_UPDATE_CTIME);
+ /*
+ * Must return updated times, and
+ * inode has been updated, so hope
+ * we get no further errors
+ */
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ }
+ } else
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+ }
+ return (res);
+}
+
+static int ntfs_fuse_chownmod(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
+ uid_t uid, gid_t gid, mode_t mode, struct stat *stbuf)
+{
+ ntfs_inode *ni;
+ int res;
+
+ if (!scx->mapping[MAPUSERS]
+ && !ctx->silent
+ && ((uid != ctx->uid) || (gid != ctx->gid)))
+ res = -EOPNOTSUPP;
+ else {
+ res = 0;
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ res = -errno;
+ else {
+ if (scx->mapping[MAPUSERS]) {
+ if (ntfs_set_ownmod(scx, ni, uid, gid, mode))
+ res = -errno;
+ else {
+ ntfs_fuse_update_times(ni,
+ NTFS_UPDATE_CTIME);
+ /*
+ * Must return updated times, and
+ * inode has been updated, so hope
+ * we get no further errors
+ */
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ }
+ } else
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+ }
+ return (res);
+}
+
+static int ntfs_fuse_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ off_t size, BOOL chkwrite, struct stat *stbuf)
+#else
+ off_t size, BOOL chkwrite __attribute__((unused)),
+ struct stat *stbuf)
+#endif
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ int res;
+ s64 oldsize;
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ goto exit;
+
+ /* deny truncating metadata files */
+ if (ino < FILE_first_user) {
+ errno = EPERM;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na)
+ goto exit;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * deny truncation if cannot write to file
+ * (already checked for ftruncate())
+ */
+ if (scx->mapping[MAPUSERS]
+ && chkwrite
+ && !ntfs_allowed_access(scx, ni, S_IWRITE)) {
+ errno = EACCES;
+ goto exit;
+ }
+#endif
+ /*
+ * for compressed files, upsizing is done by inserting a final
+ * zero, which is optimized as creating a hole when possible.
+ */
+ oldsize = na->data_size;
+ if ((na->data_flags & ATTR_COMPRESSION_MASK)
+ && (size > na->initialized_size)) {
+ char zero = 0;
+ if (ntfs_attr_pwrite(na, size - 1, 1, &zero) <= 0)
+ goto exit;
+ } else
+ if (ntfs_attr_truncate(na, size))
+ goto exit;
+ if (oldsize != size)
+ set_archive(ni);
+
+ ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ errno = (res ? -res : 0);
+exit:
+ res = -errno;
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+#if defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW)
+
+static int ntfs_fuse_utimens(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
+ struct stat *stin, struct stat *stbuf, int to_set)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ return -errno;
+
+ /* no check or update if both UTIME_OMIT */
+ if (to_set & (FUSE_SET_ATTR_ATIME + FUSE_SET_ATTR_MTIME)) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ if (ntfs_allowed_as_owner(scx, ni)
+ || ((to_set & FUSE_SET_ATTR_ATIME_NOW)
+ && (to_set & FUSE_SET_ATTR_MTIME_NOW)
+ && ntfs_allowed_access(scx, ni, S_IWRITE))) {
+#endif
+ ntfs_time_update_flags mask = NTFS_UPDATE_CTIME;
+
+ if (to_set & FUSE_SET_ATTR_ATIME_NOW)
+ mask |= NTFS_UPDATE_ATIME;
+ else
+ if (to_set & FUSE_SET_ATTR_ATIME)
+ ni->last_access_time
+ = timespec2ntfs(stin->st_atim);
+ if (to_set & FUSE_SET_ATTR_MTIME_NOW)
+ mask |= NTFS_UPDATE_MTIME;
+ else
+ if (to_set & FUSE_SET_ATTR_MTIME)
+ ni->last_data_change_time
+ = timespec2ntfs(stin->st_mtim);
+ ntfs_inode_update_times(ni, mask);
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -errno;
+#endif
+ }
+ if (!res)
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+#else /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */
+
+static int ntfs_fuse_utime(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
+ struct stat *stin, struct stat *stbuf)
+{
+ ntfs_inode *ni;
+ int res = 0;
+ struct timespec actime;
+ struct timespec modtime;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ BOOL ownerok;
+ BOOL writeok;
+#endif
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ return -errno;
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ownerok = ntfs_allowed_as_owner(scx, ni);
+ if (stin) {
+ /*
+ * fuse never calls with a NULL buf and we do not
+ * know whether the specific condition can be applied
+ * So we have to accept updating by a non-owner having
+ * write access.
+ */
+ writeok = !ownerok
+ && (stin->st_atime == stin->st_mtime)
+ && ntfs_allowed_access(scx, ni, S_IWRITE);
+ /* Must be owner */
+ if (!ownerok && !writeok)
+ res = (stin->st_atime == stin->st_mtime
+ ? -EACCES : -EPERM);
+ else {
+ actime.tv_sec = stin->st_atime;
+ actime.tv_nsec = 0;
+ modtime.tv_sec = stin->st_mtime;
+ modtime.tv_nsec = 0;
+ ni->last_access_time = timespec2ntfs(actime);
+ ni->last_data_change_time = timespec2ntfs(modtime);
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ }
+ } else {
+ /* Must be owner or have write access */
+ writeok = !ownerok
+ && ntfs_allowed_access(scx, ni, S_IWRITE);
+ if (!ownerok && !writeok)
+ res = -EACCES;
+ else
+ ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);
+ }
+#else
+ if (stin) {
+ actime.tv_sec = stin->st_atime;
+ actime.tv_nsec = 0;
+ modtime.tv_sec = stin->st_mtime;
+ modtime.tv_nsec = 0;
+ ni->last_access_time = timespec2ntfs(actime);
+ ni->last_data_change_time = timespec2ntfs(modtime);
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ } else
+ ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);
+#endif
+
+ res = ntfs_fuse_getstat(scx, ni, stbuf);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+#endif /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */
+
+static void ntfs_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int to_set, struct fuse_file_info *fi __attribute__((unused)))
+{
+ struct stat stbuf;
+ ntfs_inode *ni;
+ int res;
+ struct SECURITY_CONTEXT security;
+
+ res = 0;
+ ntfs_fuse_fill_security_context(req, &security);
+ /* no flags */
+ if (!(to_set
+ & (FUSE_SET_ATTR_MODE
+ | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID
+ | FUSE_SET_ATTR_SIZE
+ | FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni)
+ res = -errno;
+ else {
+ res = ntfs_fuse_getstat(&security, ni, &stbuf);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+ }
+ /* some set of uid/gid/mode */
+ if (to_set
+ & (FUSE_SET_ATTR_MODE
+ | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
+ switch (to_set
+ & (FUSE_SET_ATTR_MODE
+ | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
+ case FUSE_SET_ATTR_MODE :
+ res = ntfs_fuse_chmod(&security, ino,
+ attr->st_mode & 07777, &stbuf);
+ break;
+ case FUSE_SET_ATTR_UID :
+ res = ntfs_fuse_chown(&security, ino, attr->st_uid,
+ (gid_t)-1, &stbuf);
+ break;
+ case FUSE_SET_ATTR_GID :
+ res = ntfs_fuse_chown(&security, ino, (uid_t)-1,
+ attr->st_gid, &stbuf);
+ break;
+ case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_GID :
+ res = ntfs_fuse_chown(&security, ino, attr->st_uid,
+ attr->st_gid, &stbuf);
+ break;
+ case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_MODE:
+ res = ntfs_fuse_chownmod(&security, ino, attr->st_uid,
+ (gid_t)-1,attr->st_mode,
+ &stbuf);
+ break;
+ case FUSE_SET_ATTR_GID + FUSE_SET_ATTR_MODE:
+ res = ntfs_fuse_chownmod(&security, ino, (uid_t)-1,
+ attr->st_gid,attr->st_mode,
+ &stbuf);
+ break;
+ case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_GID + FUSE_SET_ATTR_MODE:
+ res = ntfs_fuse_chownmod(&security, ino, attr->st_uid,
+ attr->st_gid,attr->st_mode, &stbuf);
+ break;
+ default :
+ break;
+ }
+ }
+ /* size */
+ if (!res && (to_set & FUSE_SET_ATTR_SIZE)) {
+ res = ntfs_fuse_trunc(&security, ino, attr->st_size,
+ !fi, &stbuf);
+ }
+ /* some set of atime/mtime */
+ if (!res && (to_set & (FUSE_SET_ATTR_ATIME + FUSE_SET_ATTR_MTIME))) {
+#if defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW)
+ res = ntfs_fuse_utimens(&security, ino, attr, &stbuf, to_set);
+#else /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */
+ res = ntfs_fuse_utime(&security, ino, attr, &stbuf);
+#endif /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */
+ }
+ if (res)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_attr(req, &stbuf, ATTR_TIMEOUT);
+}
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+
+static void ntfs_fuse_access(fuse_req_t req, fuse_ino_t ino, int mask)
+{
+ int res = 0;
+ int mode;
+ ntfs_inode *ni;
+ struct SECURITY_CONTEXT security;
+
+ /* JPA return unsupported if no user mapping has been defined */
+ if (!ntfs_fuse_fill_security_context(req, &security)) {
+ if (ctx->silent)
+ res = 0;
+ else
+ res = -EOPNOTSUPP;
+ } else {
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ } else {
+ mode = 0;
+ if (mask & (X_OK | W_OK | R_OK)) {
+ if (mask & X_OK) mode += S_IEXEC;
+ if (mask & W_OK) mode += S_IWRITE;
+ if (mask & R_OK) mode += S_IREAD;
+ if (!ntfs_allowed_access(&security,
+ ni, mode))
+ res = -errno;
+ }
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+ }
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+}
+
+#endif /* !KERNELPERMS | (POSIXACLS & !KERNELACLS) */
+
+static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t typemode, dev_t dev,
+ struct fuse_entry_param *e,
+ const char *target, struct fuse_file_info *fi)
+{
+ ntfschar *uname = NULL, *utarget = NULL;
+ ntfs_inode *dir_ni = NULL, *ni;
+ struct open_file *of;
+ int state = 0;
+ le32 securid;
+ mode_t type = typemode & ~07777;
+ mode_t perm;
+ struct SECURITY_CONTEXT security;
+ int res = 0, uname_len, utarget_len;
+
+ /* Generate unicode filename. */
+ uname_len = ntfs_mbstoucs(name, &uname);
+ if ((uname_len < 0)
+ || (ctx->windows_names
+ && ntfs_forbidden_chars(uname,uname_len))) {
+ res = -errno;
+ goto exit;
+ }
+ /* Open parent directory. */
+ dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
+ if (!dir_ni) {
+ res = -errno;
+ goto exit;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* make sure parent directory is writeable and executable */
+ if (!ntfs_fuse_fill_security_context(req, &security)
+ || ntfs_allowed_access(&security,
+ dir_ni,S_IWRITE + S_IEXEC)) {
+#else
+ ntfs_fuse_fill_security_context(req, &security);
+#endif
+ if (S_ISDIR(type))
+ perm = typemode & ~ctx->dmask & 0777;
+ else
+ perm = typemode & ~ctx->fmask & 0777;
+ /*
+ * Try to get a security id available for
+ * file creation (from inheritance or argument).
+ * This is not possible for NTFS 1.x, and we will
+ * have to build a security attribute later.
+ */
+ if (!ctx->security.mapping[MAPUSERS])
+ securid = const_cpu_to_le32(0);
+ else
+ if (ctx->inherit)
+ securid = ntfs_inherited_id(&security,
+ dir_ni, S_ISDIR(type));
+ else
+#if POSIXACLS
+ securid = ntfs_alloc_securid(&security,
+ security.uid, security.gid,
+ dir_ni, perm, S_ISDIR(type));
+#else
+ securid = ntfs_alloc_securid(&security,
+ security.uid, security.gid,
+ perm & ~security.umask, S_ISDIR(type));
+#endif
+ /* Create object specified in @type. */
+ switch (type) {
+ case S_IFCHR:
+ case S_IFBLK:
+ ni = ntfs_create_device(dir_ni, securid,
+ uname, uname_len, type, dev);
+ break;
+ case S_IFLNK:
+ utarget_len = ntfs_mbstoucs(target, &utarget);
+ if (utarget_len < 0) {
+ res = -errno;
+ goto exit;
+ }
+ ni = ntfs_create_symlink(dir_ni, securid,
+ uname, uname_len,
+ utarget, utarget_len);
+ break;
+ default:
+ ni = ntfs_create(dir_ni, securid, uname,
+ uname_len, type);
+ break;
+ }
+ if (ni) {
+ /*
+ * set the security attribute if a security id
+ * could not be allocated (eg NTFS 1.x)
+ */
+ if (ctx->security.mapping[MAPUSERS]) {
+#if POSIXACLS
+ if (!securid
+ && ntfs_set_inherited_posix(&security, ni,
+ security.uid, security.gid,
+ dir_ni, perm) < 0)
+ set_fuse_error(&res);
+#else
+ if (!securid
+ && ntfs_set_owner_mode(&security, ni,
+ security.uid, security.gid,
+ perm & ~security.umask) < 0)
+ set_fuse_error(&res);
+#endif
+ }
+ set_archive(ni);
+ /* mark a need to compress the end of file */
+ if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) {
+ state |= CLOSE_COMPRESSED;
+ }
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* mark a future need to fixup encrypted inode */
+ if (fi
+ && ctx->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED))
+ state |= CLOSE_ENCRYPTED;
+#endif /* HAVE_SETXATTR */
+ if (fi && ctx->dmtime)
+ state |= CLOSE_DMTIME;
+ ntfs_inode_update_mbsname(dir_ni, name, ni->mft_no);
+ NInoSetDirty(ni);
+ e->ino = ni->mft_no;
+ e->generation = 1;
+ e->attr_timeout = ATTR_TIMEOUT;
+ e->entry_timeout = ENTRY_TIMEOUT;
+ res = ntfs_fuse_getstat(&security, ni, &e->attr);
+ /*
+ * closing ni requires access to dir_ni to
+ * synchronize the index, avoid double opening.
+ */
+ if (ntfs_inode_close_in_dir(ni, dir_ni))
+ set_fuse_error(&res);
+ ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME);
+ } else
+ res = -errno;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -errno;
+#endif
+
+exit:
+ free(uname);
+ if (ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ if (utarget)
+ free(utarget);
+ if ((res >= 0) && fi) {
+ of = (struct open_file*)malloc(sizeof(struct open_file));
+ if (of) {
+ of->parent = 0;
+ of->ino = e->ino;
+ of->state = state;
+ of->next = ctx->open_files;
+ of->previous = (struct open_file*)NULL;
+ if (ctx->open_files)
+ ctx->open_files->previous = of;
+ ctx->open_files = of;
+ fi->fh = (long)of;
+ }
+ }
+ return res;
+}
+
+static void ntfs_fuse_create_file(fuse_req_t req, fuse_ino_t parent,
+ const char *name, mode_t mode,
+ struct fuse_file_info *fi)
+{
+ int res;
+ struct fuse_entry_param entry;
+
+ res = ntfs_fuse_create(req, parent, name, mode & (S_IFMT | 07777),
+ 0, &entry, NULL, fi);
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_create(req, &entry, fi);
+}
+
+static void ntfs_fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev)
+{
+ int res;
+ struct fuse_entry_param e;
+
+ res = ntfs_fuse_create(req, parent, name, mode & (S_IFMT | 07777),
+ rdev, &e,NULL,(struct fuse_file_info*)NULL);
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_entry(req, &e);
+}
+
+static void ntfs_fuse_symlink(fuse_req_t req, const char *target,
+ fuse_ino_t parent, const char *name)
+{
+ int res;
+ struct fuse_entry_param entry;
+
+ res = ntfs_fuse_create(req, parent, name, S_IFLNK, 0,
+ &entry, target, (struct fuse_file_info*)NULL);
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_entry(req, &entry);
+}
+
+
+static int ntfs_fuse_newlink(fuse_req_t req __attribute__((unused)),
+ fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname, struct fuse_entry_param *e)
+{
+ ntfschar *uname = NULL;
+ ntfs_inode *dir_ni = NULL, *ni;
+ int res = 0, uname_len;
+ struct SECURITY_CONTEXT security;
+
+ /* Open file for which create hard link. */
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+
+ /* Generate unicode filename. */
+ uname_len = ntfs_mbstoucs(newname, &uname);
+ if ((uname_len < 0)
+ || (ctx->windows_names
+ && ntfs_forbidden_chars(uname,uname_len))) {
+ res = -errno;
+ goto exit;
+ }
+ /* Open parent directory. */
+ dir_ni = ntfs_inode_open(ctx->vol, INODE(newparent));
+ if (!dir_ni) {
+ res = -errno;
+ goto exit;
+ }
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* make sure the target parent directory is writeable */
+ if (ntfs_fuse_fill_security_context(req, &security)
+ && !ntfs_allowed_access(&security,dir_ni,S_IWRITE + S_IEXEC))
+ res = -EACCES;
+ else
+#else
+ ntfs_fuse_fill_security_context(req, &security);
+#endif
+ {
+ if (ntfs_link(ni, dir_ni, uname, uname_len)) {
+ res = -errno;
+ goto exit;
+ }
+ ntfs_inode_update_mbsname(dir_ni, newname, ni->mft_no);
+ if (e) {
+ e->ino = ni->mft_no;
+ e->generation = 1;
+ e->attr_timeout = ATTR_TIMEOUT;
+ e->entry_timeout = ENTRY_TIMEOUT;
+ res = ntfs_fuse_getstat(&security, ni, &e->attr);
+ }
+ set_archive(ni);
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME);
+ }
+exit:
+ /*
+ * Must close dir_ni first otherwise ntfs_inode_sync_file_name(ni)
+ * may fail because ni may not be in parent's index on the disk yet.
+ */
+ if (ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(uname);
+ return (res);
+}
+
+static void ntfs_fuse_link(fuse_req_t req, fuse_ino_t ino,
+ fuse_ino_t newparent, const char *newname)
+{
+ struct fuse_entry_param entry;
+ int res;
+
+ res = ntfs_fuse_newlink(req, ino, newparent, newname, &entry);
+ if (res)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_entry(req, &entry);
+}
+
+static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ ntfschar *uname = NULL;
+ ntfs_inode *dir_ni = NULL, *ni = NULL;
+ int res = 0, uname_len;
+ u64 iref;
+ fuse_ino_t ino;
+ struct open_file *of;
+ char ghostname[GHOSTLTH];
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+ /* Open parent directory. */
+ dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
+ if (!dir_ni) {
+ res = -errno;
+ goto exit;
+ }
+ /* Generate unicode filename. */
+ uname_len = ntfs_mbstoucs(name, &uname);
+ if (uname_len < 0) {
+ res = -errno;
+ goto exit;
+ }
+ /* Open object for delete. */
+ iref = ntfs_inode_lookup_by_mbsname(dir_ni, name);
+ if (iref == (u64)-1) {
+ res = -errno;
+ goto exit;
+ }
+ /* deny unlinking metadata files */
+ if (MREF(iref) < FILE_first_user) {
+ res = -EPERM;
+ goto exit;
+ }
+
+ of = ctx->open_files;
+ ino = (fuse_ino_t)MREF(iref);
+ /* improvable search in open files list... */
+ while (of
+ && (of->ino != ino))
+ of = of->next;
+ if (of && !(of->state & CLOSE_GHOST)) {
+ /* file was open, create a ghost in unlink parent */
+ of->state |= CLOSE_GHOST;
+ of->parent = parent;
+ of->ghost = ++ctx->latest_ghost;
+ sprintf(ghostname,ghostformat,of->ghost);
+ /* need to close the dir for linking the ghost */
+ if (ntfs_inode_close(dir_ni)) {
+ res = -errno;
+ goto out;
+ }
+ /* sweep existing ghost if any */
+ ntfs_fuse_rm(req, parent, ghostname);
+ res = ntfs_fuse_newlink(req, of->ino, parent, ghostname,
+ (struct fuse_entry_param*)NULL);
+ if (res)
+ goto out;
+ /* now reopen then parent directory */
+ dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
+ if (!dir_ni) {
+ res = -errno;
+ goto exit;
+ }
+ }
+
+ ni = ntfs_inode_open(ctx->vol, MREF(iref));
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* JPA deny unlinking if directory is not writable and executable */
+ if (!ntfs_fuse_fill_security_context(req, &security)
+ || ntfs_allowed_dir_access(&security, dir_ni, ino, ni,
+ S_IEXEC + S_IWRITE + S_ISVTX)) {
+#endif
+ if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni,
+ uname, uname_len))
+ res = -errno;
+ /* ntfs_delete() always closes ni and dir_ni */
+ ni = dir_ni = NULL;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -EACCES;
+#endif
+exit:
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ if (ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+out :
+ free(uname);
+ return res;
+}
+
+static void ntfs_fuse_unlink(fuse_req_t req, fuse_ino_t parent,
+ const char *name)
+{
+ int res;
+
+ res = ntfs_fuse_rm(req, parent, name);
+ if (res)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static int ntfs_fuse_safe_rename(fuse_req_t req, fuse_ino_t ino,
+ fuse_ino_t parent, const char *name, fuse_ino_t xino,
+ fuse_ino_t newparent, const char *newname,
+ const char *tmp)
+{
+ int ret;
+
+ ntfs_log_trace("Entering\n");
+
+ ret = ntfs_fuse_newlink(req, xino, newparent, tmp,
+ (struct fuse_entry_param*)NULL);
+ if (ret)
+ return ret;
+
+ ret = ntfs_fuse_rm(req, newparent, newname);
+ if (!ret) {
+
+ ret = ntfs_fuse_newlink(req, ino, newparent, newname,
+ (struct fuse_entry_param*)NULL);
+ if (ret)
+ goto restore;
+
+ ret = ntfs_fuse_rm(req, parent, name);
+ if (ret) {
+ if (ntfs_fuse_rm(req, newparent, newname))
+ goto err;
+ goto restore;
+ }
+ }
+
+ goto cleanup;
+restore:
+ if (ntfs_fuse_newlink(req, xino, newparent, newname,
+ (struct fuse_entry_param*)NULL)) {
+err:
+ ntfs_log_perror("Rename failed. Existing file '%s' was renamed "
+ "to '%s'", newname, tmp);
+ } else {
+cleanup:
+ /*
+ * Condition for this unlink has already been checked in
+ * "ntfs_fuse_rename_existing_dest()", so it should never
+ * fail (unless concurrent access to directories when fuse
+ * is multithreaded)
+ */
+ if (ntfs_fuse_rm(req, newparent, tmp) < 0)
+ ntfs_log_perror("Rename failed. Existing file '%s' still present "
+ "as '%s'", newname, tmp);
+ }
+ return ret;
+}
+
+static int ntfs_fuse_rename_existing_dest(fuse_req_t req, fuse_ino_t ino,
+ fuse_ino_t parent, const char *name,
+ fuse_ino_t xino, fuse_ino_t newparent,
+ const char *newname)
+{
+ int ret, len;
+ char *tmp;
+ const char *ext = ".ntfs-3g-";
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ntfs_inode *newdir_ni;
+ struct SECURITY_CONTEXT security;
+#endif
+
+ ntfs_log_trace("Entering\n");
+
+ len = strlen(newname) + strlen(ext) + 10 + 1; /* wc(str(2^32)) + \0 */
+ tmp = (char*)ntfs_malloc(len);
+ if (!tmp)
+ return -errno;
+
+ ret = snprintf(tmp, len, "%s%s%010d", newname, ext, ++ntfs_sequence);
+ if (ret != len - 1) {
+ ntfs_log_error("snprintf failed: %d != %d\n", ret, len - 1);
+ ret = -EOVERFLOW;
+ } else {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * Make sure existing dest can be removed.
+ * This is only needed if parent directory is
+ * sticky, because in this situation condition
+ * for unlinking is different from condition for
+ * linking
+ */
+ newdir_ni = ntfs_inode_open(ctx->vol, INODE(newparent));
+ if (newdir_ni) {
+ if (!ntfs_fuse_fill_security_context(req,&security)
+ || ntfs_allowed_dir_access(&security, newdir_ni,
+ xino, (ntfs_inode*)NULL,
+ S_IEXEC + S_IWRITE + S_ISVTX)) {
+ if (ntfs_inode_close(newdir_ni))
+ ret = -errno;
+ else
+ ret = ntfs_fuse_safe_rename(req, ino,
+ parent, name, xino,
+ newparent, newname,
+ tmp);
+ } else {
+ ntfs_inode_close(newdir_ni);
+ ret = -EACCES;
+ }
+ } else
+ ret = -errno;
+#else
+ ret = ntfs_fuse_safe_rename(req, ino, parent, name,
+ xino, newparent, newname, tmp);
+#endif
+ }
+ free(tmp);
+ return ret;
+}
+
+static void ntfs_fuse_rename(fuse_req_t req, fuse_ino_t parent,
+ const char *name, fuse_ino_t newparent,
+ const char *newname)
+{
+ int ret;
+ fuse_ino_t ino;
+ fuse_ino_t xino;
+ ntfs_inode *ni;
+
+ ntfs_log_debug("rename: old: '%s' new: '%s'\n", name, newname);
+
+ /*
+ * FIXME: Rename should be atomic.
+ */
+
+ ino = ntfs_fuse_inode_lookup(parent, name);
+ if (ino == (fuse_ino_t)-1) {
+ ret = -errno;
+ goto out;
+ }
+ /* Check whether target is present */
+ xino = ntfs_fuse_inode_lookup(newparent, newname);
+ if (xino != (fuse_ino_t)-1) {
+ /*
+ * Target exists : no need to check whether it
+ * designates the same inode, this has already
+ * been checked (by fuse ?)
+ */
+ ni = ntfs_inode_open(ctx->vol, INODE(xino));
+ if (!ni)
+ ret = -errno;
+ else {
+ ret = ntfs_check_empty_dir(ni);
+ if (ret < 0) {
+ ret = -errno;
+ ntfs_inode_close(ni);
+ goto out;
+ }
+
+ if (ntfs_inode_close(ni)) {
+ set_fuse_error(&ret);
+ goto out;
+ }
+ ret = ntfs_fuse_rename_existing_dest(req, ino, parent,
+ name, xino, newparent, newname);
+ }
+ } else {
+ /* target does not exist */
+ ret = ntfs_fuse_newlink(req, ino, newparent, newname,
+ (struct fuse_entry_param*)NULL);
+ if (ret)
+ goto out;
+
+ ret = ntfs_fuse_rm(req, parent, name);
+ if (ret)
+ ntfs_fuse_rm(req, newparent, newname);
+ }
+out:
+ if (ret)
+ fuse_reply_err(req, -ret);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static void ntfs_fuse_release(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ struct open_file *of;
+ char ghostname[GHOSTLTH];
+ int res;
+
+ of = (struct open_file*)(long)fi->fh;
+ /* Only for marked descriptors there is something to do */
+ if (!of
+ || !(of->state & (CLOSE_COMPRESSED
+ | CLOSE_ENCRYPTED | CLOSE_DMTIME))) {
+ res = 0;
+ goto out;
+ }
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ res = 0;
+ if (of->state & CLOSE_DMTIME)
+ ntfs_inode_update_times(ni,NTFS_UPDATE_MCTIME);
+ if (of->state & CLOSE_COMPRESSED)
+ res = ntfs_attr_pclose(na);
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ if (of->state & CLOSE_ENCRYPTED)
+ res = ntfs_efs_fixup_attribute(NULL, na);
+#endif /* HAVE_SETXATTR */
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+out:
+ /* remove the associate ghost file (even if release failed) */
+ if (of) {
+ if (of->state & CLOSE_GHOST) {
+ sprintf(ghostname,ghostformat,of->ghost);
+ ntfs_fuse_rm(req, of->parent, ghostname);
+ }
+ /* remove from open files list */
+ if (of->next)
+ of->next->previous = of->previous;
+ if (of->previous)
+ of->previous->next = of->next;
+ else
+ ctx->open_files = of->next;
+ free(of);
+ }
+ if (res)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static void ntfs_fuse_mkdir(fuse_req_t req, fuse_ino_t parent,
+ const char *name, mode_t mode)
+{
+ int res;
+ struct fuse_entry_param entry;
+
+ res = ntfs_fuse_create(req, parent, name, S_IFDIR | (mode & 07777),
+ 0, &entry, (char*)NULL, (struct fuse_file_info*)NULL);
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_entry(req, &entry);
+}
+
+static void ntfs_fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ int res;
+
+ res = ntfs_fuse_rm(req, parent, name);
+ if (res)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static void ntfs_fuse_fsync(fuse_req_t req,
+ fuse_ino_t ino __attribute__((unused)),
+ int type __attribute__((unused)),
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ /* sync the full device */
+ if (ntfs_device_sync(ctx->vol->dev))
+ fuse_reply_err(req, errno);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static void ntfs_fuse_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
+ uint64_t vidx)
+{
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ LCN lcn;
+ uint64_t lidx = 0;
+ int ret = 0;
+ int cl_per_bl = ctx->vol->cluster_size / blocksize;
+
+ if (blocksize > ctx->vol->cluster_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ ret = -errno;
+ goto done;
+ }
+
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ ret = -errno;
+ goto close_inode;
+ }
+
+ if ((na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED))
+ || !NAttrNonResident(na)) {
+ ret = -EINVAL;
+ goto close_attr;
+ }
+
+ if (ntfs_attr_map_whole_runlist(na)) {
+ ret = -errno;
+ goto close_attr;
+ }
+
+ lcn = ntfs_rl_vcn_to_lcn(na->rl, vidx / cl_per_bl);
+ lidx = (lcn > 0) ? lcn * cl_per_bl + vidx % cl_per_bl : 0;
+
+close_attr:
+ ntfs_attr_close(na);
+close_inode:
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&ret);
+done :
+ if (ret < 0)
+ fuse_reply_err(req, -ret);
+ else
+ fuse_reply_bmap(req, lidx);
+}
+
+#ifdef HAVE_SETXATTR
+
+/*
+ * Name space identifications and prefixes
+ */
+
+enum {
+ XATTRNS_NONE,
+ XATTRNS_USER,
+ XATTRNS_SYSTEM,
+ XATTRNS_SECURITY,
+ XATTRNS_TRUSTED,
+ XATTRNS_OPEN
+} ;
+
+/*
+ * Check whether access to internal data as an extended
+ * attribute in system name space is allowed
+ *
+ * Returns pointer to inode if allowed,
+ * NULL and errno set if not allowed
+ */
+
+static ntfs_inode *ntfs_check_access_xattr(fuse_req_t req,
+ struct SECURITY_CONTEXT *security,
+ fuse_ino_t ino, int attr, BOOL setting)
+{
+ ntfs_inode *dir_ni;
+ ntfs_inode *ni;
+ BOOL foracl;
+ BOOL bad;
+ mode_t acctype;
+
+ ni = (ntfs_inode*)NULL;
+ foracl = (attr == XATTR_POSIX_ACC)
+ || (attr == XATTR_POSIX_DEF);
+ /*
+ * When accessing Posix ACL, return unsupported if ACL
+ * were disabled or no user mapping has been defined.
+ * However no error will be returned to getfacl
+ */
+ if ((!ntfs_fuse_fill_security_context(req, security)
+ || (ctx->secure_flags
+ & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_RAW))))
+ && foracl) {
+ errno = EOPNOTSUPP;
+ } else {
+ /*
+ * parent directory must be executable, and
+ * for setting a DOS name it must be writeable
+ */
+ if (setting && (attr == XATTR_NTFS_DOS_NAME))
+ acctype = S_IEXEC | S_IWRITE;
+ else
+ acctype = S_IEXEC;
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ /* basic access was checked previously in a lookup */
+ if (ni && (acctype != S_IEXEC)) {
+ bad = FALSE;
+ /* do not reopen root */
+ if (ni->mft_no == FILE_root) {
+ /* forbid getting/setting names on root */
+ if ((attr == XATTR_NTFS_DOS_NAME)
+ || !ntfs_real_allowed_access(security,
+ ni, acctype))
+ bad = TRUE;
+ } else {
+ dir_ni = ntfs_dir_parent_inode(ni);
+ if (dir_ni) {
+ if (!ntfs_real_allowed_access(security,
+ dir_ni, acctype))
+ bad = TRUE;
+ if (ntfs_inode_close(dir_ni))
+ bad = TRUE;
+ } else
+ bad = TRUE;
+ }
+ if (bad) {
+ ntfs_inode_close(ni);
+ ni = (ntfs_inode*)NULL;
+ }
+ }
+ }
+ return (ni);
+}
+
+/*
+ * Determine the name space of an extended attribute
+ */
+
+static int xattr_namespace(const char *name)
+{
+ int namespace;
+
+ if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) {
+ namespace = XATTRNS_NONE;
+ if (!strncmp(name, nf_ns_user_prefix,
+ nf_ns_user_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_user_prefix_len))
+ namespace = XATTRNS_USER;
+ else if (!strncmp(name, nf_ns_system_prefix,
+ nf_ns_system_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_system_prefix_len))
+ namespace = XATTRNS_SYSTEM;
+ else if (!strncmp(name, nf_ns_security_prefix,
+ nf_ns_security_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_security_prefix_len))
+ namespace = XATTRNS_SECURITY;
+ else if (!strncmp(name, nf_ns_trusted_prefix,
+ nf_ns_trusted_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_trusted_prefix_len))
+ namespace = XATTRNS_TRUSTED;
+ } else
+ namespace = XATTRNS_OPEN;
+ return (namespace);
+}
+
+/*
+ * Fix the prefix of an extended attribute
+ */
+
+static int fix_xattr_prefix(const char *name, int namespace, ntfschar **lename)
+{
+ int len;
+ char *prefixed;
+
+ *lename = (ntfschar*)NULL;
+ switch (namespace) {
+ case XATTRNS_USER :
+ /*
+ * user name space : remove user prefix
+ */
+ len = ntfs_mbstoucs(name + nf_ns_user_prefix_len, lename);
+ break;
+ case XATTRNS_SYSTEM :
+ case XATTRNS_SECURITY :
+ case XATTRNS_TRUSTED :
+ /*
+ * security, trusted and unmapped system name spaces :
+ * insert ntfs-3g prefix
+ */
+ prefixed = (char*)ntfs_malloc(strlen(xattr_ntfs_3g)
+ + strlen(name) + 1);
+ if (prefixed) {
+ strcpy(prefixed,xattr_ntfs_3g);
+ strcat(prefixed,name);
+ len = ntfs_mbstoucs(prefixed, lename);
+ free(prefixed);
+ } else
+ len = -1;
+ break;
+ case XATTRNS_OPEN :
+ /*
+ * in open name space mode : do no fix prefix
+ */
+ len = ntfs_mbstoucs(name, lename);
+ break;
+ default :
+ len = -1;
+ }
+ return (len);
+}
+
+static void ntfs_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
+{
+ ntfs_attr_search_ctx *actx = NULL;
+ ntfs_inode *ni;
+ char *list = (char*)NULL;
+ int ret = 0;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ntfs_fuse_fill_security_context(req, &security);
+#endif
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ ret = -errno;
+ goto out;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* file must be readable */
+ if (!ntfs_allowed_access(&security,ni,S_IREAD)) {
+ ret = -EACCES;
+ goto exit;
+ }
+#endif
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!actx) {
+ ret = -errno;
+ goto exit;
+ }
+ if (size) {
+ list = (char*)malloc(size);
+ if (!list) {
+ ret = -errno;
+ goto exit;
+ }
+ }
+
+ if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR)
+ || (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) {
+ ret = ntfs_fuse_listxattr_common(ni, actx, list, size,
+ ctx->streams == NF_STREAMS_INTERFACE_XATTR);
+ if (ret < 0)
+ goto exit;
+ }
+ if (errno != ENOENT)
+ ret = -errno;
+exit:
+ if (actx)
+ ntfs_attr_put_search_ctx(actx);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&ret);
+out :
+ if (ret < 0)
+ fuse_reply_err(req, -ret);
+ else
+ if (size)
+ fuse_reply_buf(req, list, ret);
+ else
+ fuse_reply_xattr(req, ret);
+ free(list);
+}
+
+static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size)
+{
+ ntfs_inode *ni;
+ ntfs_inode *dir_ni;
+ ntfs_attr *na = NULL;
+ char *value = (char*)NULL;
+ ntfschar *lename = (ntfschar*)NULL;
+ int lename_len;
+ int res;
+ s64 rsize;
+ enum SYSTEMXATTRS attr;
+ int namespace;
+ struct SECURITY_CONTEXT security;
+
+ attr = ntfs_xattr_system_type(name,ctx->vol);
+ if (attr != XATTR_UNMAPPED) {
+ /*
+ * hijack internal data and ACL retrieval, whatever
+ * mode was selected for xattr (from the user's
+ * point of view, ACLs are not xattr)
+ */
+ if (size)
+ value = (char*)ntfs_malloc(size);
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ if (!size || value) {
+ ni = ntfs_check_access_xattr(req, &security, ino,
+ attr, FALSE);
+ if (ni) {
+ if (ntfs_allowed_access(&security,ni,S_IREAD)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = ntfs_dir_parent_inode(ni);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_getxattr(&security,
+ attr, ni, dir_ni, value, size);
+ if (dir_ni && ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ }
+#else
+ /*
+ * Standard access control has been done by fuse/kernel
+ */
+ if (!size || value) {
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (ni) {
+ /* user mapping not mandatory */
+ ntfs_fuse_fill_security_context(req, &security);
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = ntfs_dir_parent_inode(ni);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_getxattr(&security,
+ attr, ni, dir_ni, value, size);
+ if (dir_ni && ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ } else
+ res = -errno;
+#endif
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ if (size)
+ fuse_reply_buf(req, value, res);
+ else
+ fuse_reply_xattr(req, res);
+ free(value);
+ return;
+ }
+ if (ctx->streams == NF_STREAMS_INTERFACE_NONE) {
+ res = -EOPNOTSUPP;
+ goto out;
+ }
+ namespace = xattr_namespace(name);
+ if (namespace == XATTRNS_NONE) {
+ res = -EOPNOTSUPP;
+ goto out;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ntfs_fuse_fill_security_context(req,&security);
+ /* trusted only readable by root */
+ if ((namespace == XATTRNS_TRUSTED)
+ && security.uid) {
+ res = -EPERM;
+ goto out;
+ }
+#endif
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto out;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* file must be readable */
+ if (!ntfs_allowed_access(&security, ni, S_IREAD)) {
+ res = -errno;
+ goto exit;
+ }
+#endif
+ lename_len = fix_xattr_prefix(name, namespace, &lename);
+ if (lename_len == -1) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
+ if (!na) {
+ res = -ENODATA;
+ goto exit;
+ }
+ rsize = na->data_size;
+ if (ctx->efs_raw
+ && rsize
+ && (na->data_flags & ATTR_IS_ENCRYPTED)
+ && NAttrNonResident(na))
+ rsize = ((na->data_size + 511) & ~511) + 2;
+ if (size) {
+ if (size >= (size_t)rsize) {
+ value = (char*)ntfs_malloc(rsize);
+ if (value)
+ res = ntfs_attr_pread(na, 0, rsize, value);
+ if (!value || (res != rsize))
+ res = -errno;
+ } else
+ res = -ERANGE;
+ } else
+ res = rsize;
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ free(lename);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+
+out :
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ if (size)
+ fuse_reply_buf(req, value, res);
+ else
+ fuse_reply_xattr(req, res);
+ free(value);
+}
+
+static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags)
+{
+ ntfs_inode *ni;
+ ntfs_inode *dir_ni;
+ ntfs_attr *na = NULL;
+ ntfschar *lename = NULL;
+ int res, lename_len;
+ size_t total;
+ s64 part;
+ enum SYSTEMXATTRS attr;
+ int namespace;
+ struct SECURITY_CONTEXT security;
+
+ attr = ntfs_xattr_system_type(name,ctx->vol);
+ if (attr != XATTR_UNMAPPED) {
+ /*
+ * hijack internal data and ACL setting, whatever
+ * mode was selected for xattr (from the user's
+ * point of view, ACLs are not xattr)
+ * Note : updating an ACL does not set ctime
+ */
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ni = ntfs_check_access_xattr(req,&security,ino,attr,TRUE);
+ if (ni) {
+ if (ntfs_allowed_as_owner(&security, ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = ntfs_dir_parent_inode(ni);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_setxattr(&security,
+ attr, ni, dir_ni, value, size, flags);
+ /* never have to close dir_ni */
+ if (res)
+ res = -errno;
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#else
+ /* creation of a new name is not controlled by fuse */
+ if (attr == XATTR_NTFS_DOS_NAME)
+ ni = ntfs_check_access_xattr(req, &security,
+ ino, attr, TRUE);
+ else
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (ni) {
+ /*
+ * user mapping is not mandatory
+ * if defined, only owner is allowed
+ */
+ if (!ntfs_fuse_fill_security_context(req, &security)
+ || ntfs_allowed_as_owner(&security, ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = ntfs_dir_parent_inode(ni);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_setxattr(&security,
+ attr, ni, dir_ni, value, size, flags);
+ /* never have to close dir_ni */
+ if (res)
+ res = -errno;
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#endif
+#if CACHEING && !defined(FUSE_INTERNAL)
+ /*
+ * Most of system xattr settings cause changes to some
+ * file attribute (st_mode, st_nlink, st_mtime, etc.),
+ * so we must invalidate cached data when cacheing is
+ * in use (not possible with internal fuse or external
+ * fuse before 2.8)
+ */
+ if ((res >= 0)
+ && fuse_lowlevel_notify_inval_inode(ctx->fc, ino, -1, 0))
+ res = -errno;
+#endif
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+ return;
+ }
+ if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
+ && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) {
+ res = -EOPNOTSUPP;
+ goto out;
+ }
+ namespace = xattr_namespace(name);
+ if (namespace == XATTRNS_NONE) {
+ res = -EOPNOTSUPP;
+ goto out;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ntfs_fuse_fill_security_context(req,&security);
+ /* security and trusted only settable by root */
+ if (((namespace == XATTRNS_SECURITY)
+ || (namespace == XATTRNS_TRUSTED))
+ && security.uid) {
+ res = -EPERM;
+ goto out;
+ }
+#endif
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto out;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ switch (namespace) {
+ case XATTRNS_SECURITY :
+ case XATTRNS_TRUSTED :
+ if (security.uid) {
+ res = -EPERM;
+ goto exit;
+ }
+ break;
+ case XATTRNS_SYSTEM :
+ if (!ntfs_allowed_as_owner(&security, ni)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ default :
+ if (!ntfs_allowed_access(&security,ni,S_IWRITE)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ }
+#endif
+ lename_len = fix_xattr_prefix(name, namespace, &lename);
+ if ((lename_len == -1)
+ || (ctx->windows_names
+ && ntfs_forbidden_chars(lename,lename_len))) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
+ if (na && flags == XATTR_CREATE) {
+ res = -EEXIST;
+ goto exit;
+ }
+ if (!na) {
+ if (flags == XATTR_REPLACE) {
+ res = -ENODATA;
+ goto exit;
+ }
+ if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) {
+ res = -errno;
+ goto exit;
+ }
+ if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
+ set_archive(ni);
+ NInoFileNameSetDirty(ni);
+ }
+ na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ } else {
+ /* currently compressed streams can only be wiped out */
+ if (ntfs_attr_truncate(na, (s64)0 /* size */)) {
+ res = -errno;
+ goto exit;
+ }
+ }
+ total = 0;
+ res = 0;
+ if (size) {
+ do {
+ part = ntfs_attr_pwrite(na, total, size - total,
+ &value[total]);
+ if (part > 0)
+ total += part;
+ } while ((part > 0) && (total < size));
+ }
+ if ((total != size) || ntfs_attr_pclose(na))
+ res = -errno;
+ else {
+ if (ctx->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED)) {
+ if (ntfs_efs_fixup_attribute(NULL,na))
+ res = -errno;
+ }
+ }
+ if (!res && !(ni->flags & FILE_ATTR_ARCHIVE)) {
+ set_archive(ni);
+ NInoFileNameSetDirty(ni);
+ }
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ free(lename);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+out :
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
+{
+ ntfs_inode *ni;
+ ntfs_inode *dir_ni;
+ ntfschar *lename = NULL;
+ int res = 0, lename_len;
+ enum SYSTEMXATTRS attr;
+ int namespace;
+ struct SECURITY_CONTEXT security;
+
+ attr = ntfs_xattr_system_type(name,ctx->vol);
+ if (attr != XATTR_UNMAPPED) {
+ switch (attr) {
+ /*
+ * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES
+ * is never allowed
+ */
+ case XATTR_NTFS_ACL :
+ case XATTR_NTFS_ATTRIB :
+ case XATTR_NTFS_ATTRIB_BE :
+ case XATTR_NTFS_EFSINFO :
+ case XATTR_NTFS_TIMES :
+ case XATTR_NTFS_TIMES_BE :
+ case XATTR_NTFS_CRTIME :
+ case XATTR_NTFS_CRTIME_BE :
+ res = -EPERM;
+ break;
+ default :
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ni = ntfs_check_access_xattr(req, &security, ino,
+ attr,TRUE);
+ if (ni) {
+ if (ntfs_allowed_as_owner(&security, ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = ntfs_dir_parent_inode(ni);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_removexattr(&security,
+ attr, ni, dir_ni);
+ if (res)
+ res = -errno;
+ /* never have to close dir_ni */
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#else
+ /* creation of a new name is not controlled by fuse */
+ if (attr == XATTR_NTFS_DOS_NAME)
+ ni = ntfs_check_access_xattr(req, &security,
+ ino, attr, TRUE);
+ else
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (ni) {
+ /*
+ * user mapping is not mandatory
+ * if defined, only owner is allowed
+ */
+ if (!ntfs_fuse_fill_security_context(req, &security)
+ || ntfs_allowed_as_owner(&security, ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = ntfs_dir_parent_inode(ni);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_removexattr(&security,
+ attr, ni, dir_ni);
+ /* never have to close dir_ni */
+ if (res)
+ res = -errno;
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#endif
+#if CACHEING && !defined(FUSE_INTERNAL)
+ /*
+ * Some allowed system xattr removals cause changes to
+ * some file attribute (st_mode, st_nlink, etc.),
+ * so we must invalidate cached data when cacheing is
+ * in use (not possible with internal fuse or external
+ * fuse before 2.8)
+ */
+ if ((res >= 0)
+ && fuse_lowlevel_notify_inval_inode(ctx->fc,
+ ino, -1, 0))
+ res = -errno;
+#endif
+ break;
+ }
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+ return;
+ }
+ if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
+ && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) {
+ res = -EOPNOTSUPP;
+ goto out;
+ }
+ namespace = xattr_namespace(name);
+ if (namespace == XATTRNS_NONE) {
+ res = -EOPNOTSUPP;
+ goto out;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ntfs_fuse_fill_security_context(req,&security);
+ /* security and trusted only settable by root */
+ if (((namespace == XATTRNS_SECURITY)
+ || (namespace == XATTRNS_TRUSTED))
+ && security.uid) {
+ res = -EACCES;
+ goto out;
+ }
+#endif
+ ni = ntfs_inode_open(ctx->vol, INODE(ino));
+ if (!ni) {
+ res = -errno;
+ goto out;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ switch (namespace) {
+ case XATTRNS_SECURITY :
+ case XATTRNS_TRUSTED :
+ if (security.uid) {
+ res = -EPERM;
+ goto exit;
+ }
+ break;
+ case XATTRNS_SYSTEM :
+ if (!ntfs_allowed_as_owner(&security, ni)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ default :
+ if (!ntfs_allowed_access(&security,ni,S_IWRITE)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ }
+#endif
+ lename_len = fix_xattr_prefix(name, namespace, &lename);
+ if (lename_len == -1) {
+ res = -errno;
+ goto exit;
+ }
+ if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) {
+ if (errno == ENOENT)
+ errno = ENODATA;
+ res = -errno;
+ }
+ if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
+ set_archive(ni);
+ NInoFileNameSetDirty(ni);
+ }
+exit:
+ free(lename);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+out :
+ if (res < 0)
+ fuse_reply_err(req, -res);
+ else
+ fuse_reply_err(req, 0);
+ return;
+
+}
+
+#else
+#if POSIXACLS
+#error "Option inconsistency : POSIXACLS requires SETXATTR"
+#endif
+#endif /* HAVE_SETXATTR */
+
+static void ntfs_close(void)
+{
+ struct SECURITY_CONTEXT security;
+
+ if (!ctx)
+ return;
+
+ if (!ctx->vol)
+ return;
+
+ if (ctx->mounted) {
+ ntfs_log_info("Unmounting %s (%s)\n", opts.device,
+ ctx->vol->vol_name);
+ if (ntfs_fuse_fill_security_context((fuse_req_t)NULL, &security)) {
+ if (ctx->seccache && ctx->seccache->head.p_reads) {
+ ntfs_log_info("Permissions cache : %lu writes, "
+ "%lu reads, %lu.%1lu%% hits\n",
+ ctx->seccache->head.p_writes,
+ ctx->seccache->head.p_reads,
+ 100 * ctx->seccache->head.p_hits
+ / ctx->seccache->head.p_reads,
+ 1000 * ctx->seccache->head.p_hits
+ / ctx->seccache->head.p_reads % 10);
+ }
+ }
+ ntfs_close_secure(&security);
+ }
+
+ if (ntfs_umount(ctx->vol, FALSE))
+ ntfs_log_perror("Failed to close volume %s", opts.device);
+
+ ctx->vol = NULL;
+}
+
+static void ntfs_fuse_destroy2(void *notused __attribute__((unused)))
+{
+ ntfs_close();
+}
+
+static struct fuse_lowlevel_ops ntfs_3g_ops = {
+ .lookup = ntfs_fuse_lookup,
+ .getattr = ntfs_fuse_getattr,
+ .readlink = ntfs_fuse_readlink,
+ .opendir = ntfs_fuse_opendir,
+ .readdir = ntfs_fuse_readdir,
+ .releasedir = ntfs_fuse_releasedir,
+ .open = ntfs_fuse_open,
+ .release = ntfs_fuse_release,
+ .read = ntfs_fuse_read,
+ .write = ntfs_fuse_write,
+ .setattr = ntfs_fuse_setattr,
+ .statfs = ntfs_fuse_statfs,
+ .create = ntfs_fuse_create_file,
+ .mknod = ntfs_fuse_mknod,
+ .symlink = ntfs_fuse_symlink,
+ .link = ntfs_fuse_link,
+ .unlink = ntfs_fuse_unlink,
+ .rename = ntfs_fuse_rename,
+ .mkdir = ntfs_fuse_mkdir,
+ .rmdir = ntfs_fuse_rmdir,
+ .fsync = ntfs_fuse_fsync,
+ .fsyncdir = ntfs_fuse_fsync,
+ .bmap = ntfs_fuse_bmap,
+ .destroy = ntfs_fuse_destroy2,
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ .access = ntfs_fuse_access,
+#endif
+#ifdef HAVE_SETXATTR
+ .getxattr = ntfs_fuse_getxattr,
+ .setxattr = ntfs_fuse_setxattr,
+ .removexattr = ntfs_fuse_removexattr,
+ .listxattr = ntfs_fuse_listxattr,
+#endif /* HAVE_SETXATTR */
+#if 0 && (defined(__APPLE__) || defined(__DARWIN__)) /* Unfinished. */
+ /* MacFUSE extensions. */
+ .getxtimes = ntfs_macfuse_getxtimes,
+ .setcrtime = ntfs_macfuse_setcrtime,
+ .setbkuptime = ntfs_macfuse_setbkuptime,
+ .setchgtime = ntfs_macfuse_setchgtime,
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+#if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \
+ || (defined(__APPLE__) || defined(__DARWIN__))
+ .init = ntfs_init
+#endif
+};
+
+static int ntfs_fuse_init(void)
+{
+ ctx = (ntfs_fuse_context_t*)ntfs_calloc(sizeof(ntfs_fuse_context_t));
+ if (!ctx)
+ return -1;
+
+ *ctx = (ntfs_fuse_context_t) {
+ .uid = getuid(),
+ .gid = getgid(),
+#if defined(linux)
+ .streams = NF_STREAMS_INTERFACE_XATTR,
+#else
+ .streams = NF_STREAMS_INTERFACE_NONE,
+#endif
+ .atime = ATIME_RELATIVE,
+ .silent = TRUE,
+ .recover = TRUE
+ };
+ return 0;
+}
+
+static int ntfs_open(const char *device)
+{
+ unsigned long flags = 0;
+ ntfs_volume *vol;
+
+ if (!ctx->blkdev)
+ flags |= MS_EXCLUSIVE;
+ if (ctx->ro)
+ flags |= MS_RDONLY;
+ if (ctx->recover)
+ flags |= MS_RECOVER;
+ if (ctx->hiberfile)
+ flags |= MS_IGNORE_HIBERFILE;
+
+ ctx->vol = vol = ntfs_mount(device, flags);
+ if (!vol) {
+ ntfs_log_perror("Failed to mount '%s'", device);
+ goto err_out;
+ }
+ if (ctx->sync && ctx->vol->dev)
+ NDevSetSync(ctx->vol->dev);
+ if (ctx->compression)
+ NVolSetCompression(ctx->vol);
+ else
+ NVolClearCompression(ctx->vol);
+#ifdef HAVE_SETXATTR
+ /* archivers must see hidden files */
+ if (ctx->efs_raw)
+ ctx->hide_hid_files = FALSE;
+#endif
+ if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files,
+ !ctx->hide_hid_files, ctx->hide_dot_files))
+ goto err_out;
+
+ if (ctx->ignore_case && ntfs_set_ignore_case(vol))
+ goto err_out;
+
+ vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na);
+ if (vol->free_clusters < 0) {
+ ntfs_log_perror("Failed to read NTFS $Bitmap");
+ goto err_out;
+ }
+
+ vol->free_mft_records = ntfs_get_nr_free_mft_records(vol);
+ if (vol->free_mft_records < 0) {
+ ntfs_log_perror("Failed to calculate free MFT records");
+ goto err_out;
+ }
+
+ if (ctx->hiberfile && ntfs_volume_check_hiberfile(vol, 0)) {
+ if (errno != EPERM)
+ goto err_out;
+ if (ntfs_fuse_rm((fuse_req_t)NULL,FILE_root,"hiberfil.sys"))
+ goto err_out;
+ }
+
+ errno = 0;
+err_out:
+ return ntfs_volume_error(errno);
+
+}
+
+static void usage(void)
+{
+ ntfs_log_info(usage_msg, EXEC_NAME, VERSION, FUSE_TYPE, fuse_version(),
+ 5 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING,
+ EXEC_NAME, ntfs_home);
+}
+
+#if defined(linux) || defined(__uClinux__)
+
+static const char *dev_fuse_msg =
+"HINT: You should be root, or make ntfs-3g setuid root, or load the FUSE\n"
+" kernel module as root ('modprobe fuse' or 'insmod <path_to>/fuse.ko'"
+" or insmod <path_to>/fuse.o'). Make also sure that the fuse device"
+" exists. It's usually either /dev/fuse or /dev/misc/fuse.";
+
+static const char *fuse26_kmod_msg =
+"WARNING: Deficient Linux kernel detected. Some driver features are\n"
+" not available (swap file on NTFS, boot from NTFS by LILO), and\n"
+" unmount is not safe unless it's made sure the ntfs-3g process\n"
+" naturally terminates after calling 'umount'. If you wish this\n"
+" message to disappear then you should upgrade to at least kernel\n"
+" version 2.6.20, or request help from your distribution to fix\n"
+" the kernel problem. The below web page has more information:\n"
+" http://tuxera.com/community/ntfs-3g-faq/#fuse26\n"
+"\n";
+
+static void mknod_dev_fuse(const char *dev)
+{
+ struct stat st;
+
+ if (stat(dev, &st) && (errno == ENOENT)) {
+ mode_t mask = umask(0);
+ if (mknod(dev, S_IFCHR | 0666, makedev(10, 229))) {
+ ntfs_log_perror("Failed to create '%s'", dev);
+ if (errno == EPERM)
+ ntfs_log_error("%s", dev_fuse_msg);
+ }
+ umask(mask);
+ }
+}
+
+static void create_dev_fuse(void)
+{
+ mknod_dev_fuse("/dev/fuse");
+
+#ifdef __UCLIBC__
+ {
+ struct stat st;
+ /* The fuse device is under /dev/misc using devfs. */
+ if (stat("/dev/misc", &st) && (errno == ENOENT)) {
+ mode_t mask = umask(0);
+ mkdir("/dev/misc", 0775);
+ umask(mask);
+ }
+ mknod_dev_fuse("/dev/misc/fuse");
+ }
+#endif
+}
+
+static fuse_fstype get_fuse_fstype(void)
+{
+ char buf[256];
+ fuse_fstype fstype = FSTYPE_NONE;
+
+ FILE *f = fopen("/proc/filesystems", "r");
+ if (!f) {
+ ntfs_log_perror("Failed to open /proc/filesystems");
+ return FSTYPE_UNKNOWN;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (strstr(buf, "fuseblk\n")) {
+ fstype = FSTYPE_FUSEBLK;
+ break;
+ }
+ if (strstr(buf, "fuse\n"))
+ fstype = FSTYPE_FUSE;
+ }
+
+ fclose(f);
+ return fstype;
+}
+
+static fuse_fstype load_fuse_module(void)
+{
+ int i;
+ struct stat st;
+ pid_t pid;
+ const char *cmd = "/sbin/modprobe";
+ struct timespec req = { 0, 100000000 }; /* 100 msec */
+ fuse_fstype fstype;
+
+ if (!stat(cmd, &st) && !geteuid()) {
+ pid = fork();
+ if (!pid) {
+ execl(cmd, cmd, "fuse", NULL);
+ _exit(1);
+ } else if (pid != -1)
+ waitpid(pid, NULL, 0);
+ }
+
+ for (i = 0; i < 10; i++) {
+ /*
+ * We sleep first because despite the detection of the loaded
+ * FUSE kernel module, fuse_mount() can still fail if it's not
+ * fully functional/initialized. Note, of course this is still
+ * unreliable but usually helps.
+ */
+ nanosleep(&req, NULL);
+ fstype = get_fuse_fstype();
+ if (fstype != FSTYPE_NONE)
+ break;
+ }
+ return fstype;
+}
+
+#endif
+
+static struct fuse_chan *try_fuse_mount(char *parsed_options)
+{
+ struct fuse_chan *fc = NULL;
+ struct fuse_args margs = FUSE_ARGS_INIT(0, NULL);
+
+ /* The fuse_mount() options get modified, so we always rebuild it */
+ if ((fuse_opt_add_arg(&margs, EXEC_NAME) == -1 ||
+ fuse_opt_add_arg(&margs, "-o") == -1 ||
+ fuse_opt_add_arg(&margs, parsed_options) == -1)) {
+ ntfs_log_error("Failed to set FUSE options.\n");
+ goto free_args;
+ }
+
+ fc = fuse_mount(opts.mnt_point, &margs);
+free_args:
+ fuse_opt_free_args(&margs);
+ return fc;
+
+}
+
+static int set_fuseblk_options(char **parsed_options)
+{
+ char options[64];
+ long pagesize;
+ u32 blksize = ctx->vol->cluster_size;
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize < 1)
+ pagesize = 4096;
+
+ if (blksize > (u32)pagesize)
+ blksize = pagesize;
+
+ snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize);
+ if (ntfs_strappend(parsed_options, options))
+ return -1;
+ return 0;
+}
+
+static struct fuse_session *mount_fuse(char *parsed_options)
+{
+ struct fuse_session *se = NULL;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+ ctx->fc = try_fuse_mount(parsed_options);
+ if (!ctx->fc)
+ return NULL;
+
+ if (fuse_opt_add_arg(&args, "") == -1)
+ goto err;
+ if (ctx->debug)
+ if (fuse_opt_add_arg(&args, "-odebug") == -1)
+ goto err;
+
+ se = fuse_lowlevel_new(&args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL);
+ if (!se)
+ goto err;
+
+
+ if (fuse_set_signal_handlers(se))
+ goto err_destroy;
+ fuse_session_add_chan(se, ctx->fc);
+out:
+ fuse_opt_free_args(&args);
+ return se;
+err_destroy:
+ fuse_session_destroy(se);
+ se = NULL;
+err:
+ fuse_unmount(opts.mnt_point, ctx->fc);
+ goto out;
+}
+
+static void setup_logging(char *parsed_options)
+{
+ if (!ctx->no_detach) {
+ if (daemon(0, ctx->debug))
+ ntfs_log_error("Failed to daemonize.\n");
+ else if (!ctx->debug) {
+#ifndef DEBUG
+ ntfs_log_set_handler(ntfs_log_handler_syslog);
+ /* Override default libntfs identify. */
+ openlog(EXEC_NAME, LOG_PID, LOG_DAEMON);
+#endif
+ }
+ }
+
+ ctx->seccache = (struct PERMISSIONS_CACHE*)NULL;
+
+ ntfs_log_info("Version %s %s %d\n", VERSION, FUSE_TYPE, fuse_version());
+ if (strcmp(opts.arg_device,opts.device))
+ ntfs_log_info("Requested device %s canonicalized as %s\n",
+ opts.arg_device,opts.device);
+ ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n",
+ opts.device, (ctx->ro) ? "Read-Only" : "Read-Write",
+ ctx->vol->vol_name, ctx->vol->major_ver,
+ ctx->vol->minor_ver);
+ ntfs_log_info("Cmdline options: %s\n", opts.options ? opts.options : "");
+ ntfs_log_info("Mount options: %s\n", parsed_options);
+}
+
+int main(int argc, char *argv[])
+{
+ char *parsed_options = NULL;
+ struct fuse_session *se;
+#if !(defined(__sun) && defined (__SVR4))
+ fuse_fstype fstype = FSTYPE_UNKNOWN;
+#endif
+ const char *permissions_mode = (const char*)NULL;
+ const char *failed_secure = (const char*)NULL;
+#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
+ struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL;
+#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
+ struct stat sbuf;
+ unsigned long existing_mount;
+ int err, fd;
+
+ /*
+ * Make sure file descriptors 0, 1 and 2 are open,
+ * otherwise chaos would ensue.
+ */
+ do {
+ fd = open("/dev/null", O_RDWR);
+ if (fd > 2)
+ close(fd);
+ } while (fd >= 0 && fd <= 2);
+
+#ifndef FUSE_INTERNAL
+ if ((getuid() != geteuid()) || (getgid() != getegid())) {
+ fprintf(stderr, "%s", setuid_msg);
+ return NTFS_VOLUME_INSECURE;
+ }
+#endif
+ if (drop_privs())
+ return NTFS_VOLUME_NO_PRIVILEGE;
+
+ ntfs_set_locale();
+ ntfs_log_set_handler(ntfs_log_handler_stderr);
+
+ if (ntfs_parse_options(&opts, usage, argc, argv)) {
+ usage();
+ return NTFS_VOLUME_SYNTAX_ERROR;
+ }
+
+ if (ntfs_fuse_init()) {
+ err = NTFS_VOLUME_OUT_OF_MEMORY;
+ goto err2;
+ }
+
+ parsed_options = parse_mount_options(ctx, &opts, TRUE);
+ if (!parsed_options) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+ if (!ntfs_check_if_mounted(opts.device,&existing_mount)
+ && (existing_mount & NTFS_MF_MOUNTED)) {
+ err = NTFS_VOLUME_LOCKED;
+ goto err_out;
+ }
+
+ /* need absolute mount point for junctions */
+ if (opts.mnt_point[0] == '/')
+ ctx->abs_mnt_point = strdup(opts.mnt_point);
+ else {
+ ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX);
+ if (ctx->abs_mnt_point) {
+ if (getcwd(ctx->abs_mnt_point,
+ PATH_MAX - strlen(opts.mnt_point) - 1)) {
+ strcat(ctx->abs_mnt_point, "/");
+ strcat(ctx->abs_mnt_point, opts.mnt_point);
+ }
+ }
+ }
+ if (!ctx->abs_mnt_point) {
+ err = NTFS_VOLUME_OUT_OF_MEMORY;
+ goto err_out;
+ }
+
+ ctx->security.uid = 0;
+ ctx->security.gid = 0;
+ if ((opts.mnt_point[0] == '/')
+ && !stat(opts.mnt_point,&sbuf)) {
+ /* collect owner of mount point, useful for default mapping */
+ ctx->security.uid = sbuf.st_uid;
+ ctx->security.gid = sbuf.st_gid;
+ }
+
+#if defined(linux) || defined(__uClinux__)
+ fstype = get_fuse_fstype();
+
+ err = NTFS_VOLUME_NO_PRIVILEGE;
+ if (restore_privs())
+ goto err_out;
+
+ if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN)
+ fstype = load_fuse_module();
+ create_dev_fuse();
+
+ if (drop_privs())
+ goto err_out;
+#endif
+ if (stat(opts.device, &sbuf)) {
+ ntfs_log_perror("Failed to access '%s'", opts.device);
+ err = NTFS_VOLUME_NO_PRIVILEGE;
+ goto err_out;
+ }
+
+#if !(defined(__sun) && defined (__SVR4))
+ /* Always use fuseblk for block devices unless it's surely missing. */
+ if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE))
+ ctx->blkdev = TRUE;
+#endif
+
+#ifndef FUSE_INTERNAL
+ if (getuid() && ctx->blkdev) {
+ ntfs_log_error("%s", unpriv_fuseblk_msg);
+ err = NTFS_VOLUME_NO_PRIVILEGE;
+ goto err2;
+ }
+#endif
+ err = ntfs_open(opts.device);
+ if (err)
+ goto err_out;
+
+ /* We must do this after ntfs_open() to be able to set the blksize */
+ if (ctx->blkdev && set_fuseblk_options(&parsed_options))
+ goto err_out;
+
+ ctx->security.vol = ctx->vol;
+ ctx->vol->secure_flags = ctx->secure_flags;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ ctx->vol->efs_raw = ctx->efs_raw;
+#endif /* HAVE_SETXATTR */
+ /* JPA open $Secure, (whatever NTFS version !) */
+ /* to initialize security data */
+ if (ntfs_open_secure(ctx->vol) && (ctx->vol->major_ver >= 3))
+ failed_secure = "Could not open file $Secure";
+ if (!ntfs_build_mapping(&ctx->security,ctx->usermap_path,
+ (ctx->vol->secure_flags
+ & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))
+ && !(ctx->vol->secure_flags & (1 << SECURITY_WANTED)))) {
+#if POSIXACLS
+ /* use basic permissions if requested */
+ if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))
+ permissions_mode = "User mapping built, Posix ACLs not used";
+ else {
+ permissions_mode = "User mapping built, Posix ACLs in use";
+#if KERNELACLS
+ if (ntfs_strappend(&parsed_options,
+ ",default_permissions,acl")) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+#endif /* KERNELACLS */
+ }
+#else /* POSIXACLS */
+ if (!(ctx->vol->secure_flags
+ & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) {
+ /*
+ * No explicit option but user mapping found
+ * force default security
+ */
+#if KERNELPERMS
+ ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT);
+ if (ntfs_strappend(&parsed_options, ",default_permissions")) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+#endif /* KERNELPERMS */
+ }
+ permissions_mode = "User mapping built";
+#endif /* POSIXACLS */
+ } else {
+ ctx->security.uid = ctx->uid;
+ ctx->security.gid = ctx->gid;
+ /* same ownership/permissions for all files */
+ ctx->security.mapping[MAPUSERS] = (struct MAPPING*)NULL;
+ ctx->security.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
+ if ((ctx->vol->secure_flags & (1 << SECURITY_WANTED))
+ && !(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) {
+ ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT);
+ if (ntfs_strappend(&parsed_options, ",default_permissions")) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+ }
+ if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) {
+ ctx->vol->secure_flags |= (1 << SECURITY_RAW);
+ permissions_mode = "Global ownership and permissions enforced";
+ } else {
+ ctx->vol->secure_flags &= ~(1 << SECURITY_RAW);
+ permissions_mode = "Ownership and permissions disabled";
+ }
+ }
+ if (ctx->usermap_path)
+ free (ctx->usermap_path);
+
+#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
+ xattr_mapping = ntfs_xattr_build_mapping(ctx->vol,
+ ctx->xattrmap_path);
+ ctx->vol->xattr_mapping = xattr_mapping;
+ /*
+ * Errors are logged, do not refuse mounting, it would be
+ * too difficult to fix the unmountable mapping file.
+ */
+ if (ctx->xattrmap_path)
+ free(ctx->xattrmap_path);
+#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
+
+ se = mount_fuse(parsed_options);
+ if (!se) {
+ err = NTFS_VOLUME_FUSE_ERROR;
+ goto err_out;
+ }
+
+ ctx->mounted = TRUE;
+
+#if defined(linux) || defined(__uClinux__)
+ if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE))
+ ntfs_log_info("%s", fuse26_kmod_msg);
+#endif
+ setup_logging(parsed_options);
+ if (failed_secure)
+ ntfs_log_info("%s\n",failed_secure);
+ if (permissions_mode)
+ ntfs_log_info("%s, configuration type %d\n",permissions_mode,
+ 5 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING);
+
+ fuse_session_loop(se);
+ fuse_remove_signal_handlers(se);
+
+ err = 0;
+
+ fuse_unmount(opts.mnt_point, ctx->fc);
+ fuse_session_destroy(se);
+err_out:
+ ntfs_mount_error(opts.device, opts.mnt_point, err);
+ if (ctx->abs_mnt_point)
+ free(ctx->abs_mnt_point);
+#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
+ ntfs_xattr_free_mapping(xattr_mapping);
+#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
+err2:
+ ntfs_close();
+ free(ctx);
+ free(parsed_options);
+ free(opts.options);
+ free(opts.device);
+ return err;
+}
+
diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in
new file mode 100644
index 0000000..9714ba1
--- /dev/null
+++ b/src/ntfs-3g.8.in
@@ -0,0 +1,428 @@
+.\" Copyright (c) 2005-2006 Yura Pakhuchiy.
+.\" Copyright (c) 2005 Richard Russon.
+.\" Copyright (c) 2006-2009 Szabolcs Szakacsits.
+.\" Copyright (c) 2009 Jean-Pierre Andre
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH NTFS-3G 8 "February 2010" "ntfs-3g @VERSION@"
+.SH NAME
+ntfs-3g \- Third Generation Read/Write NTFS Driver
+.SH SYNOPSIS
+.B ntfs-3g
+\fB[-o \fIoption\fP\fB[,...]]\fR
+.I volume mount_point
+.br
+.B mount \-t ntfs-3g
+\fB[-o \fIoption\fP\fB[,...]]\fR
+.I volume mount_point
+.br
+.B lowntfs-3g
+\fB[-o \fIoption\fP\fB[,...]]\fR
+.I volume mount_point
+.br
+.B mount \-t lowntfs-3g
+\fB[-o \fIoption\fP\fB[,...]]\fR
+.I volume mount_point
+.SH DESCRIPTION
+\fBntfs-3g\fR is an NTFS driver, which can create, remove, rename, move
+files, directories, hard links, and streams; it can read and write files,
+including streams, sparse files and transparently compressed files; it can
+handle special files like symbolic links, devices, and FIFOs; moreover it
+provides standard management of file ownership and permissions, including
+POSIX ACLs.
+.PP
+It comes in two variants \fBntfs-3g\fR and \fBlowntfs-3g\fR with
+a few differences mentioned below in relevant options descriptions.
+.PP
+The \fIvolume\fR to be mounted can be either a block device or
+an image file.
+.SS Access Handling and Security
+By default, files and directories are owned by the effective
+user and group of the mounting process, and everybody has
+full read, write, execution and directory browsing permissions.
+You can also assign permissions to a single user by using the
+.B uid
+and/or the
+.B gid
+options together with the
+.B umask,
+or
+.B fmask
+and
+.B dmask
+options.
+.PP
+Doing so, Windows users have full access to the files created by
+.B ntfs-3g.
+.PP
+But, by setting the \fBpermissions\fR option, you can benefit from the full
+ownership and permissions features as defined by POSIX. Moreover, by defining
+a Windows-to-Linux user mapping, the ownerships and permissions are even
+applied to Windows users and conversely.
+.PP
+If
+.B ntfs-3g
+is set setuid-root then non-root users will
+be also able to mount volumes.
+.SS Windows Filename Compatibility
+NTFS supports several filename namespaces: DOS, Win32 and POSIX. While the
+\fBntfs-3g\fR driver handles all of them, it always creates new files in the
+POSIX namespace for maximum portability and interoperability reasons.
+This means that filenames are case sensitive and all characters are
+allowed except '/' and '\\0'. This is perfectly legal on Windows, though
+some application may get confused. The option \fBwindows_names\fP may be
+used to apply Windows restrictions to new file names.
+.SS Alternate Data Streams (ADS)
+NTFS stores all data in streams. Every file has exactly one unnamed
+data stream and can have many named data streams. The size of a file is the
+size of its unnamed data stream. By default, \fBntfs-3g\fR will only read
+the unnamed data stream.
+.PP
+By using the options "streams_interface=windows", with the ntfs-3g driver
+(not possible with lowntfs-3g), you will be able to read any named data
+streams, simply by specifying the stream's name after a colon.
+For example:
+.RS
+.sp
+cat some.mp3:artist
+.sp
+.RE
+Named data streams act like normal files, so you can read from them, write to
+them and even delete them (using rm). You can list all the named data streams
+a file has by getting the "ntfs.streams.list" extended attribute.
+.SH OPTIONS
+Below is a summary of the options that \fBntfs-3g\fR accepts.
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+Set the owner and the group of files and directories. The values are numerical.
+The defaults are the uid and gid of the current process.
+.TP
+.BI umask= value
+Set the bitmask of the file and directory permissions that are not
+present. The value is given in octal. The default value is 0 which
+means full access to everybody.
+.TP
+.BI fmask= value
+Set the bitmask of the file permissions that are not present.
+The value is given in octal. The default value is 0 which
+means full access to everybody.
+.TP
+.BI dmask= value
+Set the bitmask of the directory permissions that are not
+present. The value is given in octal. The default value is 0 which
+means full access to everybody.
+.TP
+.BI usermapping= file-name
+Use file \fIfile-name\fP as the user mapping file instead of the default
+\fB.NTFS-3G/UserMapping\fP. If \fIfile-name\fP defines a full path, the
+file must be located on a partition previously mounted. If it defines a
+relative path, it is interpreted relative to the root of NTFS partition
+being mounted.
+.P
+.RS
+When a user mapping file is defined, the options \fBuid=\fP, \fBgid=\fP,
+\fBumask=\fP, \fBfmask=\fP, \fBdmask=\fP and \fBsilent\fP are ignored.
+.RE
+.TP
+.B permissions
+Set standard permissions on created files and use standard access control.
+This option is set by default when a user mapping file is present.
+.TP
+.B acl
+Enable setting Posix ACLs on created files and use them for access control.
+This option is only available on specific builds. It is set by default
+when a user mapping file is present and the
+.B permissions
+mount option is not set.
+.TP
+.B inherit
+When creating a new file, set its initial protections
+according to inheritance rules defined in parent directory. These rules
+deviate from Posix specifications, but yield a better Windows
+compatibility. The \fBcompression\fR option or a valid user mapping file
+is required for this option to be effective.
+.TP
+.B ro
+Mount filesystem read\-only. Useful if Windows is hibernated or the
+NTFS journal file is unclean.
+.TP
+.BI locale= value
+This option can be useful when wanting a language specific locale environment.
+It is however discouraged as it leads to files with untranslatable chars
+to not be visible.
+.TP
+.B force
+This option is obsolete. It has been superseded by the \fBrecover\fR and
+\fBnorecover\fR options.
+.TP
+.B recover
+Recover and try to mount a partition which was not unmounted properly by
+Windows. The Windows logfile is cleared, which may cause inconsistencies.
+Currently this is the default option.
+.TP
+.B norecover
+Do not try to mount a partition which was not unmounted properly by Windows.
+.TP
+.B ignore_case \fP(only with lowntfs-3g)
+Ignore character case when accessing a file (\fBFOO\fR, \fBFoo\fR, \fBfoo\fR,
+etc. designate the same file). All files are displayed with lower case in
+directory listings.
+.TP
+.B remove_hiberfile
+Unlike in case of read-only mount, the read-write mount is denied if
+the NTFS volume is hibernated. One needs either to resume Windows and
+shutdown it properly, or use this option which will remove the Windows
+hibernation file. Please note, this means that the saved Windows
+session will be completely lost. Use this option under your own
+responsibility.
+.TP
+.B atime, noatime, relatime
+The
+.B atime
+option updates inode access time for each access.
+
+The
+.B noatime
+option disables inode access time updates which can speed up
+file operations and prevent sleeping (notebook) disks spinning
+up too often thus saving energy and disk lifetime.
+
+The
+.B relatime
+option is very similar to
+.B noatime.
+It updates inode access times relative to modify or change time.
+The access time is only updated if the previous access time was earlier
+than the current modify or change time. Unlike
+.B noatime
+this option doesn't break applications that need to know
+if a file has been read since the last time it was modified.
+This is the default behaviour.
+.TP
+.B delay_mtime
+Delay the updating of file modification time and file change time until
+the file is closed. This is mainly useful for files which are written
+to without changing their size, such as databases or file system images
+mounted as loop.
+.TP
+.B show_sys_files
+Show the metafiles in directory listings. Otherwise the default behaviour is
+to hide the metafiles, which are special files used to store the NTFS
+structure. Please note that even when this option is specified, "$MFT" may
+not be visible due to a glibc bug. Furthermore, irrespectively of
+show_sys_files, all files are accessible by name, for example you can always
+do
+"ls \-l '$UpCase'".
+.TP
+.B hide_hid_files
+Hide the hidden files and directories in directory listings, the hidden files
+and directories being the ones whose NTFS attribute have the hidden flag set.
+The hidden files will not be selected when using wildcards in commands,
+but all files and directories remain accessible by full name, for example you
+can always display the Windows trash bin directory by :
+"ls \-ld '$RECYCLE.BIN'".
+.TP
+.B hide_dot_files
+Set the hidden flag in the NTFS attribute for created files and directories
+whose first character of the name is a dot. Such files and directories
+normally do not appear in directory listings, and when the flag is set
+they do not appear in Windows directory displays either.
+.TP
+.B windows_names
+This option prevents files, directories and extended attributes to be
+created with a name not allowed by windows, either because it contains
+some not allowed character (which are the nine characters " * / : < > ? \\ | and
+those whose code is less than 0x20) or because the last character is a space
+or a dot. Existing such files can still be read (and renamed).
+.TP
+.B allow_other
+This option overrides the security measure restricting file access
+to the user mounting the filesystem. This option is only
+allowed to root, but this restriction can be overridden by
+the 'user_allow_other' option in the /etc/fuse.conf file.
+.TP
+.BI max_read= value
+With this option the maximum size of read operations can be set.
+The default is infinite. Note that the size of read requests is
+limited anyway to 32 pages (which is 128kbyte on i386).
+.TP
+.B silent
+Do nothing, without returning any error, on chmod and chown operations,
+when the \fBpermissions\fR option is not set and no user mapping file
+is defined. This option is on by default.
+.TP
+.B no_def_opts
+By default ntfs-3g acts as if "silent" (ignore errors on chmod and chown),
+"allow_other" (allow any user to access files) and "nonempty"
+(allow mounting on non-empty directories) were set, and "no_def_opts"
+cancels these default options.
+.TP
+.BI streams_interface= value
+This option controls how the user can access Alternate Data Streams (ADS) or
+in other words, named data streams. It can be set to, one of \fBnone\fR,
+\fBwindows\fR or \fBxattr\fR. If the option is set to \fBnone\fR, the user
+will have no access to the named data streams. If it is set to \fBwindows\fR
+(not possible with lowntfs-3g), then the user can access them just like in
+Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named
+data streams are mapped to xattrs and user can manipulate them using
+\fB{get,set}fattr\fR utilities. The default is \fBxattr\fR.
+.TP
+.B user_xattr
+Same as \fBstreams_interface=\fP\fIxattr\fP.
+.TP
+.B efs_raw
+This option should only be used in backup or restore situation.
+It changes the apparent size of files and the behavior of read and
+write operation so that encrypted files can be saved and restored
+without being decrypted. The \fBuser.ntfs.efsinfo\fP extended attribute
+has also to be saved and restored for the file to be decrypted.
+.TP
+.B compression
+This option enables creating new transparently compressed files in
+directories marked for compression. A directory is marked for compression by
+setting the bit 11 (value 0x00000800) in its Windows attribute. In such a
+directory, new files are created compressed and new subdirectories are
+themselves marked for compression. The option and the flag have no effect
+on existing files.
+.TP
+.B nocompression
+This option disables creating new transparently compressed files in directories
+marked for compression. Existing compressed files can still be read and
+updated. Currently this is the default option.
+.TP
+.B big_writes
+This option prevents fuse from splitting write buffers into 4K chunks,
+enabling big write buffers to be transferred from the application in a
+single step (up to some system limit, generally 128K bytes).
+.TP
+.B debug
+Makes ntfs-3g to print a lot of debug output from libntfs-3g and FUSE.
+.TP
+.B no_detach
+Makes ntfs-3g to not detach from terminal and print some debug output.
+.SH USER MAPPING
+NTFS uses specific ids to record the ownership of files instead of
+the \fBuid\fP and \fBgid\fP used by Linux. As a consequence a mapping
+between the ids has to be defined for ownerships to be recorded into
+NTFS and recognized.
+.P
+By default, this mapping is fetched from the file \fB.NTFS-3G/UserMapping\fP
+located in the NTFS partition. The option \fBusermapping=\fP may be used
+to define another location. When the option permissions is set and
+no mapping file is found, a default mapping is used.
+.P
+Each line in the user mapping file defines a mapping. It is organized
+in three fields separated by colons. The first field identifies a \fBuid\fP,
+the second field identifies a \fBgid\fP and the third one identifies the
+corresponding NTFS id, known as a \fBSID\fP. The \fBuid\fP and the \fBgid\fP
+are optional and defining both of them for the same \fBSID\fP is not
+recommended.
+.P
+If no interoperation with Windows is needed, you can use the option
+\fBpermissions\fP to define a standard mapping. Alternately, you may define
+your own mapping by setting a single default mapping with no uid and gid. In
+both cases, files created on Linux will appear to Windows as owned by a
+foreign user, and files created on Windows will appear to Linux as owned by
+root. Just copy the example below and replace the 9 and 10-digit numbers by
+any number not greater than 4294967295. The resulting behavior is the same as
+the one with the option permission set with no ownership option and no user
+mapping file available.
+.RS
+.sp
+.B ::S-1-5-21-3141592653-589793238-462643383-10000
+.sp
+.RE
+If a strong interoperation with Windows is needed, the mapping has to be
+defined for each user and group known in both system, and the \fBSID\fPs used
+by Windows has to be collected. This will lead to a user mapping file like :
+.RS
+.sp
+.B john::S-1-5-21-3141592653-589793238-462643383-1008
+.B mary::S-1-5-21-3141592653-589793238-462643383-1009
+.B :smith:S-1-5-21-3141592653-589793238-462643383-513
+.B ::S-1-5-21-3141592653-589793238-462643383-10000
+.sp
+.RE
+.P
+The utility \fBntfs-3g.usermap\fP may be used to create such a user
+mapping file.
+.SH EXAMPLES
+Mount /dev/sda1 to /mnt/windows:
+.RS
+.sp
+.B ntfs-3g /dev/sda1 /mnt/windows
+.RE
+or
+.RS
+.B mount -t ntfs-3g /dev/sda1 /mnt/windows
+.sp
+.RE
+Mount the ntfs data partition /dev/sda3 to /mnt/data with standard Linux
+permissions applied :
+.RS
+.sp
+.B ntfs-3g -o permissions /dev/sda3 /mnt/data
+.RE
+or
+.RS
+.B mount -t ntfs-3g -o permissions /dev/sda3 /mnt/data
+.sp
+.RE
+Read\-only mount /dev/sda5 to /home/user/mnt and make user with uid 1000
+to be the owner of all files:
+.RS
+.sp
+.B ntfs-3g /dev/sda5 /home/user/mnt \-o ro,uid=1000
+.sp
+.RE
+/etc/fstab entry for the above (the sixth and last field has to be zero to
+avoid a file system check at boot time) :
+.RS
+.sp
+.B /dev/sda5 /home/user/mnt ntfs\-3g ro,uid=1000 0 0
+.sp
+.RE
+Unmount /mnt/windows:
+.RS
+.sp
+.B umount /mnt/windows
+.sp
+.RE
+.SH EXIT CODES
+To facilitate the use of the
+.B ntfs-3g
+driver in scripts, an exit code is returned to give an indication of the
+mountability status of a volume. Value 0 means success, and all other
+ones mean an error. The unique error codes are documented in the
+.BR ntfs-3g.probe (8)
+manual page.
+.SH KNOWN ISSUES
+Please see
+.RS
+.sp
+http://www.tuxera.com/support/
+.sp
+.RE
+for common questions and known issues.
+If you would find a new one in the latest release of
+the software then please send an email describing it
+in detail. You can contact the
+development team on the ntfs\-3g\-devel@lists.sf.net
+address.
+.SH AUTHORS
+.B ntfs-3g
+was based on and a major improvement to ntfsmount and libntfs which were
+written by Yura Pakhuchiy and the Linux-NTFS team. The improvements were
+made, the ntfs-3g project was initiated and currently led by long time
+Linux-NTFS team developer Szabolcs Szakacsits (szaka@tuxera.com).
+.SH THANKS
+Several people made heroic efforts, often over five or more
+years which resulted the ntfs-3g driver. Most importantly they are
+Anton Altaparmakov, Jean-Pierre André, Richard Russon, Szabolcs Szakacsits,
+Yura Pakhuchiy, Yuval Fledel, and the author of the groundbreaking FUSE
+filesystem development framework, Miklos Szeredi.
+.SH SEE ALSO
+.BR ntfs-3g.probe (8),
+.BR ntfsprogs (8),
+.BR attr (5),
+.BR getfattr (1)
diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c
new file mode 100644
index 0000000..8919761
--- /dev/null
+++ b/src/ntfs-3g.c
@@ -0,0 +1,3862 @@
+/**
+ * ntfs-3g - Third Generation NTFS Driver
+ *
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ * Copyright (c) 2005 Yuval Fledel
+ * Copyright (c) 2006-2009 Szabolcs Szakacsits
+ * Copyright (c) 2007-2011 Jean-Pierre Andre
+ * Copyright (c) 2009 Erik Larsson
+ *
+ * This file is originated from the Linux-NTFS project.
+ *
+ * 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <fuse.h>
+
+#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
+#error "***********************************************************"
+#error "* *"
+#error "* Compilation requires at least FUSE version 2.6.0! *"
+#error "* *"
+#error "***********************************************************"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <signal.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <syslog.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#if defined(__APPLE__) || defined(__DARWIN__)
+#include <sys/dirent.h>
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+
+#include "compat.h"
+#include "attrib.h"
+#include "inode.h"
+#include "volume.h"
+#include "dir.h"
+#include "unistr.h"
+#include "layout.h"
+#include "index.h"
+#include "ntfstime.h"
+#include "security.h"
+#include "reparse.h"
+#include "object_id.h"
+#include "efs.h"
+#include "logging.h"
+#include "xattrs.h"
+#include "misc.h"
+
+#include "ntfs-3g_common.h"
+
+/*
+ * The following permission checking modes are governed by
+ * the HPERMSCONFIG value in param.h
+ */
+
+/* ACLS may be checked by kernel (requires a fuse patch) or here */
+#define KERNELACLS ((HPERMSCONFIG > 6) & (HPERMSCONFIG < 10))
+/* basic permissions may be checked by kernel or here */
+#define KERNELPERMS (((HPERMSCONFIG - 1) % 6) < 3)
+/* may want to use fuse/kernel cacheing */
+#define CACHEING (!(HPERMSCONFIG % 3))
+
+#if KERNELACLS & !KERNELPERMS
+#error Incompatible options KERNELACLS and KERNELPERMS
+#endif
+
+ /* sometimes the kernel cannot check access */
+#define ntfs_real_allowed_access(scx, ni, type) ntfs_allowed_access(scx, ni, type)
+#if POSIXACLS & KERNELPERMS & !KERNELACLS
+ /* short-circuit if PERMS checked by kernel and ACLs by fs */
+#define ntfs_allowed_access(scx, ni, type) \
+ ((scx)->vol->secure_flags & (1 << SECURITY_DEFAULT) \
+ ? 1 : ntfs_allowed_access(scx, ni, type))
+#endif
+
+#define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE
+
+typedef enum {
+ FSTYPE_NONE,
+ FSTYPE_UNKNOWN,
+ FSTYPE_FUSE,
+ FSTYPE_FUSEBLK
+} fuse_fstype;
+
+typedef struct {
+ fuse_fill_dir_t filler;
+ void *buf;
+} ntfs_fuse_fill_context_t;
+
+enum {
+ CLOSE_COMPRESSED = 1,
+ CLOSE_ENCRYPTED = 2,
+ CLOSE_DMTIME = 4
+};
+
+static struct ntfs_options opts;
+
+const char *EXEC_NAME = "ntfs-3g";
+
+static ntfs_fuse_context_t *ctx;
+static u32 ntfs_sequence;
+
+static const char *usage_msg =
+"\n"
+"%s %s %s %d - Third Generation NTFS Driver\n"
+"\t\tConfiguration type %d, "
+#ifdef HAVE_SETXATTR
+"XATTRS are on, "
+#else
+"XATTRS are off, "
+#endif
+#if POSIXACLS
+"POSIX ACLS are on\n"
+#else
+"POSIX ACLS are off\n"
+#endif
+"\n"
+"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
+"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
+"Copyright (C) 2007-2011 Jean-Pierre Andre\n"
+"Copyright (C) 2009 Erik Larsson\n"
+"\n"
+"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
+"\n"
+"Options: ro (read-only mount), remove_hiberfile, uid=, gid=,\n"
+" umask=, fmask=, dmask=, streams_interface=.\n"
+" Please see the details in the manual (type: man ntfs-3g).\n"
+"\n"
+"Example: ntfs-3g /dev/sda1 /mnt/windows\n"
+"\n"
+"%s";
+
+static const char ntfs_bad_reparse[] = "unsupported reparse point";
+
+#ifdef FUSE_INTERNAL
+int drop_privs(void);
+int restore_privs(void);
+#else
+/*
+ * setuid and setgid root ntfs-3g denies to start with external FUSE,
+ * therefore the below functions are no-op in such case.
+ */
+static int drop_privs(void) { return 0; }
+#if defined(linux) || defined(__uClinux__)
+static int restore_privs(void) { return 0; }
+#endif
+
+static const char *setuid_msg =
+"Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n"
+"external FUSE library. Either remove the setuid/setgid bit from the binary\n"
+"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n"
+"Please see more information at\n"
+"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
+
+static const char *unpriv_fuseblk_msg =
+"Unprivileged user can not mount NTFS block devices using the external FUSE\n"
+"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n"
+"FUSE support and make it setuid root. Please see more information at\n"
+"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
+#endif
+
+
+/**
+ * ntfs_fuse_is_named_data_stream - check path to be to named data stream
+ * @path: path to check
+ *
+ * Returns 1 if path is to named data stream or 0 otherwise.
+ */
+static int ntfs_fuse_is_named_data_stream(const char *path)
+{
+ if (strchr(path, ':') && ctx->streams == NF_STREAMS_INTERFACE_WINDOWS)
+ return 1;
+ return 0;
+}
+
+static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask)
+{
+ if (ctx->atime == ATIME_DISABLED)
+ mask &= ~NTFS_UPDATE_ATIME;
+ else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME &&
+ (le64_to_cpu(ni->last_access_time)
+ >= le64_to_cpu(ni->last_data_change_time)) &&
+ (le64_to_cpu(ni->last_access_time)
+ >= le64_to_cpu(ni->last_mft_change_time)))
+ return;
+ ntfs_inode_update_times(ni, mask);
+}
+
+static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol)
+{
+ ntfs_attr *na = vol->mftbmp_na;
+ s64 nr_free = ntfs_attr_get_free_bits(na);
+
+ if (nr_free >= 0)
+ nr_free += (na->allocated_size - na->data_size) << 3;
+ return nr_free;
+}
+
+/*
+ * Fill a security context as needed by security functions
+ * returns TRUE if there is a user mapping,
+ * FALSE if there is none
+ * This is not an error and the context is filled anyway,
+ * it is used for implicit Windows-like inheritance
+ */
+
+static BOOL ntfs_fuse_fill_security_context(struct SECURITY_CONTEXT *scx)
+{
+ struct fuse_context *fusecontext;
+
+ scx->vol = ctx->vol;
+ scx->mapping[MAPUSERS] = ctx->security.mapping[MAPUSERS];
+ scx->mapping[MAPGROUPS] = ctx->security.mapping[MAPGROUPS];
+ scx->pseccache = &ctx->seccache;
+ fusecontext = fuse_get_context();
+ scx->uid = fusecontext->uid;
+ scx->gid = fusecontext->gid;
+ scx->tid = fusecontext->pid;
+#ifdef FUSE_CAP_DONT_MASK
+ /* the umask can be processed by the file system */
+ scx->umask = fusecontext->umask;
+#else
+ /* the umask if forced by fuse on creation */
+ scx->umask = 0;
+#endif
+
+ return (ctx->security.mapping[MAPUSERS] != (struct MAPPING*)NULL);
+}
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+
+/*
+ * Check access to parent directory
+ *
+ * directory and file inodes are only opened when not fed in,
+ * they *HAVE TO* be fed in when already open, however
+ * file inode is only useful when S_ISVTX is requested
+ *
+ * returns 1 if allowed,
+ * 0 if not allowed or some error occurred (errno tells why)
+ */
+
+static int ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
+ const char *path, ntfs_inode *dir_ni,
+ ntfs_inode *ni, mode_t accesstype)
+{
+ int allowed;
+ ntfs_inode *ni2;
+ ntfs_inode *dir_ni2;
+ char *dirpath;
+ char *name;
+ struct stat stbuf;
+
+#if POSIXACLS & KERNELPERMS & !KERNELACLS
+ /* short-circuit if PERMS checked by kernel and ACLs by fs */
+ if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT))
+ allowed = 1;
+ else
+#endif
+ if (dir_ni)
+ allowed = ntfs_real_allowed_access(scx, dir_ni,
+ accesstype);
+ else {
+ allowed = 0;
+ dirpath = strdup(path);
+ if (dirpath) {
+ /* the root of file system is seen as a parent of itself */
+ /* is that correct ? */
+ name = strrchr(dirpath, '/');
+ *name = 0;
+ dir_ni2 = ntfs_pathname_to_inode(scx->vol,
+ NULL, dirpath);
+ if (dir_ni2) {
+ allowed = ntfs_real_allowed_access(scx,
+ dir_ni2, accesstype);
+ if (ntfs_inode_close(dir_ni2))
+ allowed = 0;
+ }
+ free(dirpath);
+ }
+ }
+ /*
+ * for a not-owned sticky directory, have to
+ * check whether file itself is owned
+ */
+ if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
+ && (allowed == 2)) {
+ if (ni)
+ ni2 = ni;
+ else
+ ni2 = ntfs_pathname_to_inode(scx->vol, NULL,
+ path);
+ allowed = 0;
+ if (ni2) {
+ allowed = (ntfs_get_owner_mode(scx,ni2,&stbuf)
+ >= 0)
+ && (stbuf.st_uid == scx->uid);
+ if (!ni)
+ ntfs_inode_close(ni2);
+ }
+ }
+ return (allowed);
+}
+
+#endif
+
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+
+/*
+ * Check access to parent directory
+ *
+ * for non-standard cases where access control cannot be checked by kernel
+ *
+ * no known situations where S_ISVTX is requested
+ *
+ * returns 1 if allowed,
+ * 0 if not allowed or some error occurred (errno tells why)
+ */
+
+static int ntfs_allowed_real_dir_access(struct SECURITY_CONTEXT *scx,
+ const char *path, ntfs_inode *dir_ni,
+ mode_t accesstype)
+{
+ int allowed;
+ ntfs_inode *dir_ni2;
+ char *dirpath;
+ char *name;
+
+ if (dir_ni)
+ allowed = ntfs_real_allowed_access(scx, dir_ni, accesstype);
+ else {
+ allowed = 0;
+ dirpath = strdup(path);
+ if (dirpath) {
+ /* the root of file system is seen as a parent of itself */
+ /* is that correct ? */
+ name = strrchr(dirpath, '/');
+ *name = 0;
+ dir_ni2 = ntfs_pathname_to_inode(scx->vol, NULL,
+ dirpath);
+ if (dir_ni2) {
+ allowed = ntfs_real_allowed_access(scx,
+ dir_ni2, accesstype);
+ if (ntfs_inode_close(dir_ni2))
+ allowed = 0;
+ }
+ free(dirpath);
+ }
+ }
+ return (allowed);
+}
+
+static ntfs_inode *get_parent_dir(const char *path)
+{
+ ntfs_inode *dir_ni;
+ char *dirpath;
+ char *p;
+
+ dirpath = strdup(path);
+ dir_ni = (ntfs_inode*)NULL;
+ if (dirpath) {
+ p = strrchr(dirpath,'/');
+ if (p) { /* always present, be safe */
+ *p = 0;
+ dir_ni = ntfs_pathname_to_inode(ctx->vol,
+ NULL, dirpath);
+ }
+ free(dirpath);
+ } else
+ errno = ENOMEM;
+ return (dir_ni);
+}
+
+
+#endif /* HAVE_SETXATTR */
+
+/**
+ * ntfs_fuse_statfs - return information about mounted NTFS volume
+ * @path: ignored (but fuse requires it)
+ * @sfs: statfs structure in which to return the information
+ *
+ * Return information about the mounted NTFS volume @sb in the statfs structure
+ * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is
+ * called). We interpret the values to be correct of the moment in time at
+ * which we are called. Most values are variable otherwise and this isn't just
+ * the free values but the totals as well. For example we can increase the
+ * total number of file nodes if we run out and we can keep doing this until
+ * there is no more space on the volume left at all.
+ *
+ * This code based on ntfs_statfs from ntfs kernel driver.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+static int ntfs_fuse_statfs(const char *path __attribute__((unused)),
+ struct statvfs *sfs)
+{
+ s64 size;
+ int delta_bits;
+ ntfs_volume *vol;
+
+ vol = ctx->vol;
+ if (!vol)
+ return -ENODEV;
+
+ /*
+ * File system block size. Used to calculate used/free space by df.
+ * Incorrectly documented as "optimal transfer block size".
+ */
+ sfs->f_bsize = vol->cluster_size;
+
+ /* Fundamental file system block size, used as the unit. */
+ sfs->f_frsize = vol->cluster_size;
+
+ /*
+ * Total number of blocks on file system in units of f_frsize.
+ * Since inodes are also stored in blocks ($MFT is a file) hence
+ * this is the number of clusters on the volume.
+ */
+ sfs->f_blocks = vol->nr_clusters;
+
+ /* Free blocks available for all and for non-privileged processes. */
+ size = vol->free_clusters;
+ if (size < 0)
+ size = 0;
+ sfs->f_bavail = sfs->f_bfree = size;
+
+ /* Free inodes on the free space */
+ delta_bits = vol->cluster_size_bits - vol->mft_record_size_bits;
+ if (delta_bits >= 0)
+ size <<= delta_bits;
+ else
+ size >>= -delta_bits;
+
+ /* Number of inodes at this point in time. */
+ sfs->f_files = (vol->mftbmp_na->allocated_size << 3) + size;
+
+ /* Free inodes available for all and for non-privileged processes. */
+ size += vol->free_mft_records;
+ if (size < 0)
+ size = 0;
+ sfs->f_ffree = sfs->f_favail = size;
+
+ /* Maximum length of filenames. */
+ sfs->f_namemax = NTFS_MAX_NAME_LEN;
+ return 0;
+}
+
+/**
+ * ntfs_fuse_parse_path - split path to path and stream name.
+ * @org_path: path to split
+ * @path: pointer to buffer in which parsed path saved
+ * @stream_name: pointer to buffer where stream name in unicode saved
+ *
+ * This function allocates buffers for @*path and @*stream, user must free them
+ * after use.
+ *
+ * Return values:
+ * <0 Error occurred, return -errno;
+ * 0 No stream name, @*stream is not allocated and set to AT_UNNAMED.
+ * >0 Stream name length in unicode characters.
+ */
+static int ntfs_fuse_parse_path(const char *org_path, char **path,
+ ntfschar **stream_name)
+{
+ char *stream_name_mbs;
+ int res;
+
+ stream_name_mbs = strdup(org_path);
+ if (!stream_name_mbs)
+ return -errno;
+ if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) {
+ *path = strsep(&stream_name_mbs, ":");
+ if (stream_name_mbs) {
+ *stream_name = NULL;
+ res = ntfs_mbstoucs(stream_name_mbs, stream_name);
+ if (res < 0)
+ return -errno;
+ return res;
+ }
+ } else
+ *path = stream_name_mbs;
+ *stream_name = AT_UNNAMED;
+ return 0;
+}
+
+static void set_fuse_error(int *err)
+{
+ if (!*err)
+ *err = -errno;
+}
+
+#if defined(__APPLE__) || defined(__DARWIN__)
+static int ntfs_macfuse_getxtimes(const char *org_path,
+ struct timespec *bkuptime, struct timespec *crtime)
+{
+ int res = 0;
+ ntfs_inode *ni;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len;
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ memset(bkuptime, 0, sizeof(struct timespec));
+ memset(crtime, 0, sizeof(struct timespec));
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+
+ /* We have no backup timestamp in NTFS. */
+ crtime->tv_sec = ni->creation_time;
+exit:
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+int ntfs_macfuse_setcrtime(const char *path, const struct timespec *tv)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ if (tv) {
+ ni->creation_time = tv->tv_sec;
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ }
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+int ntfs_macfuse_setbkuptime(const char *path, const struct timespec *tv)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ /*
+ * Only pretending to set backup time successfully to please the APIs of
+ * Mac OS X. In reality, NTFS has no backup time.
+ */
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ if (tv) {
+ ni->last_mft_change_time = tv->tv_sec;
+ ntfs_fuse_update_times(ni, 0);
+ }
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+
+#if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \
+ || (defined(__APPLE__) || defined(__DARWIN__))
+static void *ntfs_init(struct fuse_conn_info *conn)
+{
+#if defined(__APPLE__) || defined(__DARWIN__)
+ FUSE_ENABLE_XTIMES(conn);
+#endif
+#ifdef FUSE_CAP_DONT_MASK
+ /* request umask not to be enforced by fuse */
+ conn->want |= FUSE_CAP_DONT_MASK;
+#endif /* defined FUSE_CAP_DONT_MASK */
+#ifdef FUSE_CAP_BIG_WRITES
+ if (ctx->big_writes
+ && ((ctx->vol->nr_clusters << ctx->vol->cluster_size_bits)
+ >= SAFE_CAPACITY_FOR_BIG_WRITES))
+ conn->want |= FUSE_CAP_BIG_WRITES;
+#endif
+ return NULL;
+}
+#endif /* defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) */
+
+static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
+{
+ int res = 0;
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len;
+ BOOL withusermapping;
+ struct SECURITY_CONTEXT security;
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ memset(stbuf, 0, sizeof(struct stat));
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ withusermapping = ntfs_fuse_fill_security_context(&security);
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * make sure the parent directory is searchable
+ */
+ if (withusermapping
+ && !ntfs_allowed_dir_access(&security,path,
+ (!strcmp(org_path,"/") ? ni : (ntfs_inode*)NULL),
+ ni, S_IEXEC)) {
+ res = -EACCES;
+ goto exit;
+ }
+#endif
+ if (((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
+ || (ni->flags & FILE_ATTR_REPARSE_POINT))
+ && !stream_name_len) {
+ if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+ char *target;
+ int attr_size;
+
+ errno = 0;
+ target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
+ /*
+ * If the reparse point is not a valid
+ * directory junction, and there is no error
+ * we still display as a symlink
+ */
+ if (target || (errno == EOPNOTSUPP)) {
+ /* returning attribute size */
+ if (target)
+ stbuf->st_size = attr_size;
+ else
+ stbuf->st_size = sizeof(ntfs_bad_reparse);
+ stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
+ stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
+ stbuf->st_mode = S_IFLNK;
+ free(target);
+ } else {
+ res = -errno;
+ goto exit;
+ }
+ } else {
+ /* Directory. */
+ stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
+ /* get index size, if not known */
+ if (!test_nino_flag(ni, KnownSize)) {
+ na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
+ if (na) {
+ ni->data_size = na->data_size;
+ ni->allocated_size = na->allocated_size;
+ set_nino_flag(ni, KnownSize);
+ ntfs_attr_close(na);
+ }
+ }
+ stbuf->st_size = ni->data_size;
+ stbuf->st_blocks = ni->allocated_size >> 9;
+ stbuf->st_nlink = 1; /* Make find(1) work */
+ }
+ } else {
+ /* Regular or Interix (INTX) file. */
+ stbuf->st_mode = S_IFREG;
+ stbuf->st_size = ni->data_size;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /*
+ * return data size rounded to next 512 byte boundary for
+ * encrypted files to include padding required for decryption
+ * also include 2 bytes for padding info
+ */
+ if (ctx->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED)
+ && ni->data_size)
+ stbuf->st_size = ((ni->data_size + 511) & ~511) + 2;
+#endif /* HAVE_SETXATTR */
+ /*
+ * Temporary fix to make ActiveSync work via Samba 3.0.
+ * See more on the ntfs-3g-devel list.
+ */
+ stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
+ stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
+ if (ni->flags & FILE_ATTR_SYSTEM || stream_name_len) {
+ na = ntfs_attr_open(ni, AT_DATA, stream_name,
+ stream_name_len);
+ if (!na) {
+ if (stream_name_len) {
+ res = -ENOENT;
+ goto exit;
+ } else
+ goto nodata;
+ }
+ if (stream_name_len) {
+ stbuf->st_size = na->data_size;
+ stbuf->st_blocks = na->allocated_size >> 9;
+ }
+ /* Check whether it's Interix FIFO or socket. */
+ if (!(ni->flags & FILE_ATTR_HIDDEN) &&
+ !stream_name_len) {
+ /* FIFO. */
+ if (na->data_size == 0)
+ stbuf->st_mode = S_IFIFO;
+ /* Socket link. */
+ if (na->data_size == 1)
+ stbuf->st_mode = S_IFSOCK;
+ }
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* encrypted named stream */
+ /* round size up to next 512 byte boundary */
+ if (ctx->efs_raw && stream_name_len &&
+ (na->data_flags & ATTR_IS_ENCRYPTED) &&
+ NAttrNonResident(na))
+ stbuf->st_size = ((na->data_size+511) & ~511)+2;
+#endif /* HAVE_SETXATTR */
+ /*
+ * Check whether it's Interix symbolic link, block or
+ * character device.
+ */
+ if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)
+ + sizeof(ntfschar) * PATH_MAX
+ && (size_t)na->data_size >
+ sizeof(INTX_FILE_TYPES)
+ && !stream_name_len) {
+
+ INTX_FILE *intx_file;
+
+ intx_file = ntfs_malloc(na->data_size);
+ if (!intx_file) {
+ res = -errno;
+ ntfs_attr_close(na);
+ goto exit;
+ }
+ if (ntfs_attr_pread(na, 0, na->data_size,
+ intx_file) != na->data_size) {
+ res = -errno;
+ free(intx_file);
+ ntfs_attr_close(na);
+ goto exit;
+ }
+ if (intx_file->magic == INTX_BLOCK_DEVICE &&
+ na->data_size == offsetof(
+ INTX_FILE, device_end)) {
+ stbuf->st_mode = S_IFBLK;
+ stbuf->st_rdev = makedev(le64_to_cpu(
+ intx_file->major),
+ le64_to_cpu(
+ intx_file->minor));
+ }
+ if (intx_file->magic == INTX_CHARACTER_DEVICE &&
+ na->data_size == offsetof(
+ INTX_FILE, device_end)) {
+ stbuf->st_mode = S_IFCHR;
+ stbuf->st_rdev = makedev(le64_to_cpu(
+ intx_file->major),
+ le64_to_cpu(
+ intx_file->minor));
+ }
+ if (intx_file->magic == INTX_SYMBOLIC_LINK)
+ stbuf->st_mode = S_IFLNK;
+ free(intx_file);
+ }
+ ntfs_attr_close(na);
+ }
+ stbuf->st_mode |= (0777 & ~ctx->fmask);
+ }
+ if (withusermapping) {
+ if (ntfs_get_owner_mode(&security,ni,stbuf) < 0)
+ set_fuse_error(&res);
+ } else {
+ stbuf->st_uid = ctx->uid;
+ stbuf->st_gid = ctx->gid;
+ }
+ if (S_ISLNK(stbuf->st_mode))
+ stbuf->st_mode |= 0777;
+nodata :
+ stbuf->st_ino = ni->mft_no;
+#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC
+ stbuf->st_atimespec = ntfs2timespec(ni->last_access_time);
+ stbuf->st_ctimespec = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_mtimespec = ntfs2timespec(ni->last_data_change_time);
+#elif defined(HAVE_STRUCT_STAT_ST_ATIM)
+ stbuf->st_atim = ntfs2timespec(ni->last_access_time);
+ stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time);
+#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
+ {
+ struct timespec ts;
+
+ ts = ntfs2timespec(ni->last_access_time);
+ stbuf->st_atime = ts.tv_sec;
+ stbuf->st_atimensec = ts.tv_nsec;
+ ts = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_ctime = ts.tv_sec;
+ stbuf->st_ctimensec = ts.tv_nsec;
+ ts = ntfs2timespec(ni->last_data_change_time);
+ stbuf->st_mtime = ts.tv_sec;
+ stbuf->st_mtimensec = ts.tv_nsec;
+ }
+#else
+#warning "No known way to set nanoseconds in struct stat !"
+ {
+ struct timespec ts;
+
+ ts = ntfs2timespec(ni->last_access_time);
+ stbuf->st_atime = ts.tv_sec;
+ ts = ntfs2timespec(ni->last_mft_change_time);
+ stbuf->st_ctime = ts.tv_sec;
+ ts = ntfs2timespec(ni->last_data_change_time);
+ stbuf->st_mtime = ts.tv_sec;
+ }
+#endif
+exit:
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
+{
+ char *path;
+ ntfschar *stream_name;
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ INTX_FILE *intx_file = NULL;
+ int stream_name_len, res = 0;
+
+ /* Get inode. */
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ if (stream_name_len > 0) {
+ res = -EINVAL;
+ goto exit;
+ }
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ /*
+ * Reparse point : analyze as a junction point
+ */
+ if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+ char *target;
+ int attr_size;
+
+ errno = 0;
+ res = 0;
+ target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
+ if (target) {
+ strncpy(buf,target,buf_size);
+ free(target);
+ } else
+ if (errno == EOPNOTSUPP)
+ strcpy(buf,ntfs_bad_reparse);
+ else
+ res = -errno;
+ goto exit;
+ }
+ /* Sanity checks. */
+ if (!(ni->flags & FILE_ATTR_SYSTEM)) {
+ res = -EINVAL;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)) {
+ res = -EINVAL;
+ goto exit;
+ }
+ if ((size_t)na->data_size > sizeof(INTX_FILE_TYPES) +
+ sizeof(ntfschar) * PATH_MAX) {
+ res = -ENAMETOOLONG;
+ goto exit;
+ }
+ /* Receive file content. */
+ intx_file = ntfs_malloc(na->data_size);
+ if (!intx_file) {
+ res = -errno;
+ goto exit;
+ }
+ if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) {
+ res = -errno;
+ goto exit;
+ }
+ /* Sanity check. */
+ if (intx_file->magic != INTX_SYMBOLIC_LINK) {
+ res = -EINVAL;
+ goto exit;
+ }
+ /* Convert link from unicode to local encoding. */
+ if (ntfs_ucstombs(intx_file->target, (na->data_size -
+ offsetof(INTX_FILE, target)) / sizeof(ntfschar),
+ &buf, buf_size) < 0) {
+ res = -errno;
+ goto exit;
+ }
+exit:
+ if (intx_file)
+ free(intx_file);
+ if (na)
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
+ const ntfschar *name, const int name_len, const int name_type,
+ const s64 pos __attribute__((unused)), const MFT_REF mref,
+ const unsigned dt_type __attribute__((unused)))
+{
+ char *filename = NULL;
+ int ret = 0;
+ int filenamelen = -1;
+
+ if (name_type == FILE_NAME_DOS)
+ return 0;
+
+ if ((filenamelen = ntfs_ucstombs(name, name_len, &filename, 0)) < 0) {
+ ntfs_log_perror("Filename decoding failed (inode %llu)",
+ (unsigned long long)MREF(mref));
+ return -1;
+ }
+
+ if (ntfs_fuse_is_named_data_stream(filename)) {
+ ntfs_log_error("Unable to access '%s' (inode %llu) with "
+ "current named streams access interface.\n",
+ filename, (unsigned long long)MREF(mref));
+ free(filename);
+ return 0;
+ } else {
+ struct stat st = { .st_ino = MREF(mref) };
+
+ if (dt_type == NTFS_DT_REG)
+ st.st_mode = S_IFREG | (0777 & ~ctx->fmask);
+ else if (dt_type == NTFS_DT_DIR)
+ st.st_mode = S_IFDIR | (0777 & ~ctx->dmask);
+
+#if defined(__APPLE__) || defined(__DARWIN__)
+ /*
+ * Returning file names larger than MAXNAMLEN (255) bytes
+ * causes Darwin/Mac OS X to bug out and skip the entry.
+ */
+ if (filenamelen > MAXNAMLEN) {
+ ntfs_log_debug("Truncating %d byte filename to "
+ "%d bytes.\n", filenamelen, MAXNAMLEN);
+ ntfs_log_debug(" before: '%s'\n", filename);
+ memset(filename + MAXNAMLEN, 0, filenamelen - MAXNAMLEN);
+ ntfs_log_debug(" after: '%s'\n", filename);
+ }
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+
+ ret = fill_ctx->filler(fill_ctx->buf, filename, &st, 0);
+ }
+
+ free(filename);
+ return ret;
+}
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+
+static int ntfs_fuse_opendir(const char *path,
+ struct fuse_file_info *fi)
+{
+ int res = 0;
+ ntfs_inode *ni;
+ int accesstype;
+ struct SECURITY_CONTEXT security;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (ni) {
+ if (ntfs_fuse_fill_security_context(&security)) {
+ if (fi->flags & O_WRONLY)
+ accesstype = S_IWRITE;
+ else
+ if (fi->flags & O_RDWR)
+ accesstype = S_IWRITE | S_IREAD;
+ else
+ accesstype = S_IREAD;
+ /*
+ * directory must be searchable
+ * and requested access be allowed
+ */
+ if (!strcmp(path,"/")
+ ? !ntfs_allowed_dir_access(&security,
+ path, ni, ni, accesstype | S_IEXEC)
+ : !ntfs_allowed_dir_access(&security, path,
+ (ntfs_inode*)NULL, ni, S_IEXEC)
+ || !ntfs_allowed_access(&security,
+ ni,accesstype))
+ res = -EACCES;
+ }
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ return res;
+}
+
+#endif
+
+static int ntfs_fuse_readdir(const char *path, void *buf,
+ fuse_fill_dir_t filler, off_t offset __attribute__((unused)),
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ ntfs_fuse_fill_context_t fill_ctx;
+ ntfs_inode *ni;
+ s64 pos = 0;
+ int err = 0;
+
+ fill_ctx.filler = filler;
+ fill_ctx.buf = buf;
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+ if (ntfs_readdir(ni, &pos, &fill_ctx,
+ (ntfs_filldir_t)ntfs_fuse_filler))
+ err = -errno;
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&err);
+ return err;
+}
+
+static int ntfs_fuse_open(const char *org_path,
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct fuse_file_info *fi)
+#else
+ struct fuse_file_info *fi __attribute__((unused)))
+#endif
+{
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ int res = 0;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ int accesstype;
+ struct SECURITY_CONTEXT security;
+#endif
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (ni) {
+ na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
+ if (na) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ if (ntfs_fuse_fill_security_context(&security)) {
+ if (fi->flags & O_WRONLY)
+ accesstype = S_IWRITE;
+ else
+ if (fi->flags & O_RDWR)
+ accesstype = S_IWRITE | S_IREAD;
+ else
+ accesstype = S_IREAD;
+ /*
+ * directory must be searchable
+ * and requested access allowed
+ */
+ if (!ntfs_allowed_dir_access(&security,
+ path,(ntfs_inode*)NULL,ni,S_IEXEC)
+ || !ntfs_allowed_access(&security,
+ ni,accesstype))
+ res = -EACCES;
+ }
+#endif
+ if ((res >= 0)
+ && (fi->flags & (O_WRONLY | O_RDWR))) {
+ /* mark a future need to compress the last chunk */
+ if (na->data_flags & ATTR_COMPRESSION_MASK)
+ fi->fh |= CLOSE_COMPRESSED;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* mark a future need to fixup encrypted inode */
+ if (ctx->efs_raw
+ && !(na->data_flags & ATTR_IS_ENCRYPTED)
+ && (ni->flags & FILE_ATTR_ENCRYPTED))
+ fi->fh |= CLOSE_ENCRYPTED;
+#endif /* HAVE_SETXATTR */
+ /* mark a future need to update the mtime */
+ if (ctx->dmtime)
+ fi->fh |= CLOSE_DMTIME;
+ /* deny opening metadata files for writing */
+ if (ni->mft_no < FILE_first_user)
+ res = -EPERM;
+ }
+ ntfs_attr_close(na);
+ } else
+ res = -errno;
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+static int ntfs_fuse_read(const char *org_path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi __attribute__((unused)))
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len, res;
+ s64 total = 0;
+ s64 max_read;
+
+ if (!size)
+ return 0;
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ max_read = na->data_size;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* limit reads at next 512 byte boundary for encrypted attributes */
+ if (ctx->efs_raw
+ && max_read
+ && (na->data_flags & ATTR_IS_ENCRYPTED)
+ && NAttrNonResident(na)) {
+ max_read = ((na->data_size+511) & ~511) + 2;
+ }
+#endif /* HAVE_SETXATTR */
+ if (offset + (off_t)size > max_read) {
+ if (max_read < offset)
+ goto ok;
+ size = max_read - offset;
+ }
+ while (size > 0) {
+ s64 ret = ntfs_attr_pread(na, offset, size, buf + total);
+ if (ret != (s64)size)
+ ntfs_log_perror("ntfs_attr_pread error reading '%s' at "
+ "offset %lld: %lld <> %lld", org_path,
+ (long long)offset, (long long)size, (long long)ret);
+ if (ret <= 0 || ret > (s64)size) {
+ res = (ret < 0) ? -errno : -EIO;
+ goto exit;
+ }
+ size -= ret;
+ offset += ret;
+ total += ret;
+ }
+ok:
+ ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME);
+ res = total;
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi __attribute__((unused)))
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len, res, total = 0;
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0) {
+ res = stream_name_len;
+ goto out;
+ }
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ while (size) {
+ s64 ret = ntfs_attr_pwrite(na, offset, size, buf + total);
+ if (ret <= 0) {
+ res = -errno;
+ goto exit;
+ }
+ size -= ret;
+ offset += ret;
+ total += ret;
+ }
+ res = total;
+ if ((res > 0) && !ctx->dmtime)
+ ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ if (total)
+ set_archive(ni);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+out:
+ return res;
+}
+
+static int ntfs_fuse_release(const char *org_path,
+ struct fuse_file_info *fi)
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len, res;
+
+ /* Only for marked descriptors there is something to do */
+ if (!(fi->fh & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED | CLOSE_DMTIME))) {
+ res = 0;
+ goto out;
+ }
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0) {
+ res = stream_name_len;
+ goto out;
+ }
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ res = 0;
+ if (fi->fh & CLOSE_DMTIME)
+ ntfs_inode_update_times(na->ni,NTFS_UPDATE_MCTIME);
+ if (fi->fh & CLOSE_COMPRESSED)
+ res = ntfs_attr_pclose(na);
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ if (fi->fh & CLOSE_ENCRYPTED)
+ res = ntfs_efs_fixup_attribute(NULL, na);
+#endif /* HAVE_SETXATTR */
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+out:
+ return res;
+}
+
+/*
+ * Common part for truncate() and ftruncate()
+ */
+
+static int ntfs_fuse_trunc(const char *org_path, off_t size,
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ BOOL chkwrite)
+#else
+ BOOL chkwrite __attribute__((unused)))
+#endif
+{
+ ntfs_inode *ni = NULL;
+ ntfs_attr *na = NULL;
+ int res;
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len;
+ s64 oldsize;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ goto exit;
+ /* deny truncating metadata files */
+ if (ni->mft_no < FILE_first_user) {
+ errno = EPERM;
+ goto exit;
+ }
+
+ na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
+ if (!na)
+ goto exit;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * JPA deny truncation if cannot search in parent directory
+ * or cannot write to file (already checked for ftruncate())
+ */
+ if (ntfs_fuse_fill_security_context(&security)
+ && (!ntfs_allowed_dir_access(&security, path,
+ (ntfs_inode*)NULL, ni, S_IEXEC)
+ || (chkwrite
+ && !ntfs_allowed_access(&security, ni, S_IWRITE)))) {
+ errno = EACCES;
+ goto exit;
+ }
+#endif
+ /*
+ * For compressed files, upsizing is done by inserting a final
+ * zero, which is optimized as creating a hole when possible.
+ */
+ oldsize = na->data_size;
+ if ((na->data_flags & ATTR_COMPRESSION_MASK)
+ && (size > na->initialized_size)) {
+ char zero = 0;
+ if (ntfs_attr_pwrite(na, size - 1, 1, &zero) <= 0)
+ goto exit;
+ } else
+ if (ntfs_attr_truncate(na, size))
+ goto exit;
+ if (oldsize != size)
+ set_archive(ni);
+
+ ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+ errno = 0;
+exit:
+ res = -errno;
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+static int ntfs_fuse_truncate(const char *org_path, off_t size)
+{
+ return ntfs_fuse_trunc(org_path, size, TRUE);
+}
+
+static int ntfs_fuse_ftruncate(const char *org_path, off_t size,
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ /*
+ * in ->ftruncate() the file handle is guaranteed
+ * to have been opened for write.
+ */
+ return (ntfs_fuse_trunc(org_path, size, FALSE));
+}
+
+static int ntfs_fuse_chmod(const char *path,
+ mode_t mode)
+{
+ int res = 0;
+ ntfs_inode *ni;
+ struct SECURITY_CONTEXT security;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+
+ /* JPA return unsupported if no user mapping has been defined */
+ if (!ntfs_fuse_fill_security_context(&security)) {
+ if (ctx->silent)
+ res = 0;
+ else
+ res = -EOPNOTSUPP;
+ } else {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_allowed_dir_access(&security,path,
+ (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ res = -errno;
+ else {
+ if (ntfs_set_mode(&security,ni,mode))
+ res = -errno;
+ else
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ NInoSetDirty(ni);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -errno;
+#endif
+ }
+ return res;
+}
+
+static int ntfs_fuse_chown(const char *path, uid_t uid, gid_t gid)
+{
+ ntfs_inode *ni;
+ int res;
+ struct SECURITY_CONTEXT security;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ if (!ntfs_fuse_fill_security_context(&security)) {
+ if (ctx->silent)
+ return 0;
+ if (uid == ctx->uid && gid == ctx->gid)
+ return 0;
+ return -EOPNOTSUPP;
+ } else {
+ res = 0;
+ if (((int)uid != -1) || ((int)gid != -1)) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+
+ if (ntfs_allowed_dir_access(&security,path,
+ (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ res = -errno;
+ else {
+ if (ntfs_set_owner(&security,
+ ni,uid,gid))
+ res = -errno;
+ else
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -errno;
+#endif
+ }
+ }
+ return (res);
+}
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+
+static int ntfs_fuse_access(const char *path, int type)
+{
+ int res = 0;
+ int mode;
+ ntfs_inode *ni;
+ struct SECURITY_CONTEXT security;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+
+ /* JPA return unsupported if no user mapping has been defined */
+ if (!ntfs_fuse_fill_security_context(&security)) {
+ if (ctx->silent)
+ res = 0;
+ else
+ res = -EOPNOTSUPP;
+ } else {
+ /* parent directory must be seachable */
+ if (ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
+ (ntfs_inode*)NULL,S_IEXEC)) {
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ } else {
+ mode = 0;
+ if (type & (X_OK | W_OK | R_OK)) {
+ if (type & X_OK) mode += S_IEXEC;
+ if (type & W_OK) mode += S_IWRITE;
+ if (type & R_OK) mode += S_IREAD;
+ if (!ntfs_allowed_access(&security,
+ ni, mode))
+ res = -errno;
+ }
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ }
+ } else
+ res = -errno;
+ }
+ return (res);
+}
+
+#endif
+
+static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
+ const char *target, struct fuse_file_info *fi)
+{
+ char *name;
+ ntfschar *uname = NULL, *utarget = NULL;
+ ntfs_inode *dir_ni = NULL, *ni;
+ char *dir_path;
+ le32 securid;
+ char *path;
+ ntfschar *stream_name;
+ int stream_name_len;
+ mode_t type = typemode & ~07777;
+ mode_t perm;
+ struct SECURITY_CONTEXT security;
+ int res = 0, uname_len, utarget_len;
+
+ dir_path = strdup(org_path);
+ if (!dir_path)
+ return -errno;
+ /* Generate unicode filename. */
+ name = strrchr(dir_path, '/');
+ name++;
+ uname_len = ntfs_mbstoucs(name, &uname);
+ if ((uname_len < 0)
+ || (ctx->windows_names
+ && ntfs_forbidden_chars(uname,uname_len))) {
+ res = -errno;
+ goto exit;
+ }
+ stream_name_len = ntfs_fuse_parse_path(org_path,
+ &path, &stream_name);
+ /* stream name validity has been checked previously */
+ if (stream_name_len < 0) {
+ res = stream_name_len;
+ goto exit;
+ }
+ /* Open parent directory. */
+ *--name = 0;
+ dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, dir_path);
+ if (!dir_ni) {
+ free(path);
+ res = -errno;
+ goto exit;
+ }
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* make sure parent directory is writeable and executable */
+ if (!ntfs_fuse_fill_security_context(&security)
+ || ntfs_allowed_access(&security,
+ dir_ni,S_IWRITE + S_IEXEC)) {
+#else
+ ntfs_fuse_fill_security_context(&security);
+#endif
+ if (S_ISDIR(type))
+ perm = typemode & ~ctx->dmask & 0777;
+ else
+ perm = typemode & ~ctx->fmask & 0777;
+ /*
+ * Try to get a security id available for
+ * file creation (from inheritance or argument).
+ * This is not possible for NTFS 1.x, and we will
+ * have to build a security attribute later.
+ */
+ if (!ctx->security.mapping[MAPUSERS])
+ securid = 0;
+ else
+ if (ctx->inherit)
+ securid = ntfs_inherited_id(&security,
+ dir_ni, S_ISDIR(type));
+ else
+#if POSIXACLS
+ securid = ntfs_alloc_securid(&security,
+ security.uid, security.gid,
+ dir_ni, perm, S_ISDIR(type));
+#else
+ securid = ntfs_alloc_securid(&security,
+ security.uid, security.gid,
+ perm & ~security.umask, S_ISDIR(type));
+#endif
+ /* Create object specified in @type. */
+ switch (type) {
+ case S_IFCHR:
+ case S_IFBLK:
+ ni = ntfs_create_device(dir_ni, securid,
+ uname, uname_len, type, dev);
+ break;
+ case S_IFLNK:
+ utarget_len = ntfs_mbstoucs(target, &utarget);
+ if (utarget_len < 0) {
+ res = -errno;
+ goto exit;
+ }
+ ni = ntfs_create_symlink(dir_ni, securid,
+ uname, uname_len,
+ utarget, utarget_len);
+ break;
+ default:
+ ni = ntfs_create(dir_ni, securid, uname,
+ uname_len, type);
+ break;
+ }
+ if (ni) {
+ /*
+ * set the security attribute if a security id
+ * could not be allocated (eg NTFS 1.x)
+ */
+ if (ctx->security.mapping[MAPUSERS]) {
+#if POSIXACLS
+ if (!securid
+ && ntfs_set_inherited_posix(&security, ni,
+ security.uid, security.gid,
+ dir_ni, perm) < 0)
+ set_fuse_error(&res);
+#else
+ if (!securid
+ && ntfs_set_owner_mode(&security, ni,
+ security.uid, security.gid,
+ perm & ~security.umask) < 0)
+ set_fuse_error(&res);
+#endif
+ }
+ set_archive(ni);
+ /* mark a need to compress the end of file */
+ if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) {
+ fi->fh |= CLOSE_COMPRESSED;
+ }
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* mark a future need to fixup encrypted inode */
+ if (fi
+ && ctx->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED))
+ fi->fh |= CLOSE_ENCRYPTED;
+#endif /* HAVE_SETXATTR */
+ /* mark a need to update the mtime */
+ if (fi && ctx->dmtime)
+ fi->fh |= CLOSE_DMTIME;
+ NInoSetDirty(ni);
+ /*
+ * closing ni requires access to dir_ni to
+ * synchronize the index, avoid double opening.
+ */
+ if (ntfs_inode_close_in_dir(ni, dir_ni))
+ set_fuse_error(&res);
+ ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME);
+ } else
+ res = -errno;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -errno;
+#endif
+ free(path);
+
+exit:
+ free(uname);
+ if (ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ if (utarget)
+ free(utarget);
+ free(dir_path);
+ return res;
+}
+
+static int ntfs_fuse_create_stream(const char *path,
+ ntfschar *stream_name, const int stream_name_len,
+ struct fuse_file_info *fi)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ if (res == -ENOENT) {
+ /*
+ * If such file does not exist, create it and try once
+ * again to add stream to it.
+ * Note : no fuse_file_info for creation of main file
+ */
+ res = ntfs_fuse_create(path, S_IFREG, 0, NULL,
+ (struct fuse_file_info*)NULL);
+ if (!res)
+ return ntfs_fuse_create_stream(path,
+ stream_name, stream_name_len,fi);
+ else
+ res = -errno;
+ }
+ return res;
+ }
+ if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0))
+ res = -errno;
+ else
+ set_archive(ni);
+
+ if ((res >= 0)
+ && fi
+ && (fi->flags & (O_WRONLY | O_RDWR))) {
+ /* mark a future need to compress the last block */
+ if (ni->flags & FILE_ATTR_COMPRESSED)
+ fi->fh |= CLOSE_COMPRESSED;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ /* mark a future need to fixup encrypted inode */
+ if (ctx->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED))
+ fi->fh |= CLOSE_ENCRYPTED;
+#endif /* HAVE_SETXATTR */
+ if (ctx->dmtime)
+ fi->fh |= CLOSE_DMTIME;
+ }
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev,
+ struct fuse_file_info *fi)
+{
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len;
+ int res = 0;
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ if (stream_name_len
+ && (!S_ISREG(mode)
+ || (ctx->windows_names
+ && ntfs_forbidden_chars(stream_name,stream_name_len)))) {
+ res = -EINVAL;
+ goto exit;
+ }
+ if (!stream_name_len)
+ res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev,
+ NULL,fi);
+ else
+ res = ntfs_fuse_create_stream(path, stream_name,
+ stream_name_len,fi);
+exit:
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+static int ntfs_fuse_mknod(const char *path, mode_t mode, dev_t dev)
+{
+ return ntfs_fuse_mknod_common(path, mode, dev,
+ (struct fuse_file_info*)NULL);
+}
+
+static int ntfs_fuse_create_file(const char *path, mode_t mode,
+ struct fuse_file_info *fi)
+{
+ return ntfs_fuse_mknod_common(path, mode, 0, fi);
+}
+
+static int ntfs_fuse_symlink(const char *to, const char *from)
+{
+ if (ntfs_fuse_is_named_data_stream(from))
+ return -EINVAL; /* n/a for named data streams. */
+ return ntfs_fuse_create(from, S_IFLNK, 0, to,
+ (struct fuse_file_info*)NULL);
+}
+
+static int ntfs_fuse_link(const char *old_path, const char *new_path)
+{
+ char *name;
+ ntfschar *uname = NULL;
+ ntfs_inode *dir_ni = NULL, *ni;
+ char *path;
+ int res = 0, uname_len;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ BOOL samedir;
+ struct SECURITY_CONTEXT security;
+#endif
+
+ if (ntfs_fuse_is_named_data_stream(old_path))
+ return -EINVAL; /* n/a for named data streams. */
+ if (ntfs_fuse_is_named_data_stream(new_path))
+ return -EINVAL; /* n/a for named data streams. */
+ path = strdup(new_path);
+ if (!path)
+ return -errno;
+ /* Open file for which create hard link. */
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, old_path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+
+ /* Generate unicode filename. */
+ name = strrchr(path, '/');
+ name++;
+ uname_len = ntfs_mbstoucs(name, &uname);
+ if ((uname_len < 0)
+ || (ctx->windows_names
+ && ntfs_forbidden_chars(uname,uname_len))) {
+ res = -errno;
+ goto exit;
+ }
+ /* Open parent directory. */
+ *--name = 0;
+ dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!dir_ni) {
+ res = -errno;
+ goto exit;
+ }
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ samedir = !strncmp(old_path, path, strlen(path))
+ && (old_path[strlen(path)] == '/');
+ /* JPA make sure the parent directories are writeable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && ((!samedir && !ntfs_allowed_dir_access(&security,old_path,
+ (ntfs_inode*)NULL,ni,S_IWRITE + S_IEXEC))
+ || !ntfs_allowed_access(&security,dir_ni,S_IWRITE + S_IEXEC)))
+ res = -EACCES;
+ else
+#endif
+ {
+ if (ntfs_link(ni, dir_ni, uname, uname_len)) {
+ res = -errno;
+ goto exit;
+ }
+
+ set_archive(ni);
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME);
+ }
+exit:
+ /*
+ * Must close dir_ni first otherwise ntfs_inode_sync_file_name(ni)
+ * may fail because ni may not be in parent's index on the disk yet.
+ */
+ if (ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(uname);
+ free(path);
+ return res;
+}
+
+static int ntfs_fuse_rm(const char *org_path)
+{
+ char *name;
+ ntfschar *uname = NULL;
+ ntfs_inode *dir_ni = NULL, *ni;
+ char *path;
+ int res = 0, uname_len;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+ path = strdup(org_path);
+ if (!path)
+ return -errno;
+ /* Open object for delete. */
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni) {
+ res = -errno;
+ goto exit;
+ }
+ /* deny unlinking metadata files */
+ if (ni->mft_no < FILE_first_user) {
+ errno = EPERM;
+ res = -errno;
+ goto exit;
+ }
+
+ /* Generate unicode filename. */
+ name = strrchr(path, '/');
+ name++;
+ uname_len = ntfs_mbstoucs(name, &uname);
+ if (uname_len < 0) {
+ res = -errno;
+ goto exit;
+ }
+ /* Open parent directory. */
+ *--name = 0;
+ dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!dir_ni) {
+ res = -errno;
+ goto exit;
+ }
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* JPA deny unlinking if directory is not writable and executable */
+ if (!ntfs_fuse_fill_security_context(&security)
+ || ntfs_allowed_dir_access(&security, org_path, dir_ni, ni,
+ S_IEXEC + S_IWRITE + S_ISVTX)) {
+#endif
+ if (ntfs_delete(ctx->vol, org_path, ni, dir_ni,
+ uname, uname_len))
+ res = -errno;
+ /* ntfs_delete() always closes ni and dir_ni */
+ ni = dir_ni = NULL;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -EACCES;
+#endif
+exit:
+ if (ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ free(uname);
+ free(path);
+ return res;
+}
+
+static int ntfs_fuse_rm_stream(const char *path, ntfschar *stream_name,
+ const int stream_name_len)
+{
+ ntfs_inode *ni;
+ int res = 0;
+
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ if (ntfs_attr_remove(ni, AT_DATA, stream_name, stream_name_len))
+ res = -errno;
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+static int ntfs_fuse_unlink(const char *org_path)
+{
+ char *path = NULL;
+ ntfschar *stream_name;
+ int stream_name_len;
+ int res = 0;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+ if (!stream_name_len)
+ res = ntfs_fuse_rm(path);
+ else {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * JPA deny unlinking stream if directory is not
+ * writable and executable (debatable)
+ */
+ if (!ntfs_fuse_fill_security_context(&security)
+ || ntfs_allowed_dir_access(&security, path,
+ (ntfs_inode*)NULL, (ntfs_inode*)NULL,
+ S_IEXEC + S_IWRITE + S_ISVTX))
+ res = ntfs_fuse_rm_stream(path, stream_name,
+ stream_name_len);
+ else
+ res = -errno;
+#else
+ res = ntfs_fuse_rm_stream(path, stream_name, stream_name_len);
+#endif
+ }
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return res;
+}
+
+static int ntfs_fuse_safe_rename(const char *old_path,
+ const char *new_path,
+ const char *tmp)
+{
+ int ret;
+
+ ntfs_log_trace("Entering\n");
+
+ ret = ntfs_fuse_link(new_path, tmp);
+ if (ret)
+ return ret;
+
+ ret = ntfs_fuse_unlink(new_path);
+ if (!ret) {
+
+ ret = ntfs_fuse_link(old_path, new_path);
+ if (ret)
+ goto restore;
+
+ ret = ntfs_fuse_unlink(old_path);
+ if (ret) {
+ if (ntfs_fuse_unlink(new_path))
+ goto err;
+ goto restore;
+ }
+ }
+
+ goto cleanup;
+restore:
+ if (ntfs_fuse_link(tmp, new_path)) {
+err:
+ ntfs_log_perror("Rename failed. Existing file '%s' was renamed "
+ "to '%s'", new_path, tmp);
+ } else {
+cleanup:
+ /*
+ * Condition for this unlink has already been checked in
+ * "ntfs_fuse_rename_existing_dest()", so it should never
+ * fail (unless concurrent access to directories when fuse
+ * is multithreaded)
+ */
+ if (ntfs_fuse_unlink(tmp) < 0)
+ ntfs_log_perror("Rename failed. Existing file '%s' still present "
+ "as '%s'", new_path, tmp);
+ }
+ return ret;
+}
+
+static int ntfs_fuse_rename_existing_dest(const char *old_path, const char *new_path)
+{
+ int ret, len;
+ char *tmp;
+ const char *ext = ".ntfs-3g-";
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+ ntfs_log_trace("Entering\n");
+
+ len = strlen(new_path) + strlen(ext) + 10 + 1; /* wc(str(2^32)) + \0 */
+ tmp = ntfs_malloc(len);
+ if (!tmp)
+ return -errno;
+
+ ret = snprintf(tmp, len, "%s%s%010d", new_path, ext, ++ntfs_sequence);
+ if (ret != len - 1) {
+ ntfs_log_error("snprintf failed: %d != %d\n", ret, len - 1);
+ ret = -EOVERFLOW;
+ } else {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * Make sure existing dest can be removed.
+ * This is only needed if parent directory is
+ * sticky, because in this situation condition
+ * for unlinking is different from condition for
+ * linking
+ */
+ if (!ntfs_fuse_fill_security_context(&security)
+ || ntfs_allowed_dir_access(&security, new_path,
+ (ntfs_inode*)NULL, (ntfs_inode*)NULL,
+ S_IEXEC + S_IWRITE + S_ISVTX))
+ ret = ntfs_fuse_safe_rename(old_path, new_path, tmp);
+ else
+ ret = -EACCES;
+#else
+ ret = ntfs_fuse_safe_rename(old_path, new_path, tmp);
+#endif
+ }
+ free(tmp);
+ return ret;
+}
+
+static int ntfs_fuse_rename(const char *old_path, const char *new_path)
+{
+ int ret, stream_name_len;
+ char *path = NULL;
+ ntfschar *stream_name;
+ ntfs_inode *ni;
+ u64 inum;
+ BOOL same;
+
+ ntfs_log_debug("rename: old: '%s' new: '%s'\n", old_path, new_path);
+
+ /*
+ * FIXME: Rename should be atomic.
+ */
+ stream_name_len = ntfs_fuse_parse_path(new_path, &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (ni) {
+ ret = ntfs_check_empty_dir(ni);
+ if (ret < 0) {
+ ret = -errno;
+ ntfs_inode_close(ni);
+ goto out;
+ }
+
+ inum = ni->mft_no;
+ if (ntfs_inode_close(ni)) {
+ set_fuse_error(&ret);
+ goto out;
+ }
+
+ free(path);
+ path = (char*)NULL;
+ if (stream_name_len)
+ free(stream_name);
+
+ /* silently ignore a rename to same inode */
+ stream_name_len = ntfs_fuse_parse_path(old_path,
+ &path, &stream_name);
+ if (stream_name_len < 0)
+ return stream_name_len;
+
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (ni) {
+ same = ni->mft_no == inum;
+ if (ntfs_inode_close(ni))
+ ret = -errno;
+ else
+ if (!same)
+ ret = ntfs_fuse_rename_existing_dest(
+ old_path, new_path);
+ } else
+ ret = -errno;
+ goto out;
+ }
+
+ ret = ntfs_fuse_link(old_path, new_path);
+ if (ret)
+ goto out;
+
+ ret = ntfs_fuse_unlink(old_path);
+ if (ret)
+ ntfs_fuse_unlink(new_path);
+out:
+ free(path);
+ if (stream_name_len)
+ free(stream_name);
+ return ret;
+}
+
+static int ntfs_fuse_mkdir(const char *path,
+ mode_t mode)
+{
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL,
+ (struct fuse_file_info*)NULL);
+}
+
+static int ntfs_fuse_rmdir(const char *path)
+{
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+ return ntfs_fuse_rm(path);
+}
+
+#ifdef HAVE_UTIMENSAT
+
+static int ntfs_fuse_utimens(const char *path, const struct timespec tv[2])
+{
+ ntfs_inode *ni;
+ int res = 0;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && !ntfs_allowed_dir_access(&security,path,
+ (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
+ return (-errno);
+ }
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ /* no check or update if both UTIME_OMIT */
+ if ((tv[0].tv_nsec != UTIME_OMIT) || (tv[1].tv_nsec != UTIME_OMIT)) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ if (ntfs_allowed_as_owner(&security, ni)
+ || ((tv[0].tv_nsec == UTIME_NOW)
+ && (tv[0].tv_nsec == UTIME_NOW)
+ && ntfs_allowed_access(&security, ni, S_IWRITE))) {
+#endif
+ ntfs_time_update_flags mask = NTFS_UPDATE_CTIME;
+
+ if (tv[0].tv_nsec == UTIME_NOW)
+ mask |= NTFS_UPDATE_ATIME;
+ else
+ if (tv[0].tv_nsec != UTIME_OMIT)
+ ni->last_access_time
+ = timespec2ntfs(tv[0]);
+ if (tv[1].tv_nsec == UTIME_NOW)
+ mask |= NTFS_UPDATE_MTIME;
+ else
+ if (tv[1].tv_nsec != UTIME_OMIT)
+ ni->last_data_change_time
+ = timespec2ntfs(tv[1]);
+ ntfs_inode_update_times(ni, mask);
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ } else
+ res = -errno;
+#endif
+ }
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+#else /* HAVE_UTIMENSAT */
+
+static int ntfs_fuse_utime(const char *path, struct utimbuf *buf)
+{
+ ntfs_inode *ni;
+ int res = 0;
+ struct timespec actime;
+ struct timespec modtime;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ BOOL ownerok;
+ BOOL writeok;
+ struct SECURITY_CONTEXT security;
+#endif
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL; /* n/a for named data streams. */
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && !ntfs_allowed_dir_access(&security,path,
+ (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
+ return (-errno);
+ }
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ ownerok = ntfs_allowed_as_owner(&security, ni);
+ if (buf) {
+ /*
+ * fuse never calls with a NULL buf and we do not
+ * know whether the specific condition can be applied
+ * So we have to accept updating by a non-owner having
+ * write access.
+ */
+ writeok = !ownerok
+ && (buf->actime == buf->modtime)
+ && ntfs_allowed_access(&security, ni, S_IWRITE);
+ /* Must be owner */
+ if (!ownerok && !writeok)
+ res = (buf->actime == buf->modtime ? -EACCES : -EPERM);
+ else {
+ actime.tv_sec = buf->actime;
+ actime.tv_nsec = 0;
+ modtime.tv_sec = buf->modtime;
+ modtime.tv_nsec = 0;
+ ni->last_access_time = timespec2ntfs(actime);
+ ni->last_data_change_time = timespec2ntfs(modtime);
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ }
+ } else {
+ /* Must be owner or have write access */
+ writeok = !ownerok
+ && ntfs_allowed_access(&security, ni, S_IWRITE);
+ if (!ownerok && !writeok)
+ res = -EACCES;
+ else
+ ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);
+ }
+#else
+ if (buf) {
+ actime.tv_sec = buf->actime;
+ actime.tv_nsec = 0;
+ modtime.tv_sec = buf->modtime;
+ modtime.tv_nsec = 0;
+ ni->last_access_time = timespec2ntfs(actime);
+ ni->last_data_change_time = timespec2ntfs(modtime);
+ ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
+ } else
+ ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);
+#endif
+
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+#endif /* HAVE_UTIMENSAT */
+
+static int ntfs_fuse_fsync(const char *path __attribute__((unused)),
+ int type __attribute__((unused)),
+ struct fuse_file_info *fi __attribute__((unused)))
+{
+ int ret;
+
+ /* sync the full device */
+ ret = ntfs_device_sync(ctx->vol->dev);
+ if (ret)
+ ret = -errno;
+ return (ret);
+}
+
+static int ntfs_fuse_bmap(const char *path, size_t blocksize, uint64_t *idx)
+{
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ LCN lcn;
+ int ret = 0;
+ int cl_per_bl = ctx->vol->cluster_size / blocksize;
+
+ if (blocksize > ctx->vol->cluster_size)
+ return -EINVAL;
+
+ if (ntfs_fuse_is_named_data_stream(path))
+ return -EINVAL;
+
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ ret = -errno;
+ goto close_inode;
+ }
+
+ if ((na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED))
+ || !NAttrNonResident(na)) {
+ ret = -EINVAL;
+ goto close_attr;
+ }
+
+ if (ntfs_attr_map_whole_runlist(na)) {
+ ret = -errno;
+ goto close_attr;
+ }
+
+ lcn = ntfs_rl_vcn_to_lcn(na->rl, *idx / cl_per_bl);
+ *idx = (lcn > 0) ? lcn * cl_per_bl + *idx % cl_per_bl : 0;
+
+close_attr:
+ ntfs_attr_close(na);
+close_inode:
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&ret);
+ return ret;
+}
+
+#ifdef HAVE_SETXATTR
+
+/*
+ * Name space identifications and prefixes
+ */
+
+enum {
+ XATTRNS_NONE,
+ XATTRNS_USER,
+ XATTRNS_SYSTEM,
+ XATTRNS_SECURITY,
+ XATTRNS_TRUSTED,
+ XATTRNS_OPEN
+} ;
+
+/*
+ * Check whether access to internal data as an extended
+ * attribute in system name space is allowed
+ *
+ * Returns pointer to inode if allowed,
+ * NULL and errno set if not allowed
+ */
+
+static ntfs_inode *ntfs_check_access_xattr(struct SECURITY_CONTEXT *security,
+ const char *path, int attr, BOOL setting)
+{
+ ntfs_inode *ni;
+ BOOL foracl;
+ mode_t acctype;
+
+ ni = (ntfs_inode*)NULL;
+ if (ntfs_fuse_is_named_data_stream(path))
+ errno = EINVAL; /* n/a for named data streams. */
+ else {
+ foracl = (attr == XATTR_POSIX_ACC)
+ || (attr == XATTR_POSIX_DEF);
+ /*
+ * When accessing Posix ACL, return unsupported if ACL
+ * were disabled or no user mapping has been defined.
+ * However no error will be returned to getfacl
+ */
+ if ((!ntfs_fuse_fill_security_context(security)
+ || (ctx->secure_flags
+ & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_RAW))))
+ && foracl) {
+ errno = EOPNOTSUPP;
+ } else {
+ /*
+ * parent directory must be executable, and
+ * for setting a DOS name it must be writeable
+ */
+ if (setting && (attr == XATTR_NTFS_DOS_NAME))
+ acctype = S_IEXEC | S_IWRITE;
+ else
+ acctype = S_IEXEC;
+ if ((attr == XATTR_NTFS_DOS_NAME)
+ && !strcmp(path,"/"))
+ /* forbid getting/setting names on root */
+ errno = EPERM;
+ else
+ if (ntfs_allowed_real_dir_access(security, path,
+ (ntfs_inode*)NULL ,acctype)) {
+ ni = ntfs_pathname_to_inode(ctx->vol,
+ NULL, path);
+ }
+ }
+ }
+ return (ni);
+}
+
+/*
+ * Determine the name space of an extended attribute
+ */
+
+static int xattr_namespace(const char *name)
+{
+ int namespace;
+
+ if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) {
+ namespace = XATTRNS_NONE;
+ if (!strncmp(name, nf_ns_user_prefix,
+ nf_ns_user_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_user_prefix_len))
+ namespace = XATTRNS_USER;
+ else if (!strncmp(name, nf_ns_system_prefix,
+ nf_ns_system_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_system_prefix_len))
+ namespace = XATTRNS_SYSTEM;
+ else if (!strncmp(name, nf_ns_security_prefix,
+ nf_ns_security_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_security_prefix_len))
+ namespace = XATTRNS_SECURITY;
+ else if (!strncmp(name, nf_ns_trusted_prefix,
+ nf_ns_trusted_prefix_len)
+ && (strlen(name) != (size_t)nf_ns_trusted_prefix_len))
+ namespace = XATTRNS_TRUSTED;
+ } else
+ namespace = XATTRNS_OPEN;
+ return (namespace);
+}
+
+/*
+ * Fix the prefix of an extended attribute
+ */
+
+static int fix_xattr_prefix(const char *name, int namespace, ntfschar **lename)
+{
+ int len;
+ char *prefixed;
+
+ *lename = (ntfschar*)NULL;
+ switch (namespace) {
+ case XATTRNS_USER :
+ /*
+ * user name space : remove user prefix
+ */
+ len = ntfs_mbstoucs(name + nf_ns_user_prefix_len, lename);
+ break;
+ case XATTRNS_SYSTEM :
+ case XATTRNS_SECURITY :
+ case XATTRNS_TRUSTED :
+ /*
+ * security, trusted and unmapped system name spaces :
+ * insert ntfs-3g prefix
+ */
+ prefixed = ntfs_malloc(strlen(xattr_ntfs_3g)
+ + strlen(name) + 1);
+ if (prefixed) {
+ strcpy(prefixed,xattr_ntfs_3g);
+ strcat(prefixed,name);
+ len = ntfs_mbstoucs(prefixed, lename);
+ free(prefixed);
+ } else
+ len = -1;
+ break;
+ case XATTRNS_OPEN :
+ /*
+ * in open name space mode : do no fix prefix
+ */
+ len = ntfs_mbstoucs(name, lename);
+ break;
+ default :
+ len = -1;
+ }
+ return (len);
+}
+
+static int ntfs_fuse_listxattr(const char *path, char *list, size_t size)
+{
+ ntfs_attr_search_ctx *actx = NULL;
+ ntfs_inode *ni;
+ int ret = 0;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
+ (ntfs_inode*)NULL,S_IEXEC)) {
+ return (-errno);
+ }
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* file must be readable */
+ if (!ntfs_allowed_access(&security,ni,S_IREAD)) {
+ ret = -EACCES;
+ goto exit;
+ }
+#endif
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!actx) {
+ ret = -errno;
+ goto exit;
+ }
+
+ if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR)
+ || (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) {
+ ret = ntfs_fuse_listxattr_common(ni, actx, list, size,
+ ctx->streams == NF_STREAMS_INTERFACE_XATTR);
+ if (ret < 0)
+ goto exit;
+ }
+ if (errno != ENOENT)
+ ret = -errno;
+exit:
+ if (actx)
+ ntfs_attr_put_search_ctx(actx);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&ret);
+ return ret;
+}
+
+static int ntfs_fuse_getxattr_windows(const char *path, const char *name,
+ char *value, size_t size)
+{
+ ntfs_attr_search_ctx *actx = NULL;
+ ntfs_inode *ni;
+ char *to = value;
+ int ret = 0;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ struct SECURITY_CONTEXT security;
+#endif
+
+ if (strcmp(name, "ntfs.streams.list"))
+ return -EOPNOTSUPP;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
+ (ntfs_inode*)NULL,S_IEXEC)) {
+ return (-errno);
+ }
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ if (!ntfs_allowed_access(&security,ni,S_IREAD)) {
+ ret = -errno;
+ goto exit;
+ }
+#endif
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!actx) {
+ ret = -errno;
+ goto exit;
+ }
+ while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE,
+ 0, NULL, 0, actx)) {
+ char *tmp_name = NULL;
+ int tmp_name_len;
+
+ if (!actx->attr->name_length)
+ continue;
+ tmp_name_len = ntfs_ucstombs((ntfschar *)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->name_offset)),
+ actx->attr->name_length, &tmp_name, 0);
+ if (tmp_name_len < 0) {
+ ret = -errno;
+ goto exit;
+ }
+ if (ret)
+ ret++; /* For space delimiter. */
+ ret += tmp_name_len;
+ if (size) {
+ if ((size_t)ret <= size) {
+ /* Don't add space to the beginning of line. */
+ if (to != value) {
+ *to = '\0';
+ to++;
+ }
+ strncpy(to, tmp_name, tmp_name_len);
+ to += tmp_name_len;
+ } else {
+ free(tmp_name);
+ ret = -ERANGE;
+ goto exit;
+ }
+ }
+ free(tmp_name);
+ }
+ if (errno != ENOENT)
+ ret = -errno;
+exit:
+ if (actx)
+ ntfs_attr_put_search_ctx(actx);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&ret);
+ return ret;
+}
+
+static int ntfs_fuse_getxattr(const char *path, const char *name,
+ char *value, size_t size)
+{
+ ntfs_inode *ni;
+ ntfs_inode *dir_ni;
+ ntfs_attr *na = NULL;
+ ntfschar *lename = NULL;
+ int res, lename_len;
+ s64 rsize;
+ enum SYSTEMXATTRS attr;
+ int namespace;
+ struct SECURITY_CONTEXT security;
+
+ attr = ntfs_xattr_system_type(name,ctx->vol);
+ if (attr != XATTR_UNMAPPED) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * hijack internal data and ACL retrieval, whatever
+ * mode was selected for xattr (from the user's
+ * point of view, ACLs are not xattr)
+ */
+ ni = ntfs_check_access_xattr(&security, path, attr, FALSE);
+ if (ni) {
+ if (ntfs_allowed_access(&security,ni,S_IREAD)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = get_parent_dir(path);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_getxattr(&security,
+ attr, ni, dir_ni, value, size);
+ if (dir_ni && ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ } else {
+ res = -errno;
+ }
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#else
+ /*
+ * Only hijack NTFS ACL retrieval if POSIX ACLS
+ * option is not selected
+ * Access control is done by fuse
+ */
+ if (ntfs_fuse_is_named_data_stream(path))
+ res = -EINVAL; /* n/a for named data streams. */
+ else {
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (ni) {
+ /* user mapping not mandatory */
+ ntfs_fuse_fill_security_context(&security);
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = get_parent_dir(path);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_getxattr(&security,
+ attr, ni, dir_ni, value, size);
+ if (dir_ni && ntfs_inode_close(dir_ni))
+ set_fuse_error(&res);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ }
+#endif
+ return (res);
+ }
+ if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS)
+ return ntfs_fuse_getxattr_windows(path, name, value, size);
+ if (ctx->streams == NF_STREAMS_INTERFACE_NONE)
+ return -EOPNOTSUPP;
+ namespace = xattr_namespace(name);
+ if (namespace == XATTRNS_NONE)
+ return -EOPNOTSUPP;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
+ (ntfs_inode*)NULL,S_IEXEC)) {
+ return (-errno);
+ }
+ /* trusted only readable by root */
+ if ((namespace == XATTRNS_TRUSTED)
+ && security.uid)
+ return -EPERM;
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* file must be readable */
+ if (!ntfs_allowed_access(&security, ni, S_IREAD)) {
+ res = -errno;
+ goto exit;
+ }
+#endif
+ lename_len = fix_xattr_prefix(name, namespace, &lename);
+ if (lename_len == -1) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
+ if (!na) {
+ res = -ENODATA;
+ goto exit;
+ }
+ rsize = na->data_size;
+ if (ctx->efs_raw
+ && rsize
+ && (na->data_flags & ATTR_IS_ENCRYPTED)
+ && NAttrNonResident(na))
+ rsize = ((na->data_size + 511) & ~511) + 2;
+ if (size) {
+ if (size >= (size_t)rsize) {
+ res = ntfs_attr_pread(na, 0, rsize, value);
+ if (res != rsize)
+ res = -errno;
+ } else
+ res = -ERANGE;
+ } else
+ res = rsize;
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ free(lename);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+static int ntfs_fuse_setxattr(const char *path, const char *name,
+ const char *value, size_t size, int flags)
+{
+ ntfs_inode *ni;
+ ntfs_inode *dir_ni;
+ ntfs_attr *na = NULL;
+ ntfschar *lename = NULL;
+ int res, lename_len;
+ size_t total;
+ s64 part;
+ enum SYSTEMXATTRS attr;
+ int namespace;
+ struct SECURITY_CONTEXT security;
+
+ attr = ntfs_xattr_system_type(name,ctx->vol);
+ if (attr != XATTR_UNMAPPED) {
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * hijack internal data and ACL setting, whatever
+ * mode was selected for xattr (from the user's
+ * point of view, ACLs are not xattr)
+ * Note : updating an ACL does not set ctime
+ */
+ ni = ntfs_check_access_xattr(&security,path,attr,TRUE);
+ if (ni) {
+ if (ntfs_allowed_as_owner(&security,ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = get_parent_dir(path);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_setxattr(&security,
+ attr, ni, dir_ni, value, size, flags);
+ /* never have to close dir_ni */
+ if (res)
+ res = -errno;
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#else
+ /*
+ * Only hijack NTFS ACL setting if POSIX ACLS
+ * option is not selected
+ * Access control is partially done by fuse
+ */
+ if (ntfs_fuse_is_named_data_stream(path))
+ res = -EINVAL; /* n/a for named data streams. */
+ else {
+ /* creation of a new name is not controlled by fuse */
+ if (attr == XATTR_NTFS_DOS_NAME)
+ ni = ntfs_check_access_xattr(&security,path,attr,TRUE);
+ else
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (ni) {
+ /*
+ * user mapping is not mandatory
+ * if defined, only owner is allowed
+ */
+ if (!ntfs_fuse_fill_security_context(&security)
+ || ntfs_allowed_as_owner(&security,ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = get_parent_dir(path);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_setxattr(&security,
+ attr, ni, dir_ni, value,
+ size, flags);
+ /* never have to close dir_ni */
+ if (res)
+ res = -errno;
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+ }
+#endif
+ return (res);
+ }
+ if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
+ && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR))
+ return -EOPNOTSUPP;
+ namespace = xattr_namespace(name);
+ if (namespace == XATTRNS_NONE)
+ return -EOPNOTSUPP;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
+ (ntfs_inode*)NULL,S_IEXEC)) {
+ return (-errno);
+ }
+ /* security and trusted only settable by root */
+ if (((namespace == XATTRNS_SECURITY)
+ || (namespace == XATTRNS_TRUSTED))
+ && security.uid)
+ return -EPERM;
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ switch (namespace) {
+ case XATTRNS_SECURITY :
+ case XATTRNS_TRUSTED :
+ if (security.uid) {
+ res = -EPERM;
+ goto exit;
+ }
+ break;
+ case XATTRNS_SYSTEM :
+ if (!ntfs_allowed_as_owner(&security,ni)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ default :
+ if (!ntfs_allowed_access(&security,ni,S_IWRITE)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ }
+#endif
+ lename_len = fix_xattr_prefix(name, namespace, &lename);
+ if ((lename_len == -1)
+ || (ctx->windows_names
+ && ntfs_forbidden_chars(lename,lename_len))) {
+ res = -errno;
+ goto exit;
+ }
+ na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
+ if (na && flags == XATTR_CREATE) {
+ res = -EEXIST;
+ goto exit;
+ }
+ if (!na) {
+ if (flags == XATTR_REPLACE) {
+ res = -ENODATA;
+ goto exit;
+ }
+ if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) {
+ res = -errno;
+ goto exit;
+ }
+ if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
+ set_archive(ni);
+ NInoFileNameSetDirty(ni);
+ }
+ na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
+ if (!na) {
+ res = -errno;
+ goto exit;
+ }
+ } else {
+ /* currently compressed streams can only be wiped out */
+ if (ntfs_attr_truncate(na, (s64)0 /* size */)) {
+ res = -errno;
+ goto exit;
+ }
+ }
+ total = 0;
+ res = 0;
+ if (size) {
+ do {
+ part = ntfs_attr_pwrite(na, total, size - total,
+ &value[total]);
+ if (part > 0)
+ total += part;
+ } while ((part > 0) && (total < size));
+ }
+ if ((total != size) || ntfs_attr_pclose(na))
+ res = -errno;
+ else {
+ if (ctx->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED)) {
+ if (ntfs_efs_fixup_attribute(NULL,na))
+ res = -errno;
+ }
+ }
+ if (!res && !(ni->flags & FILE_ATTR_ARCHIVE)) {
+ set_archive(ni);
+ NInoFileNameSetDirty(ni);
+ }
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ free(lename);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+static int ntfs_fuse_removexattr(const char *path, const char *name)
+{
+ ntfs_inode *ni;
+ ntfs_inode *dir_ni;
+ ntfschar *lename = NULL;
+ int res = 0, lename_len;
+ enum SYSTEMXATTRS attr;
+ int namespace;
+ struct SECURITY_CONTEXT security;
+
+ attr = ntfs_xattr_system_type(name,ctx->vol);
+ if (attr != XATTR_UNMAPPED) {
+ switch (attr) {
+ /*
+ * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES
+ * is never allowed
+ */
+ case XATTR_NTFS_ACL :
+ case XATTR_NTFS_ATTRIB :
+ case XATTR_NTFS_ATTRIB_BE :
+ case XATTR_NTFS_EFSINFO :
+ case XATTR_NTFS_TIMES :
+ case XATTR_NTFS_TIMES_BE :
+ case XATTR_NTFS_CRTIME :
+ case XATTR_NTFS_CRTIME_BE :
+ res = -EPERM;
+ break;
+ default :
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /*
+ * hijack internal data and ACL removal, whatever
+ * mode was selected for xattr (from the user's
+ * point of view, ACLs are not xattr)
+ * Note : updating an ACL does not set ctime
+ */
+ ni = ntfs_check_access_xattr(&security,path,attr,TRUE);
+ if (ni) {
+ if (ntfs_allowed_as_owner(&security,ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = get_parent_dir(path);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_removexattr(&security,
+ attr, ni, dir_ni);
+ /* never have to close dir_ni */
+ if (res)
+ res = -errno;
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#else
+ /*
+ * Only hijack NTFS ACL setting if POSIX ACLS
+ * option is not selected
+ * Access control is partially done by fuse
+ */
+ /* creation of a new name is not controlled by fuse */
+ if (attr == XATTR_NTFS_DOS_NAME)
+ ni = ntfs_check_access_xattr(&security,
+ path, attr, TRUE);
+ else {
+ if (ntfs_fuse_is_named_data_stream(path)) {
+ ni = (ntfs_inode*)NULL;
+ errno = EINVAL; /* n/a for named data streams. */
+ } else
+ ni = ntfs_pathname_to_inode(ctx->vol,
+ NULL, path);
+ }
+ if (ni) {
+ /*
+ * user mapping is not mandatory
+ * if defined, only owner is allowed
+ */
+ if (!ntfs_fuse_fill_security_context(&security)
+ || ntfs_allowed_as_owner(&security,ni)) {
+ if (attr == XATTR_NTFS_DOS_NAME)
+ dir_ni = get_parent_dir(path);
+ else
+ dir_ni = (ntfs_inode*)NULL;
+ res = ntfs_xattr_system_removexattr(&security,
+ attr, ni, dir_ni);
+ /* never have to close dir_ni */
+ if (res)
+ res = -errno;
+ } else
+ res = -errno;
+ if ((attr != XATTR_NTFS_DOS_NAME)
+ && ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ } else
+ res = -errno;
+#endif
+ break;
+ }
+ return (res);
+ }
+ if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
+ && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR))
+ return -EOPNOTSUPP;
+ namespace = xattr_namespace(name);
+ if (namespace == XATTRNS_NONE)
+ return -EOPNOTSUPP;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ /* parent directory must be executable */
+ if (ntfs_fuse_fill_security_context(&security)
+ && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
+ (ntfs_inode*)NULL,S_IEXEC)) {
+ return (-errno);
+ }
+ /* security and trusted only settable by root */
+ if (((namespace == XATTRNS_SECURITY)
+ || (namespace == XATTRNS_TRUSTED))
+ && security.uid)
+ return -EACCES;
+#endif
+ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
+ if (!ni)
+ return -errno;
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ switch (namespace) {
+ case XATTRNS_SECURITY :
+ case XATTRNS_TRUSTED :
+ if (security.uid) {
+ res = -EPERM;
+ goto exit;
+ }
+ break;
+ case XATTRNS_SYSTEM :
+ if (!ntfs_allowed_as_owner(&security,ni)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ default :
+ if (!ntfs_allowed_access(&security,ni,S_IWRITE)) {
+ res = -EACCES;
+ goto exit;
+ }
+ break;
+ }
+#endif
+ lename_len = fix_xattr_prefix(name, namespace, &lename);
+ if (lename_len == -1) {
+ res = -errno;
+ goto exit;
+ }
+ if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) {
+ if (errno == ENOENT)
+ errno = ENODATA;
+ res = -errno;
+ }
+ if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
+ set_archive(ni);
+ NInoFileNameSetDirty(ni);
+ }
+exit:
+ free(lename);
+ if (ntfs_inode_close(ni))
+ set_fuse_error(&res);
+ return res;
+}
+
+#else
+#if POSIXACLS
+#error "Option inconsistency : POSIXACLS requires SETXATTR"
+#endif
+#endif /* HAVE_SETXATTR */
+
+static void ntfs_close(void)
+{
+ struct SECURITY_CONTEXT security;
+
+ if (!ctx)
+ return;
+
+ if (!ctx->vol)
+ return;
+
+ if (ctx->mounted) {
+ ntfs_log_info("Unmounting %s (%s)\n", opts.device,
+ ctx->vol->vol_name);
+ if (ntfs_fuse_fill_security_context(&security)) {
+ if (ctx->seccache && ctx->seccache->head.p_reads) {
+ ntfs_log_info("Permissions cache : %lu writes, "
+ "%lu reads, %lu.%1lu%% hits\n",
+ ctx->seccache->head.p_writes,
+ ctx->seccache->head.p_reads,
+ 100 * ctx->seccache->head.p_hits
+ / ctx->seccache->head.p_reads,
+ 1000 * ctx->seccache->head.p_hits
+ / ctx->seccache->head.p_reads % 10);
+ }
+ }
+ ntfs_close_secure(&security);
+ }
+
+ if (ntfs_umount(ctx->vol, FALSE))
+ ntfs_log_perror("Failed to close volume %s", opts.device);
+
+ ctx->vol = NULL;
+}
+
+static void ntfs_fuse_destroy2(void *unused __attribute__((unused)))
+{
+ ntfs_close();
+}
+
+static struct fuse_operations ntfs_3g_ops = {
+#if defined(HAVE_UTIMENSAT) && (defined(FUSE_INTERNAL) || (FUSE_VERSION > 28))
+ /*
+ * Accept UTIME_NOW and UTIME_OMIT in utimens, when
+ * using internal fuse or a fuse version since 2.9
+ * (this field is not present in older versions)
+ */
+ .flag_utime_omit_ok = 1,
+#endif
+ .getattr = ntfs_fuse_getattr,
+ .readlink = ntfs_fuse_readlink,
+ .readdir = ntfs_fuse_readdir,
+ .open = ntfs_fuse_open,
+ .release = ntfs_fuse_release,
+ .read = ntfs_fuse_read,
+ .write = ntfs_fuse_write,
+ .truncate = ntfs_fuse_truncate,
+ .ftruncate = ntfs_fuse_ftruncate,
+ .statfs = ntfs_fuse_statfs,
+ .chmod = ntfs_fuse_chmod,
+ .chown = ntfs_fuse_chown,
+ .create = ntfs_fuse_create_file,
+ .mknod = ntfs_fuse_mknod,
+ .symlink = ntfs_fuse_symlink,
+ .link = ntfs_fuse_link,
+ .unlink = ntfs_fuse_unlink,
+ .rename = ntfs_fuse_rename,
+ .mkdir = ntfs_fuse_mkdir,
+ .rmdir = ntfs_fuse_rmdir,
+#ifdef HAVE_UTIMENSAT
+ .utimens = ntfs_fuse_utimens,
+#else
+ .utime = ntfs_fuse_utime,
+#endif
+ .fsync = ntfs_fuse_fsync,
+ .fsyncdir = ntfs_fuse_fsync,
+ .bmap = ntfs_fuse_bmap,
+ .destroy = ntfs_fuse_destroy2,
+#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
+ .access = ntfs_fuse_access,
+ .opendir = ntfs_fuse_opendir,
+#endif
+#ifdef HAVE_SETXATTR
+ .getxattr = ntfs_fuse_getxattr,
+ .setxattr = ntfs_fuse_setxattr,
+ .removexattr = ntfs_fuse_removexattr,
+ .listxattr = ntfs_fuse_listxattr,
+#endif /* HAVE_SETXATTR */
+#if defined(__APPLE__) || defined(__DARWIN__)
+ /* MacFUSE extensions. */
+ .getxtimes = ntfs_macfuse_getxtimes,
+ .setcrtime = ntfs_macfuse_setcrtime,
+ .setbkuptime = ntfs_macfuse_setbkuptime,
+ .setchgtime = ntfs_macfuse_setchgtime,
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+#if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \
+ || (defined(__APPLE__) || defined(__DARWIN__))
+ .init = ntfs_init
+#endif
+};
+
+static int ntfs_fuse_init(void)
+{
+ ctx = ntfs_calloc(sizeof(ntfs_fuse_context_t));
+ if (!ctx)
+ return -1;
+
+ *ctx = (ntfs_fuse_context_t) {
+ .uid = getuid(),
+ .gid = getgid(),
+#if defined(linux)
+ .streams = NF_STREAMS_INTERFACE_XATTR,
+#else
+ .streams = NF_STREAMS_INTERFACE_NONE,
+#endif
+ .atime = ATIME_RELATIVE,
+ .silent = TRUE,
+ .recover = TRUE
+ };
+ return 0;
+}
+
+static int ntfs_open(const char *device)
+{
+ unsigned long flags = 0;
+
+ if (!ctx->blkdev)
+ flags |= MS_EXCLUSIVE;
+ if (ctx->ro)
+ flags |= MS_RDONLY;
+ if (ctx->recover)
+ flags |= MS_RECOVER;
+ if (ctx->hiberfile)
+ flags |= MS_IGNORE_HIBERFILE;
+
+ ctx->vol = ntfs_mount(device, flags);
+ if (!ctx->vol) {
+ ntfs_log_perror("Failed to mount '%s'", device);
+ goto err_out;
+ }
+ if (ctx->sync && ctx->vol->dev)
+ NDevSetSync(ctx->vol->dev);
+ if (ctx->compression)
+ NVolSetCompression(ctx->vol);
+ else
+ NVolClearCompression(ctx->vol);
+#ifdef HAVE_SETXATTR
+ /* archivers must see hidden files */
+ if (ctx->efs_raw)
+ ctx->hide_hid_files = FALSE;
+#endif
+ if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files,
+ !ctx->hide_hid_files, ctx->hide_dot_files))
+ goto err_out;
+
+ ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na);
+ if (ctx->vol->free_clusters < 0) {
+ ntfs_log_perror("Failed to read NTFS $Bitmap");
+ goto err_out;
+ }
+
+ ctx->vol->free_mft_records = ntfs_get_nr_free_mft_records(ctx->vol);
+ if (ctx->vol->free_mft_records < 0) {
+ ntfs_log_perror("Failed to calculate free MFT records");
+ goto err_out;
+ }
+
+ if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) {
+ if (errno != EPERM)
+ goto err_out;
+ if (ntfs_fuse_rm("/hiberfil.sys"))
+ goto err_out;
+ }
+
+ errno = 0;
+err_out:
+ return ntfs_volume_error(errno);
+
+}
+
+static void usage(void)
+{
+ ntfs_log_info(usage_msg, EXEC_NAME, VERSION, FUSE_TYPE, fuse_version(),
+ 4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING,
+ EXEC_NAME, ntfs_home);
+}
+
+#if defined(linux) || defined(__uClinux__)
+
+static const char *dev_fuse_msg =
+"HINT: You should be root, or make ntfs-3g setuid root, or load the FUSE\n"
+" kernel module as root ('modprobe fuse' or 'insmod <path_to>/fuse.ko'"
+" or insmod <path_to>/fuse.o'). Make also sure that the fuse device"
+" exists. It's usually either /dev/fuse or /dev/misc/fuse.";
+
+static const char *fuse26_kmod_msg =
+"WARNING: Deficient Linux kernel detected. Some driver features are\n"
+" not available (swap file on NTFS, boot from NTFS by LILO), and\n"
+" unmount is not safe unless it's made sure the ntfs-3g process\n"
+" naturally terminates after calling 'umount'. If you wish this\n"
+" message to disappear then you should upgrade to at least kernel\n"
+" version 2.6.20, or request help from your distribution to fix\n"
+" the kernel problem. The below web page has more information:\n"
+" http://tuxera.com/community/ntfs-3g-faq/#fuse26\n"
+"\n";
+
+static void mknod_dev_fuse(const char *dev)
+{
+ struct stat st;
+
+ if (stat(dev, &st) && (errno == ENOENT)) {
+ mode_t mask = umask(0);
+ if (mknod(dev, S_IFCHR | 0666, makedev(10, 229))) {
+ ntfs_log_perror("Failed to create '%s'", dev);
+ if (errno == EPERM)
+ ntfs_log_error("%s", dev_fuse_msg);
+ }
+ umask(mask);
+ }
+}
+
+static void create_dev_fuse(void)
+{
+ mknod_dev_fuse("/dev/fuse");
+
+#ifdef __UCLIBC__
+ {
+ struct stat st;
+ /* The fuse device is under /dev/misc using devfs. */
+ if (stat("/dev/misc", &st) && (errno == ENOENT)) {
+ mode_t mask = umask(0);
+ mkdir("/dev/misc", 0775);
+ umask(mask);
+ }
+ mknod_dev_fuse("/dev/misc/fuse");
+ }
+#endif
+}
+
+static fuse_fstype get_fuse_fstype(void)
+{
+ char buf[256];
+ fuse_fstype fstype = FSTYPE_NONE;
+
+ FILE *f = fopen("/proc/filesystems", "r");
+ if (!f) {
+ ntfs_log_perror("Failed to open /proc/filesystems");
+ return FSTYPE_UNKNOWN;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (strstr(buf, "fuseblk\n")) {
+ fstype = FSTYPE_FUSEBLK;
+ break;
+ }
+ if (strstr(buf, "fuse\n"))
+ fstype = FSTYPE_FUSE;
+ }
+
+ fclose(f);
+ return fstype;
+}
+
+static fuse_fstype load_fuse_module(void)
+{
+ int i;
+ struct stat st;
+ pid_t pid;
+ const char *cmd = "/sbin/modprobe";
+ struct timespec req = { 0, 100000000 }; /* 100 msec */
+ fuse_fstype fstype;
+
+ if (!stat(cmd, &st) && !geteuid()) {
+ pid = fork();
+ if (!pid) {
+ execl(cmd, cmd, "fuse", NULL);
+ _exit(1);
+ } else if (pid != -1)
+ waitpid(pid, NULL, 0);
+ }
+
+ for (i = 0; i < 10; i++) {
+ /*
+ * We sleep first because despite the detection of the loaded
+ * FUSE kernel module, fuse_mount() can still fail if it's not
+ * fully functional/initialized. Note, of course this is still
+ * unreliable but usually helps.
+ */
+ nanosleep(&req, NULL);
+ fstype = get_fuse_fstype();
+ if (fstype != FSTYPE_NONE)
+ break;
+ }
+ return fstype;
+}
+
+#endif
+
+static struct fuse_chan *try_fuse_mount(char *parsed_options)
+{
+ struct fuse_chan *fc = NULL;
+ struct fuse_args margs = FUSE_ARGS_INIT(0, NULL);
+
+ /* The fuse_mount() options get modified, so we always rebuild it */
+ if ((fuse_opt_add_arg(&margs, EXEC_NAME) == -1 ||
+ fuse_opt_add_arg(&margs, "-o") == -1 ||
+ fuse_opt_add_arg(&margs, parsed_options) == -1)) {
+ ntfs_log_error("Failed to set FUSE options.\n");
+ goto free_args;
+ }
+
+ fc = fuse_mount(opts.mnt_point, &margs);
+free_args:
+ fuse_opt_free_args(&margs);
+ return fc;
+
+}
+
+static int set_fuseblk_options(char **parsed_options)
+{
+ char options[64];
+ long pagesize;
+ u32 blksize = ctx->vol->cluster_size;
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize < 1)
+ pagesize = 4096;
+
+ if (blksize > (u32)pagesize)
+ blksize = pagesize;
+
+ snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize);
+ if (ntfs_strappend(parsed_options, options))
+ return -1;
+ return 0;
+}
+
+static struct fuse *mount_fuse(char *parsed_options)
+{
+ struct fuse *fh = NULL;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+ ctx->fc = try_fuse_mount(parsed_options);
+ if (!ctx->fc)
+ return NULL;
+
+ if (fuse_opt_add_arg(&args, "") == -1)
+ goto err;
+#if !CACHEING
+ if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=0") == -1)
+ goto err;
+#else
+ if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=1") == -1)
+ goto err;
+#endif
+ if (ctx->debug)
+ if (fuse_opt_add_arg(&args, "-odebug") == -1)
+ goto err;
+
+ fh = fuse_new(ctx->fc, &args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL);
+ if (!fh)
+ goto err;
+
+ if (fuse_set_signal_handlers(fuse_get_session(fh)))
+ goto err_destory;
+out:
+ fuse_opt_free_args(&args);
+ return fh;
+err_destory:
+ fuse_destroy(fh);
+ fh = NULL;
+err:
+ fuse_unmount(opts.mnt_point, ctx->fc);
+ goto out;
+}
+
+static void setup_logging(char *parsed_options)
+{
+ if (!ctx->no_detach) {
+ if (daemon(0, ctx->debug))
+ ntfs_log_error("Failed to daemonize.\n");
+ else if (!ctx->debug) {
+#ifndef DEBUG
+ ntfs_log_set_handler(ntfs_log_handler_syslog);
+ /* Override default libntfs identify. */
+ openlog(EXEC_NAME, LOG_PID, LOG_DAEMON);
+#endif
+ }
+ }
+
+ ctx->seccache = (struct PERMISSIONS_CACHE*)NULL;
+
+ ntfs_log_info("Version %s %s %d\n", VERSION, FUSE_TYPE, fuse_version());
+ if (strcmp(opts.arg_device,opts.device))
+ ntfs_log_info("Requested device %s canonicalized as %s\n",
+ opts.arg_device,opts.device);
+ ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n",
+ opts.device, (ctx->ro) ? "Read-Only" : "Read-Write",
+ ctx->vol->vol_name, ctx->vol->major_ver,
+ ctx->vol->minor_ver);
+ ntfs_log_info("Cmdline options: %s\n", opts.options ? opts.options : "");
+ ntfs_log_info("Mount options: %s\n", parsed_options);
+}
+
+int main(int argc, char *argv[])
+{
+ char *parsed_options = NULL;
+ struct fuse *fh;
+#if !(defined(__sun) && defined (__SVR4))
+ fuse_fstype fstype = FSTYPE_UNKNOWN;
+#endif
+ const char *permissions_mode = (const char*)NULL;
+ const char *failed_secure = (const char*)NULL;
+#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
+ struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL;
+#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
+ struct stat sbuf;
+ unsigned long existing_mount;
+ int err, fd;
+
+ /*
+ * Make sure file descriptors 0, 1 and 2 are open,
+ * otherwise chaos would ensue.
+ */
+ do {
+ fd = open("/dev/null", O_RDWR);
+ if (fd > 2)
+ close(fd);
+ } while (fd >= 0 && fd <= 2);
+
+#ifndef FUSE_INTERNAL
+ if ((getuid() != geteuid()) || (getgid() != getegid())) {
+ fprintf(stderr, "%s", setuid_msg);
+ return NTFS_VOLUME_INSECURE;
+ }
+#endif
+ if (drop_privs())
+ return NTFS_VOLUME_NO_PRIVILEGE;
+
+ ntfs_set_locale();
+ ntfs_log_set_handler(ntfs_log_handler_stderr);
+
+ if (ntfs_parse_options(&opts, usage, argc, argv)) {
+ usage();
+ return NTFS_VOLUME_SYNTAX_ERROR;
+ }
+
+ if (ntfs_fuse_init()) {
+ err = NTFS_VOLUME_OUT_OF_MEMORY;
+ goto err2;
+ }
+
+ parsed_options = parse_mount_options(ctx, &opts, FALSE);
+ if (!parsed_options) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+ if (!ntfs_check_if_mounted(opts.device,&existing_mount)
+ && (existing_mount & NTFS_MF_MOUNTED)) {
+ err = NTFS_VOLUME_LOCKED;
+ goto err_out;
+ }
+
+ /* need absolute mount point for junctions */
+ if (opts.mnt_point[0] == '/')
+ ctx->abs_mnt_point = strdup(opts.mnt_point);
+ else {
+ ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX);
+ if (ctx->abs_mnt_point) {
+ if (getcwd(ctx->abs_mnt_point,
+ PATH_MAX - strlen(opts.mnt_point) - 1)) {
+ strcat(ctx->abs_mnt_point, "/");
+ strcat(ctx->abs_mnt_point, opts.mnt_point);
+ }
+ }
+ }
+ if (!ctx->abs_mnt_point) {
+ err = NTFS_VOLUME_OUT_OF_MEMORY;
+ goto err_out;
+ }
+
+ ctx->security.uid = 0;
+ ctx->security.gid = 0;
+ if ((opts.mnt_point[0] == '/')
+ && !stat(opts.mnt_point,&sbuf)) {
+ /* collect owner of mount point, useful for default mapping */
+ ctx->security.uid = sbuf.st_uid;
+ ctx->security.gid = sbuf.st_gid;
+ }
+
+#if defined(linux) || defined(__uClinux__)
+ fstype = get_fuse_fstype();
+
+ err = NTFS_VOLUME_NO_PRIVILEGE;
+ if (restore_privs())
+ goto err_out;
+
+ if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN)
+ fstype = load_fuse_module();
+ create_dev_fuse();
+
+ if (drop_privs())
+ goto err_out;
+#endif
+ if (stat(opts.device, &sbuf)) {
+ ntfs_log_perror("Failed to access '%s'", opts.device);
+ err = NTFS_VOLUME_NO_PRIVILEGE;
+ goto err_out;
+ }
+
+#if !(defined(__sun) && defined (__SVR4))
+ /* Always use fuseblk for block devices unless it's surely missing. */
+ if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE))
+ ctx->blkdev = TRUE;
+#endif
+
+#ifndef FUSE_INTERNAL
+ if (getuid() && ctx->blkdev) {
+ ntfs_log_error("%s", unpriv_fuseblk_msg);
+ err = NTFS_VOLUME_NO_PRIVILEGE;
+ goto err2;
+ }
+#endif
+ err = ntfs_open(opts.device);
+ if (err)
+ goto err_out;
+
+ /* We must do this after ntfs_open() to be able to set the blksize */
+ if (ctx->blkdev && set_fuseblk_options(&parsed_options))
+ goto err_out;
+
+ ctx->security.vol = ctx->vol;
+ ctx->vol->secure_flags = ctx->secure_flags;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ ctx->vol->efs_raw = ctx->efs_raw;
+#endif /* HAVE_SETXATTR */
+ /* JPA open $Secure, (whatever NTFS version !) */
+ /* to initialize security data */
+ if (ntfs_open_secure(ctx->vol) && (ctx->vol->major_ver >= 3))
+ failed_secure = "Could not open file $Secure";
+ if (!ntfs_build_mapping(&ctx->security,ctx->usermap_path,
+ (ctx->vol->secure_flags
+ & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))
+ && !(ctx->vol->secure_flags & (1 << SECURITY_WANTED)))) {
+#if POSIXACLS
+ /* use basic permissions if requested */
+ if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))
+ permissions_mode = "User mapping built, Posix ACLs not used";
+ else {
+ permissions_mode = "User mapping built, Posix ACLs in use";
+#if KERNELACLS
+ if (ntfs_strappend(&parsed_options, ",default_permissions,acl")) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+#endif /* KERNELACLS */
+ }
+#else /* POSIXACLS */
+#if KERNELPERMS
+ if (!(ctx->vol->secure_flags
+ & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) {
+ /*
+ * No explicit option but user mapping found
+ * force default security
+ */
+ ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT);
+ if (ntfs_strappend(&parsed_options, ",default_permissions")) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+ }
+#endif /* KERNELPERMS */
+ permissions_mode = "User mapping built";
+#endif /* POSIXACLS */
+ } else {
+ ctx->security.uid = ctx->uid;
+ ctx->security.gid = ctx->gid;
+ /* same ownership/permissions for all files */
+ ctx->security.mapping[MAPUSERS] = (struct MAPPING*)NULL;
+ ctx->security.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
+ if ((ctx->vol->secure_flags & (1 << SECURITY_WANTED))
+ && !(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) {
+ ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT);
+ if (ntfs_strappend(&parsed_options, ",default_permissions")) {
+ err = NTFS_VOLUME_SYNTAX_ERROR;
+ goto err_out;
+ }
+ }
+ if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) {
+ ctx->vol->secure_flags |= (1 << SECURITY_RAW);
+ permissions_mode = "Global ownership and permissions enforced";
+ } else {
+ ctx->vol->secure_flags &= ~(1 << SECURITY_RAW);
+ permissions_mode = "Ownership and permissions disabled";
+ }
+ }
+ if (ctx->usermap_path)
+ free (ctx->usermap_path);
+
+#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
+ xattr_mapping = ntfs_xattr_build_mapping(ctx->vol,
+ ctx->xattrmap_path);
+ ctx->vol->xattr_mapping = xattr_mapping;
+ /*
+ * Errors are logged, do not refuse mounting, it would be
+ * too difficult to fix the unmountable mapping file.
+ */
+ if (ctx->xattrmap_path)
+ free(ctx->xattrmap_path);
+#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
+
+ fh = mount_fuse(parsed_options);
+ if (!fh) {
+ err = NTFS_VOLUME_FUSE_ERROR;
+ goto err_out;
+ }
+
+ ctx->mounted = TRUE;
+
+#if defined(linux) || defined(__uClinux__)
+ if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE))
+ ntfs_log_info("%s", fuse26_kmod_msg);
+#endif
+ setup_logging(parsed_options);
+ if (failed_secure)
+ ntfs_log_info("%s\n",failed_secure);
+ if (permissions_mode)
+ ntfs_log_info("%s, configuration type %d\n",permissions_mode,
+ 4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING);
+ if ((ctx->vol->secure_flags & (1 << SECURITY_RAW))
+ && !ctx->uid && ctx->gid)
+ ntfs_log_error("Warning : using problematic uid==0 and gid!=0\n");
+
+ fuse_loop(fh);
+
+ err = 0;
+
+ fuse_unmount(opts.mnt_point, ctx->fc);
+ fuse_destroy(fh);
+err_out:
+ ntfs_mount_error(opts.device, opts.mnt_point, err);
+ if (ctx->abs_mnt_point)
+ free(ctx->abs_mnt_point);
+#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
+ ntfs_xattr_free_mapping(xattr_mapping);
+#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
+err2:
+ ntfs_close();
+ free(ctx);
+ free(parsed_options);
+ free(opts.options);
+ free(opts.device);
+ return err;
+}
diff --git a/src/ntfs-3g.probe.8.in b/src/ntfs-3g.probe.8.in
new file mode 100644
index 0000000..62ce57e
--- /dev/null
+++ b/src/ntfs-3g.probe.8.in
@@ -0,0 +1,81 @@
+.\" Copyright (c) 2008 Szabolcs Szakacsits.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH NTFS-3G.PROBE 8 "January 2008" "ntfs-3g.probe @VERSION@"
+.SH NAME
+ntfs-3g.probe \- Probe an NTFS volume mountability
+.SH SYNOPSIS
+.B ntfs-3g.probe
+.I <\-\-readonly|\-\-readwrite>
+.I volume
+.br
+.SH DESCRIPTION
+The \fBntfs-3g.probe\fR utility tests a volume if it's NTFS mountable
+read-only or read-write, and exits with a status value accordingly.
+The \fIvolume\fR can be a block device or image file.
+.SH OPTIONS
+Below is a summary of the options that \fBntfs-3g.probe\fR accepts.
+.TP
+.B \-r, \-\-readonly
+Test if the volume can be mounted read-only.
+.TP
+.B \-w, \-\-readwrite
+Test if the volume can be mounted read-write.
+.TP
+.B \-h, \-\-help
+Display help and exit.
+.SH EXAMPLE
+Test if /dev/sda1 can be mounted read-write:
+.RS
+.sp
+.B ntfs-3g.probe --readwrite /dev/sda1
+.sp
+.RE
+.SH EXIT CODES
+The exit codes are as follows:
+.IP 0
+Volume is mountable.
+.IP 11
+Syntax error, command line parsing failed.
+.IP 12
+The volume doesn't have a valid NTFS.
+.IP 13
+Inconsistent NTFS, hardware or device driver fault, or unsetup
+SoftRAID/FakeRAID hardware.
+.IP 14
+The NTFS partition is hibernated.
+.IP 15
+The volume was not cleanly unmounted.
+.IP 16
+The volume is already exclusively opened and in use by a kernel
+driver or software.
+.IP 17
+Unsetup SoftRAID/FakeRAID hardware.
+.IP 18
+Unknown reason.
+.IP 19
+Not enough privilege to mount.
+.IP 20
+Out of memory.
+.IP 21
+Unclassified FUSE error.
+.SH KNOWN ISSUES
+Please see
+.RS
+.sp
+http://tuxera.com/community/ntfs-3g-faq/
+.sp
+.RE
+for common questions and known issues.
+If you think you have found an undocumented problem in the latest release of
+the software then please send an email describing it in detail.
+You can contact the development team on the ntfs\-3g\-devel@lists.sf.net
+address.
+.SH AUTHORS
+.B ntfs-3g.probe
+was written by Szabolcs Szakacsits.
+.SH THANKS
+Alon Bar-Lev has integrated the utility into the NTFS-3G build process and
+tested it with Erik Larsson before the public release.
+.SH SEE ALSO
+.BR ntfs-3g (8)
diff --git a/src/ntfs-3g.probe.c b/src/ntfs-3g.probe.c
new file mode 100644
index 0000000..592abd7
--- /dev/null
+++ b/src/ntfs-3g.probe.c
@@ -0,0 +1,163 @@
+/**
+ * ntfs-3g.probe - Probe NTFS volume mountability
+ *
+ * Copyright (c) 2007-2009 Szabolcs Szakacsits
+ *
+ * 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <getopt.h>
+
+#include "compat.h"
+#include "volume.h"
+#include "misc.h"
+
+typedef enum {
+ PROBE_UNSET,
+ PROBE_READONLY,
+ PROBE_READWRITE
+} probe_t;
+
+static struct options {
+ probe_t probetype;
+ char *device;
+} opts;
+
+static const char *EXEC_NAME = "ntfs-3g.probe";
+
+static const char *usage_msg =
+"\n"
+"%s %s - Probe NTFS volume mountability\n"
+"\n"
+"Copyright (C) 2007 Szabolcs Szakacsits\n"
+"\n"
+"Usage: %s <--readonly|--readwrite> <device|image_file>\n"
+"\n"
+"Example: ntfs-3g.probe --readwrite /dev/sda1\n"
+"\n"
+"%s";
+
+static int ntfs_open(const char *device)
+{
+ ntfs_volume *vol;
+ unsigned long flags = 0;
+ int ret = NTFS_VOLUME_OK;
+
+ if (opts.probetype == PROBE_READONLY)
+ flags |= MS_RDONLY;
+
+ vol = ntfs_mount(device, flags);
+ if (!vol)
+ ret = ntfs_volume_error(errno);
+
+ ntfs_umount(vol, FALSE);
+
+ return ret;
+}
+
+static void usage(void)
+{
+ ntfs_log_info(usage_msg, EXEC_NAME, VERSION, EXEC_NAME, ntfs_home);
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int c;
+
+ static const char *sopt = "-hrw";
+ static const struct option lopt[] = {
+ { "readonly", no_argument, NULL, 'r' },
+ { "readwrite", no_argument, NULL, 'w' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ opterr = 0; /* We handle errors. */
+ opts.probetype = PROBE_UNSET;
+
+ while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
+ switch (c) {
+ case 1: /* A non-option argument */
+ if (!opts.device) {
+ opts.device = ntfs_malloc(PATH_MAX + 1);
+ if (!opts.device)
+ return -1;
+
+ strncpy(opts.device, optarg, PATH_MAX);
+ opts.device[PATH_MAX] = 0;
+ } else {
+ ntfs_log_error("%s: You must specify exactly "
+ "one device\n", EXEC_NAME);
+ return -1;
+ }
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ case 'r':
+ opts.probetype = PROBE_READONLY;
+ break;
+ case 'w':
+ opts.probetype = PROBE_READWRITE;
+ break;
+ default:
+ ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME,
+ argv[optind - 1]);
+ return -1;
+ }
+ }
+
+ if (!opts.device) {
+ ntfs_log_error("ERROR: %s: Device is missing\n", EXEC_NAME);
+ return -1;
+ }
+
+ if (opts.probetype == PROBE_UNSET) {
+ ntfs_log_error("ERROR: %s: Probe type is missing\n", EXEC_NAME);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int err;
+
+ ntfs_log_set_handler(ntfs_log_handler_stderr);
+
+ if (parse_options(argc, argv)) {
+ usage();
+ exit(NTFS_VOLUME_SYNTAX_ERROR);
+ }
+
+ err = ntfs_open(opts.device);
+
+ free(opts.device);
+ exit(err);
+}
+
diff --git a/src/ntfs-3g.secaudit.8.in b/src/ntfs-3g.secaudit.8.in
new file mode 100644
index 0000000..79c05ac
--- /dev/null
+++ b/src/ntfs-3g.secaudit.8.in
@@ -0,0 +1,171 @@
+.\" Copyright (c) 2007-2009 Jean-Pierre André.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH NTFS-3G.SECAUDIT 8 "February 2010" "ntfs-3g.secaudit 1.3.8"
+.SH NAME
+ntfs-3g.secaudit \- NTFS Security Data Auditing
+.SH SYNOPSIS
+.B ntfs-3g.secaudit
+\fB[\fIoptions\fP\fB]\fR
+.I args
+.PP
+Where \fIoptions\fP is a combination of :
+.RS
+-a full auditing of security data (Linux only)
+.RE
+.RS
+-b backup ACLs
+.RE
+.RS
+-e setting extra backed-up parameters (in conjunction with -s)
+.RE
+.RS
+-h displaying hexadecimal security descriptors saved in a file
+.RE
+.RS
+-r recursing in a directory
+.RE
+.RS
+-s setting backed-up ACLs
+.RE
+.RS
+-v verbose (very verbose if set twice)
+.RE
+.PP
+and args define the parameters and the set of files acted upon.
+.PP
+Typing secaudit with no args will display a summary of available options.
+.SH DESCRIPTION
+\fBntfs-3g.secaudit\fR
+displays the ownership and permissions of a set of files on an NTFS
+file system, and checks their consistency. It can be started in terminal
+mode only (no graphical user interface is available.)
+.PP
+When a \fIvolume\fR is required, it has to be unmounted, and the command
+has to be issued as \fBroot\fP. The \fIvolume\fR can be either a block
+device (i.e. a disk partition) or an image file.
+.PP
+When acting on a directory or volume, the command may produce a lot
+of information. It is therefore advisable to redirect the output to
+a file or pipe it to a text editor for examination.
+.SH OPTIONS
+Below are the valid combinations of options and arguments that
+\fBntfs-3g.secaudit\fR accepts. All the indicated arguments are
+mandatory and must be unique (if wildcards are used, they must
+resolve to a single name.)
+.TP
+\fB-h\fP \fIfile\fP
+Displays in an human readable form the hexadecimal security descriptors
+saved in \fIfile\fP. This can be used to turn a verbose output into a very
+verbose output.
+.TP
+\fB-a[rv]\fP \fIvolume\fP
+Audits the volume : all the global security data on \fIvolume\fP are scanned
+and errors are displayed. If option \fB-r\fP is present, all files and
+directories are also scanned and their relations to global security data
+are checked. This can produce a lot of data.
+
+This option is not effective on volumes formatted for old NTFS versions (pre
+NTFS 3.0). Such volumes have no global security data.
+
+When errors are signalled, it is advisable to repair the volume with an
+appropriate tool (such as \fBchkdsk\fP on Windows.)
+.TP
+\fB[-v]\fP \fIvolume\fP \fIfile\fP
+Displays the security parameters of \fIfile\fP : its interpreted Linux mode
+(rwx flags in octal) and Posix ACL[1], its security key if any, and its
+security descriptor if verbose output.
+.TP
+\fB-r[v]\fP \fIvolume\fP \fIdirectory\fP
+displays the security parameters of all files and subdirectories in
+\fIdirectory\fP : their interpreted Linux mode (rwx flags in octal) and Posix
+ACL[1], their security key if any, and their security descriptor if
+verbose output.
+.TP
+.B -b[v] \fIvolume\fP \fI[directory]\fP
+Recursively extracts to standard output the NTFS ACLs of files in \fIvolume\fP
+and \fIdirectory\fP.
+.TP
+\fB-s[ev]\fP \fIvolume\fP \fI[backup-file]\fP
+Sets the NTFS ACLS as indicated in \fIbackup-file\fP or standard input. The
+input data must have been created on Linux. With option \fB-e\fP, also sets
+extra parameters (currently Windows attrib).
+.TP
+\fIvolume\fP \fIperms\fP \fIfile\fP
+Sets the security parameters of file to perms. Perms is the Linux
+requested mode (rwx flags, expressed in octal form as in chmod) or
+a Posix ACL[1] (expressed like in setfacl -m). This sets a new ACL
+which is effective for Linux and Windows.
+.TP
+\fB-r[v]\fP \fIvolume\fP \fIperms\fP \fIdirectory\fP
+Sets the security parameters of all files and subdirectories in
+\fIdirectory\fP to \fIperms\fP. Perms is the Linux requested mode (rwx flags,
+expressed in octal form as in \fBchmod\fP), or a Posix ACL[1] (expressed like
+in \fBsetfacl -m\fP.) This sets new ACLs which are effective for Linux and
+Windows.
+.TP
+\fB[-v]\fP \fImounted-file\fP
+Displays the security parameters of \fImounted-file\fP : its interpreted
+Linux mode (rwx flags in octal) and Posix ACL[1], its security key if any,
+and its security descriptor if verbose output. This is a special case which
+acts on a mounted file (or directory) and does not require being root. The
+Posix ACL interpretation can only be displayed if the full path to
+\fImounted-file\fP from the root of the global file tree is provided.
+.SH NOTE
+[1] provided the POSIX ACL option was selected at compile time. A Posix ACL
+specification looks like "\fB[d:]{ugmo}:[id]:[perms],...\fP" where id is a
+numeric user or group id, and perms an octal digit or a set from the letters
+r, w and x.
+.RS
+Example : "\fBu::7,g::5,o:0,u:510:rwx,g:500:5,d:u:510:7\fP"
+.SH EXAMPLES
+Audit the global security data on /dev/sda1
+.RS
+.sp
+.B ntfs-3g.secaudit -ar /dev/sda1
+.sp
+.RE
+Display the ownership and permissions parameters for files in directory
+/audio/music on device /dev/sda5, excluding sub-directories :
+.RS
+.sp
+.B ntfs-3g.secaudit /dev/sda5 /audio/music
+.sp
+.RE
+Set all files in directory /audio/music on device /dev/sda5 as writeable
+by owner and read-only for everybody :
+.RS
+.sp
+.B ntfs-3g.secaudit -r /dev/sda5 644 /audio/music
+.sp
+.RE
+.SH EXIT CODES
+.B ntfs-3g.secaudit
+exits with a value of 0 when no error was detected, and with a value
+of 1 when an error was detected.
+.SH KNOWN ISSUES
+Please see
+.RS
+.sp
+http://www.tuxera.com/community/ntfs-3g-faq/
+.sp
+.RE
+for common questions and known issues.
+If you would find a new one in the latest release of
+the software then please send an email describing it
+in detail. You can contact the
+development team on the ntfs\-3g\-devel@lists.sf.net
+address.
+.SH AUTHORS
+.B ntfs-3g.secaudit
+has been developed by Jean-Pierre André.
+.SH THANKS
+Several people made heroic efforts, often over five or more
+years which resulted the ntfs-3g driver. Most importantly they are
+Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy,
+Yuval Fledel, and the author of the groundbreaking FUSE filesystem development
+framework, Miklos Szeredi.
+.SH SEE ALSO
+.BR ntfsprogs (8),
+.BR attr (5),
+.BR getfattr (1)
diff --git a/src/ntfs-3g.usermap.8.in b/src/ntfs-3g.usermap.8.in
new file mode 100644
index 0000000..6efd47b
--- /dev/null
+++ b/src/ntfs-3g.usermap.8.in
@@ -0,0 +1,96 @@
+.\" Copyright (c) 2007-2009 Jean-Pierre André.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH NTFS-3G.USERMAP 8 "February 2010" "ntfs-3g.usermap 1.1.2"
+.SH NAME
+ntfs-3g.usermap \- NTFS Building a User Mapping File
+.SH SYNOPSIS
+.B ntfs-3g.usermap
+\fIwindows-system-device\fP
+\fB[\fIother-ntfs-device\fP...\fB]\fR
+.PP
+Where \fIwindows-system-device\fP is the device containing the Windows system
+whose users are to be mapped to current Linux system.
+.PP
+And \fIother-ntfs-device\fP is another device containing files which are
+to be accessed both by the Windows mentioned above and current Linux system.
+.PP
+the ntfs-3g.usermap command must be started as root, and the designated devices
+must not be mounted.
+.PP
+Typing ntfs-3g.usermap with no args will display a summary of command
+arguments.
+.SH DESCRIPTION
+\fBntfs-3g.usermap\fR
+creates the file defining the mapping of Windows accounts to Linux logins for
+users who owns files which should be visible from both Windows and
+Linux.
+.PP
+It relies on existing files which were created on Windows, trying
+to locate significant files and asking which Linux user or group should
+own them. When a Linux owner or group is requested, the reply may be :
+.PP
+- the uid or gid (numeric or symbolic) of Linux owner or group of the file.
+.RS
+In that situation, no more file with the same Windows owner will be selected.
+.RE
+- or no answer, when not able to define the owner or group.
+.RS
+In that situation another file owned by the same Windows user or group
+may be selected later so that a mapping can be defined.
+.RE
+.PP
+The mappings for standard Windows users, such as "Administrator" or
+"All Users" are defined implicitly. As a consequence a user mapping should
+never be defined as Linux root.
+.PP
+When there are no more significant files, ntfs-3g.usermap create the
+mapping file into the file UserMapping in the current directory. This
+file has to be moved to the hidden directory .NTFS-3G in the root of
+all the NTFS file systems to be shared between Windows and Linux. This
+requires the file system to be mounted, but the created file will not
+be taken into account if not present at mount time, which means the
+file system has to be unmounted and mounted again for the new mapping
+file to be taken into account.
+.SH OPTIONS
+No option is defined for ntfs-3g.usermap.
+.SH EXAMPLES
+Map the users defined on the Windows system present on /dev/sda1 :
+.RS
+.sp
+.B ntfs-3g.usermap /dev/sda1
+.sp
+.RE
+.PP
+A detailed example, with screen displays is available on
+http://pagesperso-orange.fr/b.andre/usermap.html
+.SH EXIT CODES
+.B ntfs-3g.usermap
+exits with a value of 0 when no error was detected, and with a value
+of 1 when an error was detected.
+.SH KNOWN ISSUES
+Please see
+.RS
+.sp
+http://www.tuxera.com/community/ntfs-3g-faq/
+.sp
+.RE
+for common questions and known issues.
+If you would find a new one in the latest release of
+the software then please send an email describing it
+in detail. You can contact the
+development team on the ntfs\-3g\-devel@lists.sf.net
+address.
+.SH AUTHORS
+.B ntfs-3g.secaudit
+has been developed by Jean-Pierre André.
+.SH THANKS
+Several people made heroic efforts, often over five or more
+years which resulted the ntfs-3g driver. Most importantly they are
+Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy,
+Yuval Fledel, and the author of the groundbreaking FUSE filesystem development
+framework, Miklos Szeredi.
+.SH SEE ALSO
+.BR ntfsprogs (8),
+.BR attr (5),
+.BR getfattr (1)
diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c
new file mode 100644
index 0000000..dc5fc37
--- /dev/null
+++ b/src/ntfs-3g_common.c
@@ -0,0 +1,690 @@
+/**
+ * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g.
+ *
+ * Copyright (c) 2010-2011 Jean-Pierre Andre
+ * Copyright (c) 2010 Erik Larsson
+ *
+ * This program/include file 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/include file 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <getopt.h>
+#include <fuse.h>
+
+#include "inode.h"
+#include "security.h"
+#include "xattrs.h"
+#include "ntfs-3g_common.h"
+#include "realpath.h"
+#include "misc.h"
+
+const char xattr_ntfs_3g[] = "ntfs-3g.";
+
+const char nf_ns_user_prefix[] = "user.";
+const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1;
+const char nf_ns_system_prefix[] = "system.";
+const int nf_ns_system_prefix_len = sizeof(nf_ns_system_prefix) - 1;
+const char nf_ns_security_prefix[] = "security.";
+const int nf_ns_security_prefix_len = sizeof(nf_ns_security_prefix) - 1;
+const char nf_ns_trusted_prefix[] = "trusted.";
+const int nf_ns_trusted_prefix_len = sizeof(nf_ns_trusted_prefix) - 1;
+
+static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo";
+
+static const char def_opts[] = "allow_other,nonempty,";
+
+ /*
+ * Table of recognized options
+ * Their order may be significant
+ * The options invalid in some configuration should still
+ * be present, so that an error can be returned
+ */
+const struct DEFOPTION optionlist[] = {
+ { "ro", OPT_RO, FLGOPT_APPEND | FLGOPT_BOGUS },
+ { "noatime", OPT_NOATIME, FLGOPT_BOGUS },
+ { "atime", OPT_ATIME, FLGOPT_BOGUS },
+ { "relatime", OPT_RELATIME, FLGOPT_BOGUS },
+ { "delay_mtime", OPT_DMTIME, FLGOPT_BOGUS },
+ { "fake_rw", OPT_FAKE_RW, FLGOPT_BOGUS },
+ { "fsname", OPT_FSNAME, FLGOPT_NOSUPPORT },
+ { "no_def_opts", OPT_NO_DEF_OPTS, FLGOPT_BOGUS },
+ { "default_permissions", OPT_DEFAULT_PERMISSIONS, FLGOPT_BOGUS },
+ { "permissions", OPT_PERMISSIONS, FLGOPT_BOGUS },
+ { "acl", OPT_ACL, FLGOPT_BOGUS },
+ { "umask", OPT_UMASK, FLGOPT_OCTAL },
+ { "fmask", OPT_FMASK, FLGOPT_OCTAL },
+ { "dmask", OPT_DMASK, FLGOPT_OCTAL },
+ { "uid", OPT_UID, FLGOPT_DECIMAL },
+ { "gid", OPT_GID, FLGOPT_DECIMAL },
+ { "show_sys_files", OPT_SHOW_SYS_FILES, FLGOPT_BOGUS },
+ { "hide_hid_files", OPT_HIDE_HID_FILES, FLGOPT_BOGUS },
+ { "hide_dot_files", OPT_HIDE_DOT_FILES, FLGOPT_BOGUS },
+ { "ignore_case", OPT_IGNORE_CASE, FLGOPT_BOGUS },
+ { "windows_names", OPT_WINDOWS_NAMES, FLGOPT_BOGUS },
+ { "compression", OPT_COMPRESSION, FLGOPT_BOGUS },
+ { "nocompression", OPT_NOCOMPRESSION, FLGOPT_BOGUS },
+ { "silent", OPT_SILENT, FLGOPT_BOGUS },
+ { "recover", OPT_RECOVER, FLGOPT_BOGUS },
+ { "norecover", OPT_NORECOVER, FLGOPT_BOGUS },
+ { "remove_hiberfile", OPT_REMOVE_HIBERFILE, FLGOPT_BOGUS },
+ { "sync", OPT_SYNC, FLGOPT_BOGUS | FLGOPT_APPEND },
+ { "big_writes", OPT_BIG_WRITES, FLGOPT_BOGUS },
+ { "locale", OPT_LOCALE, FLGOPT_STRING },
+ { "nfconv", OPT_NFCONV, FLGOPT_BOGUS },
+ { "nonfconv", OPT_NONFCONV, FLGOPT_BOGUS },
+ { "streams_interface", OPT_STREAMS_INTERFACE, FLGOPT_STRING },
+ { "user_xattr", OPT_USER_XATTR, FLGOPT_BOGUS },
+ { "noauto", OPT_NOAUTO, FLGOPT_BOGUS },
+ { "debug", OPT_DEBUG, FLGOPT_BOGUS },
+ { "no_detach", OPT_NO_DETACH, FLGOPT_BOGUS },
+ { "remount", OPT_REMOUNT, FLGOPT_BOGUS },
+ { "blksize", OPT_BLKSIZE, FLGOPT_STRING },
+ { "inherit", OPT_INHERIT, FLGOPT_BOGUS },
+ { "addsecurids", OPT_ADDSECURIDS, FLGOPT_BOGUS },
+ { "staticgrps", OPT_STATICGRPS, FLGOPT_BOGUS },
+ { "usermapping", OPT_USERMAPPING, FLGOPT_STRING },
+ { "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING },
+ { "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS },
+ { (const char*)NULL, 0, 0 } /* end marker */
+} ;
+
+#define STRAPPEND_MAX_INSIZE 8192
+#define strappend_is_large(x) ((x) > STRAPPEND_MAX_INSIZE)
+
+int ntfs_strappend(char **dest, const char *append)
+{
+ char *p;
+ size_t size_append, size_dest = 0;
+
+ if (!dest)
+ return -1;
+ if (!append)
+ return 0;
+
+ size_append = strlen(append);
+ if (*dest)
+ size_dest = strlen(*dest);
+
+ if (strappend_is_large(size_dest) || strappend_is_large(size_append)) {
+ errno = EOVERFLOW;
+ ntfs_log_perror("%s: Too large input buffer", EXEC_NAME);
+ return -1;
+ }
+
+ p = (char*)realloc(*dest, size_dest + size_append + 1);
+ if (!p) {
+ ntfs_log_perror("%s: Memory realloction failed", EXEC_NAME);
+ return -1;
+ }
+
+ *dest = p;
+ strcpy(*dest + size_dest, append);
+
+ return 0;
+}
+
+static int bogus_option_value(char *val, const char *s)
+{
+ if (val) {
+ ntfs_log_error("'%s' option shouldn't have value.\n", s);
+ return -1;
+ }
+ return 0;
+}
+
+static int missing_option_value(char *val, const char *s)
+{
+ if (!val) {
+ ntfs_log_error("'%s' option should have a value.\n", s);
+ return -1;
+ }
+ return 0;
+}
+
+char *parse_mount_options(ntfs_fuse_context_t *ctx,
+ const struct ntfs_options *popts, BOOL low_fuse)
+{
+ char *options, *s, *opt, *val, *ret = NULL;
+ const char *orig_opts = popts->options;
+ BOOL no_def_opts = FALSE;
+ int default_permissions = 0;
+ int permissions = 0;
+ int acl = 0;
+ int want_permissions = 0;
+ int intarg;
+ const struct DEFOPTION *poptl;
+
+ ctx->secure_flags = 0;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ ctx->efs_raw = FALSE;
+#endif /* HAVE_SETXATTR */
+ ctx->compression = DEFAULT_COMPRESSION;
+ options = strdup(orig_opts ? orig_opts : "");
+ if (!options) {
+ ntfs_log_perror("%s: strdup failed", EXEC_NAME);
+ return NULL;
+ }
+
+ s = options;
+ while (s && *s && (val = strsep(&s, ","))) {
+ opt = strsep(&val, "=");
+ poptl = optionlist;
+ while (poptl->name && strcmp(poptl->name,opt))
+ poptl++;
+ if (poptl->name) {
+ if ((poptl->flags & FLGOPT_BOGUS)
+ && bogus_option_value(val, opt))
+ goto err_exit;
+ if ((poptl->flags & FLGOPT_OCTAL)
+ && (!val
+ || !sscanf(val, "%o", &intarg))) {
+ ntfs_log_error("'%s' option needs an octal value\n",
+ opt);
+ goto err_exit;
+ }
+ if ((poptl->flags & FLGOPT_DECIMAL)
+ && (!val
+ || !sscanf(val, "%i", &intarg))) {
+ ntfs_log_error("'%s' option needs a decimal value\n",
+ opt);
+ goto err_exit;
+ }
+ if ((poptl->flags & FLGOPT_STRING)
+ && missing_option_value(val, opt))
+ goto err_exit;
+
+ switch (poptl->type) {
+ case OPT_RO :
+ case OPT_FAKE_RW :
+ ctx->ro = TRUE;
+ break;
+ case OPT_NOATIME :
+ ctx->atime = ATIME_DISABLED;
+ break;
+ case OPT_ATIME :
+ ctx->atime = ATIME_ENABLED;
+ break;
+ case OPT_RELATIME :
+ ctx->atime = ATIME_RELATIVE;
+ break;
+ case OPT_DMTIME :
+ ctx->dmtime = TRUE;
+ break;
+ case OPT_NO_DEF_OPTS :
+ no_def_opts = TRUE; /* Don't add default options. */
+ ctx->silent = FALSE; /* cancel default silent */
+ break;
+ case OPT_DEFAULT_PERMISSIONS :
+ default_permissions = 1;
+ break;
+ case OPT_PERMISSIONS :
+ permissions = 1;
+ break;
+#if POSIXACLS
+ case OPT_ACL :
+ acl = 1;
+ break;
+#endif
+ case OPT_UMASK :
+ ctx->dmask = ctx->fmask = intarg;
+ want_permissions = 1;
+ break;
+ case OPT_FMASK :
+ ctx->fmask = intarg;
+ want_permissions = 1;
+ break;
+ case OPT_DMASK :
+ ctx->dmask = intarg;
+ want_permissions = 1;
+ break;
+ case OPT_UID :
+ ctx->uid = intarg;
+ want_permissions = 1;
+ break;
+ case OPT_GID :
+ ctx->gid = intarg;
+ want_permissions = 1;
+ break;
+ case OPT_SHOW_SYS_FILES :
+ ctx->show_sys_files = TRUE;
+ break;
+ case OPT_HIDE_HID_FILES :
+ ctx->hide_hid_files = TRUE;
+ break;
+ case OPT_HIDE_DOT_FILES :
+ ctx->hide_dot_files = TRUE;
+ break;
+ case OPT_WINDOWS_NAMES :
+ ctx->windows_names = TRUE;
+ break;
+ case OPT_IGNORE_CASE :
+ if (low_fuse)
+ ctx->ignore_case = TRUE;
+ else {
+ ntfs_log_error("'%s' is an unsupported option.\n",
+ poptl->name);
+ goto err_exit;
+ }
+ break;
+ case OPT_COMPRESSION :
+ ctx->compression = TRUE;
+ break;
+ case OPT_NOCOMPRESSION :
+ ctx->compression = FALSE;
+ break;
+ case OPT_SILENT :
+ ctx->silent = TRUE;
+ break;
+ case OPT_RECOVER :
+ ctx->recover = TRUE;
+ break;
+ case OPT_NORECOVER :
+ ctx->recover = FALSE;
+ break;
+ case OPT_REMOVE_HIBERFILE :
+ ctx->hiberfile = TRUE;
+ break;
+ case OPT_SYNC :
+ ctx->sync = TRUE;
+ break;
+#ifdef FUSE_CAP_BIG_WRITES
+ case OPT_BIG_WRITES :
+ ctx->big_writes = TRUE;
+ break;
+#endif
+ case OPT_LOCALE :
+ ntfs_set_char_encoding(val);
+ break;
+#if defined(__APPLE__) || defined(__DARWIN__)
+#ifdef ENABLE_NFCONV
+ case OPT_NFCONV :
+ if (ntfs_macosx_normalize_filenames(1)) {
+ ntfs_log_error("ntfs_macosx_normalize_filenames(1) failed!\n");
+ goto err_exit;
+ }
+ break;
+ case OPT_NONFCONV :
+ if (ntfs_macosx_normalize_filenames(0)) {
+ ntfs_log_error("ntfs_macosx_normalize_filenames(0) failed!\n");
+ goto err_exit;
+ }
+ break;
+#endif /* ENABLE_NFCONV */
+#endif /* defined(__APPLE__) || defined(__DARWIN__) */
+ case OPT_STREAMS_INTERFACE :
+ if (!strcmp(val, "none"))
+ ctx->streams = NF_STREAMS_INTERFACE_NONE;
+ else if (!strcmp(val, "xattr"))
+ ctx->streams = NF_STREAMS_INTERFACE_XATTR;
+ else if (!strcmp(val, "openxattr"))
+ ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR;
+ else if (!low_fuse && !strcmp(val, "windows"))
+ ctx->streams = NF_STREAMS_INTERFACE_WINDOWS;
+ else {
+ ntfs_log_error("Invalid named data streams "
+ "access interface.\n");
+ goto err_exit;
+ }
+ break;
+ case OPT_USER_XATTR :
+ ctx->streams = NF_STREAMS_INTERFACE_XATTR;
+ break;
+ case OPT_NOAUTO :
+ /* Don't pass noauto option to fuse. */
+ break;
+ case OPT_DEBUG :
+ ctx->debug = TRUE;
+ ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
+ ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
+ break;
+ case OPT_NO_DETACH :
+ ctx->no_detach = TRUE;
+ break;
+ case OPT_REMOUNT :
+ ntfs_log_error("Remounting is not supported at present."
+ " You have to umount volume and then "
+ "mount it once again.\n");
+ goto err_exit;
+ case OPT_BLKSIZE :
+ ntfs_log_info("WARNING: blksize option is ignored "
+ "because ntfs-3g must calculate it.\n");
+ break;
+ case OPT_INHERIT :
+ /*
+ * do not overwrite inherited permissions
+ * in create()
+ */
+ ctx->inherit = TRUE;
+ break;
+ case OPT_ADDSECURIDS :
+ /*
+ * create security ids for files being read
+ * with an individual security attribute
+ */
+ ctx->secure_flags |= (1 << SECURITY_ADDSECURIDS);
+ break;
+ case OPT_STATICGRPS :
+ /*
+ * use static definition of groups
+ * for file access control
+ */
+ ctx->secure_flags |= (1 << SECURITY_STATICGRPS);
+ break;
+ case OPT_USERMAPPING :
+ ctx->usermap_path = strdup(val);
+ if (!ctx->usermap_path) {
+ ntfs_log_error("no more memory to store "
+ "'usermapping' option.\n");
+ goto err_exit;
+ }
+ break;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+#ifdef XATTR_MAPPINGS
+ case OPT_XATTRMAPPING :
+ ctx->xattrmap_path = strdup(val);
+ if (!ctx->xattrmap_path) {
+ ntfs_log_error("no more memory to store "
+ "'xattrmapping' option.\n");
+ goto err_exit;
+ }
+ break;
+#endif /* XATTR_MAPPINGS */
+ case OPT_EFS_RAW :
+ ctx->efs_raw = TRUE;
+ break;
+#endif /* HAVE_SETXATTR */
+ case OPT_FSNAME : /* Filesystem name. */
+ /*
+ * We need this to be able to check whether filesystem
+ * mounted or not.
+ * (falling through to default)
+ */
+ default :
+ ntfs_log_error("'%s' is an unsupported option.\n",
+ poptl->name);
+ goto err_exit;
+ }
+ if ((poptl->flags & FLGOPT_APPEND)
+ && (ntfs_strappend(&ret, poptl->name)
+ || ntfs_strappend(&ret, ",")))
+ goto err_exit;
+ } else { /* Probably FUSE option. */
+ if (ntfs_strappend(&ret, opt))
+ goto err_exit;
+ if (val) {
+ if (ntfs_strappend(&ret, "="))
+ goto err_exit;
+ if (ntfs_strappend(&ret, val))
+ goto err_exit;
+ }
+ if (ntfs_strappend(&ret, ","))
+ goto err_exit;
+ }
+ }
+ if (!no_def_opts && ntfs_strappend(&ret, def_opts))
+ goto err_exit;
+ if ((default_permissions || (permissions && !acl))
+ && ntfs_strappend(&ret, "default_permissions,"))
+ goto err_exit;
+ /* The atime options exclude each other */
+ if (ctx->atime == ATIME_RELATIVE && ntfs_strappend(&ret, "relatime,"))
+ goto err_exit;
+ else if (ctx->atime == ATIME_ENABLED && ntfs_strappend(&ret, "atime,"))
+ goto err_exit;
+ else if (ctx->atime == ATIME_DISABLED && ntfs_strappend(&ret, "noatime,"))
+ goto err_exit;
+
+ if (ntfs_strappend(&ret, "fsname="))
+ goto err_exit;
+ if (ntfs_strappend(&ret, popts->device))
+ goto err_exit;
+ if (permissions && !acl)
+ ctx->secure_flags |= (1 << SECURITY_DEFAULT);
+ if (acl)
+ ctx->secure_flags |= (1 << SECURITY_ACL);
+ if (want_permissions)
+ ctx->secure_flags |= (1 << SECURITY_WANTED);
+ if (ctx->ro)
+ ctx->secure_flags &= ~(1 << SECURITY_ADDSECURIDS);
+exit:
+ free(options);
+ return ret;
+err_exit:
+ free(ret);
+ ret = NULL;
+ goto exit;
+}
+
+/**
+ * parse_options - Read and validate the programs command line
+ * Read the command line, verify the syntax and parse the options.
+ *
+ * Return: 0 success, -1 error.
+ */
+int ntfs_parse_options(struct ntfs_options *popts, void (*usage)(void),
+ int argc, char *argv[])
+{
+ int c;
+
+ static const char *sopt = "-o:hnvV";
+ static const struct option lopt[] = {
+ { "options", required_argument, NULL, 'o' },
+ { "help", no_argument, NULL, 'h' },
+ { "no-mtab", no_argument, NULL, 'n' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ opterr = 0; /* We'll handle the errors, thank you. */
+
+ while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
+ switch (c) {
+ case 1: /* A non-option argument */
+ if (!popts->device) {
+ popts->device = ntfs_malloc(PATH_MAX + 1);
+ if (!popts->device)
+ return -1;
+
+ /* Canonicalize device name (mtab, etc) */
+ popts->arg_device = optarg;
+ if (!ntfs_realpath_canonicalize(optarg,
+ popts->device)) {
+ ntfs_log_perror("%s: Failed to access "
+ "volume '%s'", EXEC_NAME, optarg);
+ free(popts->device);
+ popts->device = NULL;
+ return -1;
+ }
+ } else if (!popts->mnt_point) {
+ popts->mnt_point = optarg;
+ } else {
+ ntfs_log_error("%s: You must specify exactly one "
+ "device and exactly one mount "
+ "point.\n", EXEC_NAME);
+ return -1;
+ }
+ break;
+ case 'o':
+ if (popts->options)
+ if (ntfs_strappend(&popts->options, ","))
+ return -1;
+ if (ntfs_strappend(&popts->options, optarg))
+ return -1;
+ break;
+ case 'h':
+ usage();
+ exit(9);
+ case 'n':
+ /*
+ * no effect - automount passes it, meaning 'no-mtab'
+ */
+ break;
+ case 'v':
+ /*
+ * We must handle the 'verbose' option even if
+ * we don't use it because mount(8) passes it.
+ */
+ break;
+ case 'V':
+ ntfs_log_info("%s %s %s %d\n", EXEC_NAME, VERSION,
+ FUSE_TYPE, fuse_version());
+ exit(0);
+ default:
+ ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME,
+ argv[optind - 1]);
+ return -1;
+ }
+ }
+
+ if (!popts->device) {
+ ntfs_log_error("%s: No device is specified.\n", EXEC_NAME);
+ return -1;
+ }
+ if (!popts->mnt_point) {
+ ntfs_log_error("%s: No mountpoint is specified.\n", EXEC_NAME);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_SETXATTR
+
+int ntfs_fuse_listxattr_common(ntfs_inode *ni, ntfs_attr_search_ctx *actx,
+ char *list, size_t size, BOOL prefixing)
+{
+ int ret = 0;
+ char *to = list;
+#ifdef XATTR_MAPPINGS
+ BOOL accepted;
+ const struct XATTRMAPPING *item;
+#endif /* XATTR_MAPPINGS */
+
+ /* first list the regular user attributes (ADS) */
+ while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE,
+ 0, NULL, 0, actx)) {
+ char *tmp_name = NULL;
+ int tmp_name_len;
+
+ if (!actx->attr->name_length)
+ continue;
+ tmp_name_len = ntfs_ucstombs(
+ (ntfschar *)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->name_offset)),
+ actx->attr->name_length, &tmp_name, 0);
+ if (tmp_name_len < 0) {
+ ret = -errno;
+ goto exit;
+ }
+ /*
+ * When using name spaces, do not return
+ * security, trusted or system attributes
+ * (filtered elsewhere anyway)
+ * otherwise insert "user." prefix
+ */
+ if (prefixing) {
+ if ((strlen(tmp_name) > sizeof(xattr_ntfs_3g))
+ && !strncmp(tmp_name,xattr_ntfs_3g,
+ sizeof(xattr_ntfs_3g)-1))
+ tmp_name_len = 0;
+ else
+ ret += tmp_name_len
+ + nf_ns_user_prefix_len + 1;
+ } else
+ ret += tmp_name_len + 1;
+ if (size && tmp_name_len) {
+ if ((size_t)ret <= size) {
+ if (prefixing) {
+ strcpy(to, nf_ns_user_prefix);
+ to += nf_ns_user_prefix_len;
+ }
+ strncpy(to, tmp_name, tmp_name_len);
+ to += tmp_name_len;
+ *to = 0;
+ to++;
+ } else {
+ free(tmp_name);
+ ret = -ERANGE;
+ goto exit;
+ }
+ }
+ free(tmp_name);
+ }
+#ifdef XATTR_MAPPINGS
+ /* now append the system attributes mapped to user space */
+ for (item=ni->vol->xattr_mapping; item; item=item->next) {
+ switch (item->xattr) {
+ case XATTR_NTFS_EFSINFO :
+ accepted = ni->vol->efs_raw
+ && (ni->flags & FILE_ATTR_ENCRYPTED);
+ break;
+ case XATTR_NTFS_REPARSE_DATA :
+ accepted = (ni->flags & FILE_ATTR_REPARSE_POINT)
+ != const_cpu_to_le32(0);
+ break;
+// TODO : we are supposed to only return xattrs which are set
+// this is more complex for OBJECT_ID and DOS_NAME
+ default : accepted = TRUE;
+ break;
+ }
+ if (accepted) {
+ ret += strlen(item->name) + 1;
+ if (size) {
+ if ((size_t)ret <= size) {
+ strcpy(to, item->name);
+ to += strlen(item->name);
+ *to++ = 0;
+ } else {
+ ret = -ERANGE;
+ goto exit;
+ }
+ }
+#else /* XATTR_MAPPINGS */
+ /* List efs info xattr for encrypted files */
+ if (ni->vol->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED)) {
+ ret += sizeof(nf_ns_alt_xattr_efsinfo);
+ if ((size_t)ret <= size) {
+ memcpy(to, nf_ns_alt_xattr_efsinfo,
+ sizeof(nf_ns_alt_xattr_efsinfo));
+ to += sizeof(nf_ns_alt_xattr_efsinfo);
+#endif /* XATTR_MAPPINGS */
+ }
+ }
+exit :
+ return (ret);
+}
+
+#endif /* HAVE_SETXATTR */
diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h
new file mode 100644
index 0000000..9d26984
--- /dev/null
+++ b/src/ntfs-3g_common.h
@@ -0,0 +1,183 @@
+/*
+ * ntfs-3g_common.h - Common declarations for ntfs-3g and lowntfs-3g.
+ *
+ * Copyright (c) 2010-2011 Jean-Pierre Andre
+ * Copyright (c) 2010 Erik Larsson
+ *
+ * This program/include file 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/include file 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _NTFS_3G_COMMON_H
+#define _NTFS_3G_COMMON_H
+
+#include "inode.h"
+
+struct ntfs_options {
+ char *mnt_point; /* Mount point */
+ char *options; /* Mount options */
+ char *device; /* Device to mount */
+ char *arg_device; /* Device requested in argv */
+} ;
+
+typedef enum {
+ NF_STREAMS_INTERFACE_NONE, /* No access to named data streams. */
+ NF_STREAMS_INTERFACE_XATTR, /* Map named data streams to xattrs. */
+ NF_STREAMS_INTERFACE_OPENXATTR, /* Same, not limited to "user." */
+ NF_STREAMS_INTERFACE_WINDOWS, /* "file:stream" interface. */
+} ntfs_fuse_streams_interface;
+
+struct DEFOPTION {
+ const char *name;
+ int type;
+ int flags;
+} ;
+ /* Options, order not significant */
+enum {
+ OPT_RO,
+ OPT_NOATIME,
+ OPT_ATIME,
+ OPT_RELATIME,
+ OPT_DMTIME,
+ OPT_FAKE_RW,
+ OPT_FSNAME,
+ OPT_NO_DEF_OPTS,
+ OPT_DEFAULT_PERMISSIONS,
+ OPT_PERMISSIONS,
+ OPT_ACL,
+ OPT_UMASK,
+ OPT_FMASK,
+ OPT_DMASK,
+ OPT_UID,
+ OPT_GID,
+ OPT_SHOW_SYS_FILES,
+ OPT_HIDE_HID_FILES,
+ OPT_HIDE_DOT_FILES,
+ OPT_IGNORE_CASE,
+ OPT_WINDOWS_NAMES,
+ OPT_COMPRESSION,
+ OPT_NOCOMPRESSION,
+ OPT_SILENT,
+ OPT_RECOVER,
+ OPT_NORECOVER,
+ OPT_REMOVE_HIBERFILE,
+ OPT_SYNC,
+ OPT_BIG_WRITES,
+ OPT_LOCALE,
+ OPT_NFCONV,
+ OPT_NONFCONV,
+ OPT_STREAMS_INTERFACE,
+ OPT_USER_XATTR,
+ OPT_NOAUTO,
+ OPT_DEBUG,
+ OPT_NO_DETACH,
+ OPT_REMOUNT,
+ OPT_BLKSIZE,
+ OPT_INHERIT,
+ OPT_ADDSECURIDS,
+ OPT_STATICGRPS,
+ OPT_USERMAPPING,
+ OPT_XATTRMAPPING,
+ OPT_EFS_RAW,
+} ;
+
+ /* Option flags */
+enum {
+ FLGOPT_BOGUS = 1,
+ FLGOPT_STRING = 2,
+ FLGOPT_OCTAL = 4,
+ FLGOPT_DECIMAL = 8,
+ FLGOPT_APPEND = 16,
+ FLGOPT_NOSUPPORT = 32
+} ;
+
+typedef enum {
+ ATIME_ENABLED,
+ ATIME_DISABLED,
+ ATIME_RELATIVE
+} ntfs_atime_t;
+
+typedef struct {
+ ntfs_volume *vol;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int fmask;
+ unsigned int dmask;
+ ntfs_fuse_streams_interface streams;
+ ntfs_atime_t atime;
+ BOOL dmtime;
+ BOOL ro;
+ BOOL show_sys_files;
+ BOOL hide_hid_files;
+ BOOL hide_dot_files;
+ BOOL windows_names;
+ BOOL ignore_case;
+ BOOL compression;
+ BOOL acl;
+ BOOL silent;
+ BOOL recover;
+ BOOL hiberfile;
+ BOOL sync;
+ BOOL big_writes;
+ BOOL debug;
+ BOOL no_detach;
+ BOOL blkdev;
+ BOOL mounted;
+#ifdef HAVE_SETXATTR /* extended attributes interface required */
+ BOOL efs_raw;
+#ifdef XATTR_MAPPINGS
+ char *xattrmap_path;
+#endif /* XATTR_MAPPINGS */
+#endif /* HAVE_SETXATTR */
+ struct fuse_chan *fc;
+ BOOL inherit;
+ unsigned int secure_flags;
+ char *usermap_path;
+ char *abs_mnt_point;
+ struct PERMISSIONS_CACHE *seccache;
+ struct SECURITY_CONTEXT security;
+ struct open_file *open_files; /* only defined in lowntfs-3g */
+ u64 latest_ghost;
+} ntfs_fuse_context_t;
+
+extern const char *EXEC_NAME;
+
+#ifdef FUSE_INTERNAL
+#define FUSE_TYPE "integrated FUSE"
+#else
+#define FUSE_TYPE "external FUSE"
+#endif
+
+extern const char xattr_ntfs_3g[];
+
+extern const char nf_ns_user_prefix[];
+extern const int nf_ns_user_prefix_len;
+extern const char nf_ns_system_prefix[];
+extern const int nf_ns_system_prefix_len;
+extern const char nf_ns_security_prefix[];
+extern const int nf_ns_security_prefix_len;
+extern const char nf_ns_trusted_prefix[];
+extern const int nf_ns_trusted_prefix_len;
+
+int ntfs_strappend(char **dest, const char *append);
+char *parse_mount_options(ntfs_fuse_context_t *ctx,
+ const struct ntfs_options *popts, BOOL low_fuse);
+int ntfs_parse_options(struct ntfs_options *popts, void (*usage)(void),
+ int argc, char *argv[]);
+
+int ntfs_fuse_listxattr_common(ntfs_inode *ni, ntfs_attr_search_ctx *actx,
+ char *list, size_t size, BOOL prefixing);
+
+#endif /* _NTFS_3G_COMMON_H */
diff --git a/src/secaudit.c b/src/secaudit.c
new file mode 100644
index 0000000..122d7cf
--- /dev/null
+++ b/src/secaudit.c
@@ -0,0 +1,7284 @@
+/*
+ * Display and audit security attributes in an NTFS volume
+ *
+ * Copyright (c) 2007-2011 Jean-Pierre Andre
+ *
+ * Options :
+ * -a auditing security data
+ * -b backing up NTFS ACLs
+ * -e set extra backed-up parameters (in conjunction with -s)
+ * -h displaying hexadecimal security descriptors within a file
+ * -r recursing in a directory
+ * -s setting backed-up NTFS ACLs
+ * -v verbose (very verbose if set twice)
+ * also, if compile-time option is set
+ * -t run internal tests (with no access to storage)
+ *
+ * On Linux (being root, with volume not mounted) :
+ * secaudit -h [file]
+ * display the security descriptors found in file
+ * secaudit -a[rv] volume
+ * audit the volume
+ * secaudit [-v] volume file
+ * display the security parameters of file
+ * secaudit -r[v] volume directory
+ * display the security parameters of files in directory
+ * secaudit -b[v] volume [directory]
+ * backup the security parameters of files in directory
+ * secaudit -s[ve] volume [backupfile]
+ * set the security parameters as indicated in backup
+ * with -e set extra parameters (Windows attrib)
+ * secaudit volume perms file
+ * set the security parameters of file to perms (mode or acl)
+ * secaudit -r[v] volume perms directory
+ * set the security parameters of files in directory to perms
+ * special case, does not require being root :
+ * secaudit [-v] mounted-file
+ * display the security parameters of mounted file
+ *
+ *
+ * On Windows (the volume being part of file name)
+ * secaudit -h [file]
+ * display the security descriptors found in file
+ * secaudit [-v] file
+ * display the security parameters of file
+ * secaudit -r[v] directory
+ * display the security parameters of files in directory
+ * secaudit -b[v] directory
+ * backup the security parameters of files in directory
+ * secaudit -s[v] [backupfile]
+ * set the security parameters as indicated in backup
+ * with -e set extra parameters (Windows attrib)
+ * secaudit perms file
+ * set the security parameters of file to perms (mode or acl)
+ * secaudit -r[v] perms directory
+ * set the security parameters of files in directory to perms
+ */
+
+/* History
+ *
+ * Nov 2007
+ * - first version, by concatenating miscellaneous utilities
+ *
+ * Jan 2008, version 1.0.1
+ * - fixed mode displaying
+ * - added a global severe errors count
+ *
+ * Feb 2008, version 1.0.2
+ * - implemented conversions for big-endian machines
+ *
+ * Mar 2008, version 1.0.3
+ * - avoided consistency checks on $Secure when there is no such file
+ *
+ * Mar 2008, version 1.0.4
+ * - changed ordering of ACE's
+ * - changed representation for special flags
+ * - defaulted to stdin for option -h
+ * - added self tests (compile time option)
+ * - fixed errors specific to big-endian computers
+ *
+ * Apr 2008, version 1.1.0
+ * - developped Posix ACLs to NTFS ACLs conversions
+ * - developped NTFS ACLs backup and restore
+ *
+ * Apr 2008, version 1.1.1
+ * - fixed an error specific to big-endian computers
+ * - checked hash value and fixed error report in restore
+ * - improved display in showhex() and restore()
+ *
+ * Apr 2008, version 1.1.2
+ * - improved and fixed Posix ACLs to NTFS ACLs conversions
+ *
+ * Apr 2008, version 1.1.3
+ * - reenabled recursion for setting a new mode or ACL
+ * - processed Unicode file names and displayed them as UTF-8
+ * - allocated dynamically memory for file names when recursing
+ *
+ * May 2008, version 1.1.4
+ * - all Unicode/UTF-8 strings checked and processed
+ *
+ * Jul 2008, version 1.1.5
+ * - made Windows owner consistent with Linux owner when changing mode
+ * - allowed owner change on Windows when it does not match Linux owner
+ * - skipped currently unused code
+ *
+ * Aug 2008, version 1.2.0
+ * - processed \.NTFS-3G\UserMapping on Windows
+ * - made use of user mappings through the security API or direct access
+ * - fixed a bug in restore
+ * - fixed UTF-8 conversions
+ *
+ * Sep 2008, version 1.3.0
+ * - split the code to have part of it shared with ntfs-3g library
+ * - fixed testing for end of SDS block
+ * - added samples checking in selftest for easier debugging
+ *
+ * Oct 2008, version 1.3.1
+ * - fixed outputting long long data when testing on a Palm organizer
+ *
+ * Dec 2008, version 1.3.2
+ * - fixed restoring ACLs
+ * - added optional logging of ACL hashes to facilitate restore checks
+ * - fixed collecting SACLs
+ * - fixed setting special control flags
+ * - fixed clearing existing SACLs (Linux only) and DACLs
+ * - changed the sequencing of items when quering a security descriptor
+ * - avoided recursing on junctions and symlinks on Windows
+ *
+ * Jan 2009, version 1.3.3
+ * - save/restore Windows attributes (code from Faisal)
+ *
+ * Mar 2009, version 1.3.4
+ * - enabled displaying attributes of a mounted file over Linux
+ *
+ * Apr 2009, version 1.3.5
+ * - fixed initialisation of stand-alone user mapping
+ * - fixed POSIXACL redefinition when included in the ntfs-3g package
+ * - fixed displaying of options
+ * - fixed a dependency on the shared library version used
+ *
+ * May 2009, version 1.3.6
+ * - added new samples for self testing
+ *
+ * Jun 2009, version 1.3.7
+ * - fixed displaying owner and group of a mounted file over Linux
+ *
+ * Jul 2009, version 1.3.8
+ * - fixed again displaying owner and group of a mounted file over Linux
+ * - cleaned some code to avoid warnings
+ *
+ * Nov 2009, version 1.3.9
+ * - allowed security descriptors up to 64K
+ *
+ * Nov 2009, version 1.3.10
+ * - applied patches for MacOSX from Erik Larsson
+ *
+ * Nov 2009, version 1.3.11
+ * - replace <attr/xattr.h> by <sys/xattr.h> (provided by glibc)
+ *
+ * Dec 2009, version 1.3.12
+ * - worked around "const" possibly redefined in config.h
+ *
+ * Dec 2009, version 1.3.13
+ * - fixed the return code of dorestore()
+ *
+ * Dec 2009, version 1.3.14
+ * - adapted to opensolaris
+ *
+ * Jan 2010, version 1.3.15
+ * - more adaptations to opensolaris
+ * - removed the fix for return code of dorestore()
+ *
+ * Jan 2010, version 1.3.16
+ * - repeated the fix for return code of dorestore()
+ *
+ * Mar 2010, version 1.3.17
+ * - adapted to new default user mapping
+ * - fixed #ifdef'd code for selftest
+ *
+ * May 2010, version 1.3.18
+ * - redefined early error logging
+ *
+ * Mar 2011, version 1.3.19
+ * - fixed interface to ntfs_initialize_file_security()
+ *
+ * Apr 2011, version 1.3.20
+ * - fixed false memory leak detection
+ *
+ * Jun 2011, version 1.3.21
+ * - cleaned a few unneeded variables
+ *
+ * Nov 2011, version 1.3.22
+ * - added a distinctive prefix to owner and group SID
+ * - fixed a false memory leak detection
+ */
+
+/*
+ * 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * General parameters which may have to be adapted to needs
+ */
+
+#define AUDT_VERSION "1.3.22"
+
+#define GET_FILE_SECURITY "ntfs_get_file_security"
+#define SET_FILE_SECURITY "ntfs_set_file_security"
+#define GET_FILE_ATTRIBUTES "ntfs_get_file_attributes"
+#define SET_FILE_ATTRIBUTES "ntfs_set_file_attributes"
+#define READ_DIRECTORY "ntfs_read_directory"
+#define READ_SDS "ntfs_read_sds"
+#define READ_SII "ntfs_read_sii"
+#define READ_SDH "ntfs_read_sdh"
+#define GET_USER "ntfs_get_user"
+#define GET_GROUP "ntfs_get_group"
+#define GET_USID "ntfs_get_usid"
+#define GET_GSID "ntfs_get_gsid"
+#define INIT_FILE_SECURITY "ntfs_initialize_file_security"
+#define LEAVE_FILE_SECURITY "ntfs_leave_file_security"
+
+/*
+ * External declarations
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdarg.h>
+
+ /*
+ * integration into secaudit, check whether Win32,
+ * may have to be adapted to compiler or something else
+ */
+
+#ifndef WIN32
+#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC)
+#define WIN32 1
+#endif
+#endif
+
+ /*
+ * integration into secaudit/Win32
+ */
+#ifdef WIN32
+#include <windows.h>
+#define __LITTLE_ENDIAN 1234
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#else
+ /*
+ * integration into secaudit/STSC
+ */
+#ifdef STSC
+#include <stat.h>
+#undef __BYTE_ORDER
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+ /*
+ * integration into secaudit/Linux
+ */
+
+#include <sys/stat.h>
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#include <unistd.h>
+#include <dlfcn.h>
+#endif /* STSC */
+#endif /* WIN32 */
+#include "secaudit.h"
+
+#ifndef WIN32
+
+#ifndef STSC
+
+#if !defined(HAVE_CONFIG_H) && POSIXACLS
+ /* require <sys/xattr.h> if not integrated into ntfs-3g package */
+#define HAVE_SETXATTR 1
+#endif
+
+#ifdef HAVE_CONFIG_H
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS /* work around "_FILE_OFFSET_BITS" possibly already defined */
+#endif
+ /* <sys/xattr.h> according to config.h if integrated into ntfs-3g package */
+#include "config.h"
+#ifdef const /* work around "const" possibly redefined in config.h */
+#undef const
+#endif
+#ifndef POSIXACLS
+#define POSIXACLS 0
+#endif
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#else
+#warning "The extended attribute package is not available"
+#endif /* HAVE_SETXATTR */
+
+#endif /* STSC */
+
+#define MS_NONE 0 /* no flag for mounting the device */
+#define MS_RDONLY 1 /* flag for mounting the device read-only */
+
+struct CALLBACK;
+
+typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname,
+ int length, int type, long long pos, u64 mft_ref,
+ unsigned int dt_type);
+
+#ifndef HAVE_SYSLOG_H
+void ntfs_log_early_error(const char *format, ...)
+ __attribute__((format(printf, 1, 2)));
+#endif
+
+#if USESTUBS | defined(STSC)
+
+int ntfs_get_file_security(void *scapi,
+ const char *path, DWORD selection,
+ char *buf, DWORD buflen, LPDWORD psize);
+BOOL ntfs_set_file_security(void *scapi,
+ const char *path, DWORD selection, const char *attr);
+int ntfs_get_file_attributes(void *scapi,
+ const char *path);
+BOOL ntfs_set_file_attributes(void *scapi,
+ const char *path, DWORD attrib);
+BOOL ntfs_read_directory(void *scapi,
+ const char *path, dircallback callback, void *context);
+int ntfs_read_sds(void *scapi,
+ char *buf, DWORD buflen, DWORD offset);
+void *ntfs_read_sii(void *scapi, void *entry);
+void *ntfs_read_sdh(void *scapi, void *entry);
+
+int ntfs_get_usid(void *scapi, uid_t uid, char *buf);
+int ntfs_get_gsid(void *scapi, gid_t gid, char *buf);
+int ntfs_get_user(void *scapi, const char *usid);
+int ntfs_get_group(void *scapi, const char *gsid);
+
+void *ntfs_initialize_file_security(const char *device, unsigned long flags);
+BOOL ntfs_leave_file_security(void *scapi);
+
+#else
+
+typedef int (*type_get_file_security)(void *scapi,
+ const char *path, DWORD selection,
+ char *buf, DWORD buflen, LPDWORD psize);
+typedef BOOL (*type_set_file_security)(void *scapi,
+ const char *path, DWORD selection, const char *attr);
+typedef int (*type_get_file_attributes)(void *scapi,
+ const char *path);
+typedef BOOL (*type_set_file_attributes)(void *scapi,
+ const char *path, DWORD attrib);
+typedef BOOL (*type_read_directory)(void *scapi,
+ const char *path, dircallback callback, void *context);
+typedef int (*type_read_sds)(void *scapi,
+ char *buf, DWORD buflen, DWORD offset);
+typedef void *(*type_read_sii)(void *scapi, void *entry);
+typedef void *(*type_read_sdh)(void *scapi, void *entry);
+
+typedef int (*type_get_usid)(void *scapi, uid_t uid, char *buf);
+typedef int (*type_get_gsid)(void *scapi, gid_t gid, char *buf);
+typedef int (*type_get_user)(void *scapi, const char *usid);
+typedef int (*type_get_group)(void *scapi, const char *gsid);
+
+typedef void *(*type_initialize_file_security)(const char *device,
+ unsigned long flags);
+typedef BOOL (*type_leave_file_security)(void *scapi);
+
+type_get_file_security ntfs_get_file_security;
+type_set_file_security ntfs_set_file_security;
+type_get_file_attributes ntfs_get_file_attributes;
+type_set_file_attributes ntfs_set_file_attributes;
+type_read_directory ntfs_read_directory;
+type_read_sds ntfs_read_sds;
+type_read_sii ntfs_read_sii;
+type_read_sdh ntfs_read_sdh;
+
+type_get_usid ntfs_get_usid;
+type_get_gsid ntfs_get_gsid;
+type_get_user ntfs_get_user;
+type_get_group ntfs_get_group;
+
+type_initialize_file_security ntfs_initialize_file_security;
+type_leave_file_security ntfs_leave_file_security;
+
+
+#endif /* USESTUBS | defined(STSC) */
+#endif /* WIN32 */
+
+/*
+ * Prototypes for local functions
+ */
+
+BOOL open_security_api(void);
+BOOL close_security_api(void);
+#ifndef WIN32
+BOOL open_volume(const char*, unsigned long flags);
+BOOL close_volume(const char*);
+#endif
+unsigned int get2l(const char*, int);
+unsigned long get4l(const char*, int);
+u64 get6l(const char*, int);
+u64 get6h(const char*, int);
+u64 get8l(const char*, int);
+void set2l(char*, unsigned int);
+void set4l(char*, unsigned long);
+void hexdump(const char*, int, int);
+u32 hash(const le32*, int);
+unsigned int utf8size(const char*, int);
+unsigned int makeutf8(char*, const char*, int);
+unsigned int utf16size(const char*);
+unsigned int makeutf16(char*, const char*);
+unsigned int utf16len(const char*);
+void printname(FILE*, const char*);
+void printerror(FILE*);
+BOOL guess_dir(const char*);
+void showsid(const char*, int, const char*, int);
+void showusid(const char*, int);
+void showgsid(const char*, int);
+void showheader(const char*, int);
+void showace(const char*, int, int, int);
+void showacl(const char*, int, int, int);
+void showdacl(const char*, int, int);
+void showsacl(const char*, int, int);
+void showall(const char*, int);
+void showposix(const struct POSIX_SECURITY*);
+int linux_permissions(const char*, BOOL);
+uid_t linux_owner(const char*);
+gid_t linux_group(const char*);
+int basicread(void*, char*, size_t, off_t);
+int dummyread(void*, char*, size_t, off_t);
+int local_build_mapping(struct MAPPING *[], const char*);
+void newblock(s32);
+void freeblocks(void);
+u32 getmsbhex(const char*);
+u32 getlsbhex(const char*);
+BOOL ishexdump(const char*, int, int);
+void showhex(FILE*);
+void showfull(const char*, BOOL);
+BOOL applyattr(const char*, const char*, BOOL, int, s32);
+BOOL restore(FILE*);
+BOOL dorestore(const char*, FILE*);
+u32 merge_rights(const struct POSIX_SECURITY*, BOOL);
+void tryposix(struct POSIX_SECURITY*);
+void check_samples(void);
+void basictest(int, BOOL, const SID*, const SID*);
+void posixtest(int, BOOL, const SID*, const SID*);
+void selftests(void);
+unsigned int getfull(char*, const char*);
+BOOL updatefull(const char *name, DWORD flags, char *attr);
+BOOL setfull(const char*, int, BOOL);
+BOOL singleshow(const char*);
+void showmounted(const char*);
+BOOL recurseshow(const char*);
+BOOL singleset(const char*, int);
+BOOL recurseset(const char*, int);
+#ifdef WIN32
+BOOL backup(const char*);
+BOOL listfiles(const char*);
+#if POSIXACLS
+BOOL iterate(RECURSE, const char*, const struct POSIX_SECURITY*);
+#else
+BOOL iterate(RECURSE, const char*, mode_t);
+#endif
+#else
+BOOL backup(const char*, const char*);
+BOOL listfiles(const char*, const char*);
+#endif
+#if POSIXACLS
+BOOL setfull_posix(const char *, const struct POSIX_SECURITY*, BOOL);
+struct POSIX_SECURITY *linux_permissions_posix(const char*, BOOL);
+BOOL recurseset_posix(const char*, const struct POSIX_SECURITY*);
+BOOL singleset_posix(const char*, const struct POSIX_SECURITY*);
+struct POSIX_SECURITY *encode_posix_acl(const char*);
+#endif
+static void *stdmalloc(size_t);
+static void stdfree(void*);
+
+BOOL valid_sds(const char*, unsigned int, unsigned int,
+ unsigned int, u32, BOOL);
+BOOL valid_sii(const char*, u32);
+BOOL valid_sdh(const char*, u32, u32);
+int consist_sds(const char*, unsigned int, unsigned int, BOOL);
+int consist_sii(const char*);
+int consist_sdh(const char*);
+int audit_sds(BOOL);
+int audit_sii(void);
+int audit_sdh(void);
+void audit_summary(void);
+BOOL audit(const char*);
+int getoptions(int, char*[]);
+
+#ifndef WIN32
+
+/*
+ * Structures for collecting directory contents (Linux only)
+ */
+
+struct LINK {
+ struct LINK *next;
+ char name[1];
+} ;
+
+struct CALLBACK {
+ struct LINK *head;
+ const char *dir;
+} ;
+
+int callback(struct CALLBACK *context, char *ntfsname,
+ int length, int type, long long pos, u64 mft_ref,
+ unsigned int dt_type);
+#endif
+
+/*
+ * Global constants
+ */
+
+#define BANNER "secaudit " AUDT_VERSION " : NTFS security data auditing"
+
+#if SELFTESTS & !USESTUBS
+
+/*
+ * Dummy mapping file (self tests only)
+ */
+
+#define DUMMYAUTH "S-1-5-21-3141592653-589793238-462843383-"
+char dummymapping[] = "500::" DUMMYAUTH "1000\n"
+ "501::" DUMMYAUTH "1001\n"
+ "502::" DUMMYAUTH "1002\n"
+ "503::" DUMMYAUTH "1003\n"
+ "516::" DUMMYAUTH "1016\n"
+ ":500:" DUMMYAUTH "513\r\n"
+ ":511:S-1-5-21-1607551490-981732888-1819828000-513\n"
+ ":516:" DUMMYAUTH "1012\r\n"
+ "::" DUMMYAUTH "10000\n";
+
+/*
+ * SID for world (S-1-1-0)
+ */
+
+static const char worldsidbytes[] = {
+ 1, /* revision */
+ 1, /* auth count */
+ 0, 0, 0, 0, 0, 1, /* base */
+ 0, 0, 0, 0 /* 1st level */
+} ;
+static const SID *worldsid = (const SID*)worldsidbytes;
+
+/*
+ * SID for administrator
+ */
+
+static const char adminsidbytes[] = {
+ 1, /* revision */
+ 2, /* auth count */
+ 0, 0, 0, 0, 0, 5, /* base */
+ 32, 0, 0, 0, /* 1st level */
+ 32, 2, 0, 0 /* 2nd level */
+};
+
+static const SID *adminsid = (const SID*)adminsidbytes;
+
+/*
+ * SID for system
+ */
+
+static const char systemsidbytes[] = {
+ 1, /* revision */
+ 1, /* auth count */
+ 0, 0, 0, 0, 0, 5, /* base */
+ 18, 0, 0, 0 /* 1st level */
+ };
+
+static const SID *systemsid = (const SID*)systemsidbytes;
+
+#endif
+
+/*
+ * Global variables
+ */
+
+BOOL opt_a; /* audit security data */
+BOOL opt_b; /* backup NTFS ACLs */
+BOOL opt_e; /* restore extra (currently windows attribs) */
+BOOL opt_h; /* display an hexadecimal descriptor in a file */
+BOOL opt_r; /* recursively apply to subdirectories */
+BOOL opt_s; /* restore NTFS ACLs */
+#if SELFTESTS & !USESTUBS
+BOOL opt_t; /* run self-tests */
+#endif
+int opt_v; /* verbose or very verbose*/
+struct SECURITY_DATA *securdata[(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ)];
+#if FORCEMASK
+BOOL opt_m; /* force a mask - dangerous */
+u32 forcemsk /* mask to force */
+#endif
+unsigned int errors; /* number of severe errors */
+unsigned int warnings; /* number of non-severe errors */
+
+struct CHKALLOC *firstalloc;
+struct SECURITY_CONTEXT context;
+MAPTYPE mappingtype;
+
+#ifdef STSC
+#define static
+#endif
+
+#ifndef WIN32
+
+void *ntfs_handle;
+void *ntfs_context = (void*)NULL;
+
+/*
+ * Open and close the security API (platform dependent)
+ */
+
+BOOL open_security_api(void)
+{
+#if USESTUBS | defined(STSC)
+ return (TRUE);
+#else
+ char *error;
+ BOOL err;
+ const char *libfile;
+
+ err = TRUE;
+ libfile = getenv(ENVNTFS3G);
+ if (!libfile)
+ libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE);
+ ntfs_handle = dlopen(libfile,RTLD_LAZY);
+ if (ntfs_handle) {
+ ntfs_initialize_file_security = (type_initialize_file_security)
+ dlsym(ntfs_handle,INIT_FILE_SECURITY);
+ error = dlerror();
+ if (error)
+ fprintf(stderr," %s\n",error);
+ else {
+ ntfs_leave_file_security = (type_leave_file_security)
+ dlsym(ntfs_handle,LEAVE_FILE_SECURITY);
+ ntfs_get_file_security = (type_get_file_security)
+ dlsym(ntfs_handle,GET_FILE_SECURITY);
+ ntfs_set_file_security = (type_set_file_security)
+ dlsym(ntfs_handle,SET_FILE_SECURITY);
+ ntfs_get_file_attributes = (type_get_file_attributes)
+ dlsym(ntfs_handle,GET_FILE_ATTRIBUTES);
+ ntfs_set_file_attributes = (type_set_file_attributes)
+ dlsym(ntfs_handle,SET_FILE_ATTRIBUTES);
+ ntfs_read_directory = (type_read_directory)
+ dlsym(ntfs_handle,READ_DIRECTORY);
+ ntfs_read_sds = (type_read_sds)
+ dlsym(ntfs_handle,READ_SDS);
+ ntfs_read_sii = (type_read_sii)
+ dlsym(ntfs_handle,READ_SII);
+ ntfs_read_sdh = (type_read_sdh)
+ dlsym(ntfs_handle,READ_SDH);
+ ntfs_get_user = (type_get_user)
+ dlsym(ntfs_handle,GET_USER);
+ ntfs_get_group = (type_get_group)
+ dlsym(ntfs_handle,GET_GROUP);
+ ntfs_get_usid = (type_get_usid)
+ dlsym(ntfs_handle,GET_USID);
+ ntfs_get_gsid = (type_get_gsid)
+ dlsym(ntfs_handle,GET_GSID);
+ err = !ntfs_initialize_file_security
+ || !ntfs_leave_file_security
+ || !ntfs_get_file_security
+ || !ntfs_set_file_security
+ || !ntfs_get_file_attributes
+ || !ntfs_set_file_attributes
+ || !ntfs_read_directory
+ || !ntfs_read_sds
+ || !ntfs_read_sii
+ || !ntfs_read_sdh
+ || !ntfs_get_user
+ || !ntfs_get_group
+ || !ntfs_get_usid
+ || !ntfs_get_gsid;
+
+ if (error)
+ fprintf(stderr,"ntfs-3g API not available\n");
+ }
+ } else {
+ fprintf(stderr,"Could not open ntfs-3g library\n");
+ fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n");
+ fprintf(stderr,"to appropriate path and retry\n");
+ }
+ return (!err);
+#endif /* USESTUBS | defined(STSC) */
+}
+
+BOOL close_security_api(void)
+{
+#if USESTUBS | defined(STSC)
+ return (0);
+#else
+ return (!dlclose(ntfs_handle));
+#endif /* USESTUBS | defined(STSC) */
+}
+
+/*
+ * Open and close a volume (platform dependent)
+ * Assumes a single volume is opened
+ */
+
+BOOL open_volume(const char *volume, unsigned long flags)
+{
+ BOOL ok;
+
+ ok = FALSE;
+ if (!ntfs_context) {
+ ntfs_context = (*ntfs_initialize_file_security)(volume,flags);
+ if (ntfs_context) {
+ if (*(u32*)ntfs_context != MAGIC_API) {
+ fprintf(stderr,"Versions of ntfs-3g and secaudit"
+ " are not compatible\n");
+ } else {
+ fprintf(stderr,"\"%s\" opened\n",volume);
+ mappingtype = MAPEXTERN;
+ ok = TRUE;
+ }
+ } else {
+ fprintf(stderr,"Could not open \"%s\"\n",volume);
+ fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume);
+ }
+ } else
+ fprintf(stderr,"A volume is already open\n");
+ return (ok);
+}
+
+BOOL close_volume(const char *volume)
+{
+ BOOL r;
+
+ r = (*ntfs_leave_file_security)(ntfs_context);
+ if (r)
+ fprintf(stderr,"\"%s\" closed\n",volume);
+ else
+ fprintf(stderr,"Could not close \"%s\"\n",volume);
+ ntfs_context = (void*)NULL;
+ return (r);
+}
+
+#endif /* WIN32 */
+
+/*
+ * Extract small or big endian data from an array of bytes
+ */
+
+unsigned int get2l(const char *attr, int p)
+{
+ int i;
+ unsigned int v;
+
+ v = 0;
+ for (i=0; i<2; i++)
+ v += (attr[p+i] & 255) << (8*i);
+ return (v);
+}
+
+unsigned long get4l(const char *attr, int p)
+{
+ int i;
+ unsigned long v;
+
+ v = 0;
+ for (i=0; i<4; i++)
+ v += ((long)(attr[p+i] & 255)) << (8*i);
+ return (v);
+}
+
+u64 get6l(const char *attr, int p)
+{
+ int i;
+ u64 v;
+
+ v = 0;
+ for (i=0; i<6; i++)
+ v += ((long long)(attr[p+i] & 255)) << (8*i);
+ return (v);
+}
+
+u64 get6h(const char *attr, int p)
+{
+ int i;
+ u64 v;
+
+ v = 0;
+ for (i=0; i<6; i++)
+ v = (v << 8) + (attr[p+i] & 255);
+ return (v);
+}
+
+u64 get8l(const char *attr, int p)
+{
+ int i;
+ u64 v;
+
+ v = 0;
+ for (i=0; i<8; i++)
+ v += ((long long)(attr[p+i] & 255)) << (8*i);
+ return (v);
+}
+
+/*
+ * Set small or big endian data into an array of bytes
+ */
+
+void set2l(char *p, unsigned int v)
+{
+ int i;
+
+ for (i=0; i<2; i++)
+ p[i] = ((v >> 8*i) & 255);
+}
+
+void set4l(char *p, unsigned long v)
+{
+ int i;
+
+ for (i=0; i<4; i++)
+ p[i] = ((v >> 8*i) & 255);
+}
+
+
+/*
+ * hexadecimal dump of an array of bytes
+ */
+
+void hexdump(const char *attr, int size, int level)
+{
+ int i,j;
+
+ for (i=0; i<size; i+=16) {
+ if (level)
+ printf("%*c",level,' ');
+ printf("%06x ",i);
+ for (j=i; (j<(i+16)) && (j<size); j++)
+ printf((j & 3 ? "%02x" : " %02x"),attr[j] & 255);
+ printf("\n");
+ }
+}
+
+u32 hash(const le32 *buf, int size /* bytes */)
+{
+ u32 h;
+ int i;
+
+ h = 0;
+ for (i=0; 4*i<size; i++)
+ h = le32_to_cpu(buf[i]) + (h << 3) + ((h >> 29) & 7);
+ return (h);
+}
+
+/*
+ * Evaluate the size of UTS-8 conversion of a UTF-16LE text
+ * trailing '\0' not accounted for
+ * Returns 0 for invalid input
+ */
+
+unsigned int utf8size(const char *utf16, int length)
+{
+ int i;
+ unsigned int size;
+ enum { BASE, SURR, ERR } state;
+
+ size = 0;
+ state = BASE;
+ for (i=0; i<2*length; i+=2) {
+ switch (state) {
+ case BASE :
+ if (utf16[i+1] & 0xf8) {
+ if ((utf16[i+1] & 0xf8) == 0xd8)
+ state = (utf16[i+1] & 4 ? ERR : SURR);
+ else
+#if NOREVBOM
+ if (((utf16[i+1] & 0xff) == 0xff)
+ && ((utf16[i] & 0xfe) == 0xfe))
+ state = ERR;
+ else
+ size += 3;
+#else
+ size += 3;
+#endif
+ } else
+ if ((utf16[i] & 0x80) || utf16[i+1])
+ size += 2;
+ else
+ size++;
+ break;
+ case SURR :
+ if ((utf16[i+1] & 0xfc) == 0xdc) {
+ state = BASE;
+ size += 4;
+ } else
+ state = ERR;
+ break;
+ case ERR :
+ break;
+ }
+ }
+ if (state != BASE)
+ size = 0;
+ return (size);
+}
+
+/*
+ * Convert a UTF-16LE text to UTF-8
+ * Note : wcstombs() not used because on Linux it fails for characters
+ * not present in current locale
+ * Returns size or zero for invalid input
+ */
+
+unsigned int makeutf8(char *utf8, const char *utf16, int length)
+{
+ int i;
+ unsigned int size;
+ unsigned int rem;
+ enum { BASE, SURR, ERR } state;
+
+ size = 0;
+ rem = 0;
+ state = BASE;
+ for (i=0; i<2*length; i+=2) {
+ switch (state) {
+ case BASE :
+ if (utf16[i+1] & 0xf8) {
+ if ((utf16[i+1] & 0xf8) == 0xd8) {
+ if (utf16[i+1] & 4)
+ state = ERR;
+ else {
+ utf8[size++] = 0xf0 + (utf16[i+1] & 7)
+ + ((utf16[i] & 0xc0) == 0xc0);
+ utf8[size++] = 0x80 + (((utf16[i] + 64) >> 2) & 63);
+ rem = utf16[i] & 3;
+ state = SURR;
+ }
+ } else {
+#if NOREVBOM
+ if (((utf16[i+1] & 0xff) == 0xff)
+ && ((utf16[i] & 0xfe) == 0xfe))
+ state = ERR;
+ else {
+ utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15);
+ utf8[size++] = 0x80
+ + ((utf16[i+1] & 15) << 2)
+ + ((utf16[i] >> 6) & 3);
+ utf8[size++] = 0x80 + (utf16[i] & 63);
+ }
+#else
+ utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15);
+ utf8[size++] = 0x80
+ + ((utf16[i+1] & 15) << 2)
+ + ((utf16[i] >> 6) & 3);
+ utf8[size++] = 0x80 + (utf16[i] & 63);
+#endif
+ }
+ } else
+ if ((utf16[i] & 0x80) || utf16[i+1]) {
+ utf8[size++] = 0xc0
+ + ((utf16[i+1] & 15) << 2)
+ + ((utf16[i] >> 6) & 3);
+ utf8[size++] = 0x80 + (utf16[i] & 63);
+ } else
+ utf8[size++] = utf16[i];
+ break;
+ case SURR :
+ if ((utf16[i+1] & 0xfc) == 0xdc) {
+ utf8[size++] = 0x80 + (rem << 4)
+ + ((utf16[i+1] & 3) << 2)
+ + ((utf16[i] >> 6) & 3);
+ utf8[size++] = 0x80 + (utf16[i] & 63);
+ state = BASE;
+ } else
+ state = ERR;
+ break;
+ case ERR :
+ break;
+ }
+ }
+ utf8[size] = 0;
+ if (state != BASE)
+ state = ERR;
+ return (state == ERR ? 0 : size);
+}
+
+#ifdef WIN32
+
+/*
+ * Evaluate the size of UTF-16LE conversion of a UTF-8 text
+ * (basic conversions only)
+ * trailing '\0' not accounted for
+ */
+
+unsigned int utf16size(const char *utf8)
+{
+ unsigned int size;
+ const char *p;
+ int c;
+ unsigned int code;
+ enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, ERR } state;
+
+ p = utf8;
+ size = 0;
+ state = BASE;
+ while (*p) {
+ c = *p++ & 255;
+ switch (state) {
+ case BASE :
+ if (!(c & 0x80))
+ size++;
+ else
+ if (c < 0xc2)
+ state = ERR;
+ else
+ if (c < 0xe0)
+ state = TWO;
+ else
+ if (c < 0xf0) {
+ if (c == 0xe0)
+ state = THREE2;
+ else
+ if (c == 0xed)
+ state = THREE3;
+ else
+ state = THREE;
+ } else
+ if (c < 0xf8) {
+ state = FOUR;
+ code = c & 7;
+ } else
+ state = ERR;
+ break;
+ case TWO :
+ if ((c & 0xc0) != 0x80)
+ state = ERR;
+ else {
+ size++;
+ state = BASE;
+ }
+ break;
+ case THREE :
+ if ((c & 0xc0) != 0x80)
+ state = ERR;
+ else
+ state = TWO;
+ break;
+ case THREE2 :
+ if ((c & 0xe0) != 0xa0)
+ state = ERR;
+ else
+ state = TWO;
+ break;
+ case THREE3 :
+ if ((c & 0xe0) != 0x80)
+ state = ERR;
+ else
+ state = TWO;
+ break;
+ case FOUR :
+ if ((((code << 6) + (c & 63)) > 0x10f)
+ || (((code << 6) + (c & 63)) < 0x10))
+ state = ERR;
+ else {
+ size++;
+ state = THREE;
+ }
+ break;
+ case ERR :
+ break;
+ }
+ }
+ if (state != BASE) size = 0;
+ return (size);
+}
+
+/*
+ * Convert a UTF8 text to UTF-16LE
+ * (basic conversions only)
+ * Note : mbstowcs() not used because on Linux it fails for characters
+ * not present in current locale
+ */
+
+unsigned int makeutf16(char *target, const char *utf8)
+{
+ unsigned int size;
+ unsigned int code;
+ const char *p;
+ int c;
+ enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, FOUR2, FOUR3, ERR } state;
+
+ p = utf8;
+ size = 0;
+ c = 0;
+ state = BASE;
+ while (*p) {
+ c = *p++ & 255;
+ switch (state) {
+ case BASE :
+ if (!(c & 0x80)) {
+ target[2*size] = c;
+ target[2*size + 1] = 0;
+ size++;
+ } else {
+ if (c < 0xc2)
+ state = ERR;
+ else
+ if (c < 0xe0) {
+ code = c & 31;
+ state = TWO;
+ } else
+ if (c < 0xf0) {
+ code = c & 15;
+ if (c == 0xe0)
+ state = THREE2;
+ else
+ if (c == 0xed)
+ state = THREE3;
+ else
+ state = THREE;
+ } else
+ if (c < 0xf8) {
+ code = c & 7;
+ state = FOUR;
+ } else
+ state = ERR;
+ }
+ break;
+ case TWO :
+#if NOREVBOM
+ if (((c & 0xc0) != 0x80)
+ || ((code == 0x3ff) && (c >= 0xbe)))
+#else
+ if ((c & 0xc0) != 0x80)
+#endif
+ state = ERR;
+ else {
+ target[2*size] = ((code & 3) << 6) + (c & 63);
+ target[2*size + 1] = ((code >> 2) & 255);
+ size++;
+ state = BASE;
+ }
+ break;
+ case THREE :
+ if ((c & 0xc0) != 0x80)
+ state = ERR;
+ else {
+ code = ((code & 15) << 6) + (c & 63);
+ state = TWO;
+ }
+ break;
+ case THREE2 :
+ if ((c & 0xe0) != 0xa0)
+ state = ERR;
+ else {
+ code = ((code & 15) << 6) + (c & 63);
+ state = TWO;
+ }
+ break;
+ case THREE3 :
+ if ((c & 0xe0) != 0x80)
+ state = ERR;
+ else {
+ code = ((code & 15) << 6) + (c & 63);
+ state = TWO;
+ }
+ break;
+ case FOUR :
+ if ((c & 0xc0) != 0x80)
+ state = ERR;
+ else {
+ code = (code << 6) + (c & 63);
+ state = FOUR2;
+ }
+ break;
+ case FOUR2 :
+ if ((c & 0xc0) != 0x80)
+ state = ERR;
+ else {
+ code = (code << 6) + (c & 63);
+ state = FOUR3;
+ }
+ break;
+ case FOUR3 :
+ if ((code > 0x43ff)
+ || (code < 0x400)
+ || ((c & 0xc0) != 0x80))
+ state = ERR;
+ else {
+ target[2*size] = ((code - 0x400) >> 4) & 255;
+ target[2*size+1] = 0xd8 + (((code - 0x400) >> 12) & 3);
+ target[2*size+2] = ((code & 3) << 6) + (c & 63);
+ target[2*size+3] = 0xdc + ((code >> 2) & 3);
+ size += 2;
+ state = BASE;
+ }
+ break;
+ case ERR :
+ break;
+ }
+ }
+ if (state != BASE)
+ size = 0;
+ target[2*size] = 0;
+ target[2*size + 1] = 0;
+ return (size);
+}
+
+unsigned int utf16len(const char *str)
+{
+ unsigned int len;
+
+ len = 0;
+ while (str[2*len] || str[2*len+1]) len++;
+ return (len);
+}
+
+#endif
+
+/*
+ * Print a file name
+ * on Windows it prints UTF-16LE names as UTF-8
+ */
+
+void printname(FILE *file, const char *name)
+{
+#ifdef WIN32
+ char utf8name[MAXFILENAME];
+
+ makeutf8(utf8name,name,utf16len(name));
+ fprintf(file,"%s",utf8name);
+#else
+ fprintf(file,"%s",name);
+#endif
+}
+
+/*
+ * Print the last error code
+ */
+
+void printerror(FILE *file)
+{
+#ifdef WIN32
+ int err;
+ const char *txt;
+
+ err = GetLastError();
+ switch (err) {
+ case 5 :
+ txt = "Access to security descriptor was denied";
+ break;
+ case 1307 :
+ txt = "This SID may not be assigned as the owner of this object";
+ break;
+ case 1308 :
+ txt = "This SID may not be assigned as the group of this object";
+ break;
+ case 1314 :
+ txt = "You do not have the privilege to change this SID";
+ break;
+ default :
+ txt = (const char*)NULL;
+ break;
+ }
+ if (txt)
+ fprintf(file,"Error %d : %s\n",err,txt);
+ else
+ fprintf(file,"Error %d\n",err);
+#else
+#ifdef STSC
+ if (errno) fprintf(file,"Error code %d\n",errno);
+#else
+ if (errno) fprintf(file,"Error code %d : %s\n",errno,strerror(errno));
+#endif
+#endif
+}
+
+#ifndef HAVE_SYSLOG_H
+
+/*
+ * Redefine early error messages in stand-alone situations
+ */
+
+void ntfs_log_early_error(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vfprintf(stderr,format,args);
+ va_end(args);
+}
+
+#endif
+
+/*
+ * Guess whether a security attribute is intended for a directory
+ * based on the presence of inheritable ACE
+ * (not 100% reliable)
+ */
+
+BOOL guess_dir(const char *attr)
+{
+ int off;
+ int isdir;
+ int cnt;
+ int i;
+ int x;
+
+ isdir = 0;
+ off = get4l(attr,16);
+ if (off) {
+ cnt = get2l(attr,off+4);
+ x = 8;
+ for (i=0; i<cnt; i++) {
+ if (attr[off + x + 1] & 3)
+ isdir = 1;
+ x += get2l(attr,off + x + 2);
+ }
+ }
+ return (isdir);
+}
+
+/*
+ * Display a SID
+ * See http://msdn2.microsoft.com/en-us/library/aa379649.aspx
+ */
+
+void showsid(const char *attr, int off, const char *prefix, int level)
+{
+ int cnt;
+ int i;
+ BOOL known;
+ u64 auth;
+ unsigned long first;
+ unsigned long second;
+ unsigned long last;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ cnt = attr[off+1] & 255;
+ auth = get6h(attr,off+2);
+ first = get4l(attr,off+8);
+ known = FALSE;
+ if ((attr[off] == 1) /* revision */
+ && (auth < 100))
+ switch (cnt) {
+ case 0 : /* no level (error) */
+ break;
+ case 1 : /* single level */
+ switch (auth) {
+ case 0 :
+ if (first == 0) {
+ known = TRUE;
+ printf("%*cNull SID\n",-level,marker);
+ }
+ break;
+ case 1 :
+ if (first == 0) {
+ known = TRUE;
+ printf("%*cWorld SID\n",-level,marker);
+ }
+ break;
+ case 3 :
+ switch (first) {
+ case 0 :
+ known = TRUE;
+ printf("%*cCreator owner SID\n",-level,marker);
+ break;
+ case 1 :
+ known = TRUE;
+ printf("%*cCreator group SID\n",-level,marker);
+ break;
+ }
+ break;
+ case 5 :
+ switch (first) {
+ case 7 :
+ known = TRUE;
+ printf("%*cAnonymous logon SID\n",-level,marker);
+ break;
+ case 11 :
+ known = TRUE;
+ printf("%*cAuthenticated user SID\n",-level,marker);
+ break;
+ case 13 :
+ known = TRUE;
+ printf("%*cLocal service SID\n",-level,marker);
+ break;
+ case 14 :
+ known = TRUE;
+ printf("%*cNetwork service SID\n",-level,marker);
+ break;
+ case 18 :
+ known = TRUE;
+ printf("%*cNT System SID\n",-level,marker);
+ break;
+ }
+ break;
+ }
+ break;
+ case 2 : /* double level */
+ second = get4l(attr,off+12);
+ switch (auth) {
+ case 5 :
+ if (first == 32) {
+ known = TRUE;
+ switch (second) {
+ case 544 :
+ printf("%*cLocal admins SID\n",-level,marker);
+ break;
+ case 545 :
+ printf("%*cLocal users SID\n",-level,marker);
+ break;
+ case 546 :
+ printf("%*cLocal guests SID\n",-level,marker);
+ break;
+ default :
+ printf("%*cSome domain SID\n",-level,marker);
+ break;
+ }
+ }
+ break;
+ }
+ default : /* three levels or more */
+ second = get4l(attr,off+12);
+ last = get4l(attr,off+4+4*cnt);
+ switch (auth) {
+ case 5 :
+ if (first == 21) {
+ known = TRUE;
+ switch (last)
+ {
+ case 512 :
+ printf("%*cLocal admins SID\n",-level,marker);
+ break;
+ case 513 :
+ printf("%*cLocal users SID\n",-level,marker);
+ break;
+ case 514 :
+ printf("%*cLocal guests SID\n",-level,marker);
+ break;
+ default :
+ printf("%*cLocal user-%lu SID\n",-level,marker,last);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ if (!known)
+ printf("%*cUnknown SID\n",-level,marker);
+ printf("%*c%shex S-%d-",-level,marker,prefix,attr[off] & 255);
+ printf("%llx",auth);
+ for (i=0; i<cnt; i++)
+ printf("-%lx",get4l(attr,off+8+4*i));
+ printf("\n");
+ printf("%*c%sdec S-%d-",-level,marker,prefix,attr[off] & 255);
+ printf("%llu",auth);
+ for (i=0; i<cnt; i++)
+ printf("-%lu",get4l(attr,off+8+4*i));
+ printf("\n");
+}
+
+void showusid(const char *attr, int level)
+{
+ int off;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ if (level)
+ printf("%*c",-level,marker);
+ printf("Owner SID\n");
+ off = get4l(attr,4);
+ showsid(attr,off,"O:",level+4);
+}
+
+void showgsid(const char *attr, int level)
+{
+ int off;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ if (level)
+ printf("%*c",-level,marker);
+ printf("Group SID\n");
+ off = get4l(attr,8);
+ showsid(attr,off,"G:",level+4);
+}
+
+void showheader(const char *attr, int level)
+{
+ int flags;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ if (level)
+ printf("%*c",-level,marker);
+ printf("Global header\n");
+ printf("%*crevision %d\n",-level-4,marker,attr[0]);
+ flags = get2l(attr,2);
+ printf("%*cflags 0x%x\n",-level-4,marker,flags);
+ if (flags & 1)
+ printf("%*c owner is defaulted\n",-level-4,marker);
+ if (flags & 2)
+ printf("%*c group is defaulted\n",-level-4,marker);
+ if (flags & 4)
+ printf("%*c DACL present\n",-level-4,marker);
+ if (flags & 8)
+ printf("%*c DACL is defaulted\n",-level-4,marker);
+ if (flags & 0x10)
+ printf("%*c SACL present\n",-level-4,marker);
+ if (flags & 0x20)
+ printf("%*c SACL is defaulted\n",-level-4,marker);
+ if (flags & 0x100)
+ printf("%*c DACL inheritance is requested\n",-level-4,marker);
+ if (flags & 0x200)
+ printf("%*c SACL inheritance is requested\n",-level-4,marker);
+ if (flags & 0x400)
+ printf("%*c DACL was inherited automatically\n",-level-4,marker);
+ if (flags & 0x800)
+ printf("%*c SACL was inherited automatically\n",-level-4,marker);
+ if (flags & 0x1000)
+ printf("%*c DACL cannot be modified by inheritable ACEs\n",-level-4,marker);
+ if (flags & 0x2000)
+ printf("%*c SACL cannot be modified by inheritable ACEs\n",-level-4,marker);
+ if (flags & 0x8000)
+ printf("%*c self relative descriptor\n",-level-4,marker);
+ if (flags & 0x43eb)
+ printf("%*c unknown flags 0x%x present\n",-level-4,marker,
+ flags & 0x43eb);
+ printf("%*cOff USID 0x%x\n",-level-4,marker,(int)get4l(attr,4));
+ printf("%*cOff GSID 0x%x\n",-level-4,marker,(int)get4l(attr,8));
+ printf("%*cOff SACL 0x%x\n",-level-4,marker,(int)get4l(attr,12));
+ printf("%*cOff DACL 0x%x\n",-level-4,marker,(int)get4l(attr,16));
+}
+
+void showace(const char *attr, int off, int isdir, int level)
+{
+ int flags;
+ u32 rights;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ printf("%*ctype %d\n",-level,marker,attr[off]);
+ switch (attr[off]) {
+ case 0 :
+ printf("%*cAccess allowed\n",-level-4,marker);
+ break;
+ case 1 :
+ printf("%*cAccess denied\n",-level-4,marker);
+ break;
+ case 2 :
+ printf("%*cSystem audit\n",-level-4,marker);
+ break;
+ default :
+ printf("%*cunknown\n",-level-4,marker);
+ break;
+ }
+ flags = attr[off+1] & 255;
+ printf("%*cflags 0x%x\n",-level,marker,flags);
+ if (flags & 1)
+ printf("%*cObject inherits ACE\n",-level-4,marker);
+ if (flags & 2)
+ printf("%*cContainer inherits ACE\n",-level-4,marker);
+ if (flags & 4)
+ printf("%*cDon\'t propagate inherits ACE\n",-level-4,marker);
+ if (flags & 8)
+ printf("%*cInherit only ACE\n",-level-4,marker);
+ if (flags & 0x40)
+ printf("%*cAudit on success\n",-level-4,marker);
+ if (flags & 0x80)
+ printf("%*cAudit on failure\n",-level-4,marker);
+
+ printf("%*cSize 0x%x\n",-level,marker,get2l(attr,off+2));
+
+ rights = get4l(attr,off+4);
+ printf("%*cAcc rgts 0x%lx\n",-level,marker,(long)rights);
+ printf("%*cObj specific acc rgts 0x%lx\n",-level-4,marker,(long)rights & 65535);
+ if (isdir) /* a directory */ {
+ if (rights & 0x01)
+ printf("%*cList directory\n",-level-8,marker);
+ if (rights & 0x02)
+ printf("%*cAdd file\n",-level-8,marker);
+ if (rights & 0x04)
+ printf("%*cAdd subdirectory\n",-level-8,marker);
+ if (rights & 0x08)
+ printf("%*cRead EA\n",-level-8,marker);
+ if (rights & 0x10)
+ printf("%*cWrite EA\n",-level-8,marker);
+ if (rights & 0x20)
+ printf("%*cTraverse\n",-level-8,marker);
+ if (rights & 0x40)
+ printf("%*cDelete child\n",-level-8,marker);
+ if (rights & 0x80)
+ printf("%*cRead attributes\n",-level-8,marker);
+ if (rights & 0x100)
+ printf("%*cWrite attributes\n",-level-8,marker);
+ }
+ else {
+ /* see FILE_READ_DATA etc in winnt.h */
+ if (rights & 0x01)
+ printf("%*cRead data\n",-level-8,marker);
+ if (rights & 0x02)
+ printf("%*cWrite data\n",-level-8,marker);
+ if (rights & 0x04)
+ printf("%*cAppend data\n",-level-8,marker);
+ if (rights & 0x08)
+ printf("%*cRead EA\n",-level-8,marker);
+ if (rights & 0x10)
+ printf("%*cWrite EA\n",-level-8,marker);
+ if (rights & 0x20)
+ printf("%*cExecute\n",-level-8,marker);
+ if (rights & 0x80)
+ printf("%*cRead attributes\n",-level-8,marker);
+ if (rights & 0x100)
+ printf("%*cWrite attributes\n",-level-8,marker);
+ }
+ printf("%*cstandard acc rgts 0x%lx\n",-level-4,marker,(long)(rights >> 16) & 127);
+ if (rights & 0x10000)
+ printf("%*cDelete\n",-level-8,marker);
+ if (rights & 0x20000)
+ printf("%*cRead control\n",-level-8,marker);
+ if (rights & 0x40000)
+ printf("%*cWrite DAC\n",-level-8,marker);
+ if (rights & 0x80000)
+ printf("%*cWrite owner\n",-level-8,marker);
+ if (rights & 0x100000)
+ printf("%*cSynchronize\n",-level-8,marker);
+ if (rights & 0x800000)
+ printf("%*cCan access security ACL\n",-level-4,marker);
+ if (rights & 0x10000000)
+ printf("%*cGeneric all\n",-level-4,marker);
+ if (rights & 0x20000000)
+ printf("%*cGeneric execute\n",-level-4,marker);
+ if (rights & 0x40000000)
+ printf("%*cGeneric write\n",-level-4,marker);
+ if (rights & 0x80000000)
+ printf("%*cGeneric read\n",-level-4,marker);
+
+ printf("%*cSID at 0x%x\n",-level,marker,off+8);
+ showsid(attr,off+8,"",level+4);
+ printf("%*cSummary :",-level,marker);
+ if (attr[off] == 0)
+ printf(" grant");
+ if (attr[off] == 1)
+ printf(" deny");
+ if (rights & le32_to_cpu(FILE_GREAD | FILE_GWRITE | FILE_GEXEC)) {
+ printf(" ");
+ if (rights & le32_to_cpu(FILE_GREAD))
+ printf("r");
+ if (rights & le32_to_cpu(FILE_GWRITE))
+ printf("w");
+ if (rights & le32_to_cpu(FILE_GEXEC))
+ printf("x");
+ } else
+ printf(" none");
+ if (flags & 11)
+ printf(" inherited");
+ if (!(flags & 8)) {
+ int sz;
+
+ printf(" applied");
+ sz = attr[off+9]*4 + 8;
+ if (!memcmp(&attr[off+8],&attr[get4l(attr,4)],sz))
+ printf(" to owner");
+ if (!memcmp(&attr[off+8],&attr[get4l(attr,8)],sz))
+ printf(" to group");
+ }
+ printf("\n");
+
+}
+
+void showacl(const char *attr, int off, int isdir, int level)
+{
+ int i;
+ int cnt;
+ int size;
+ int x;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ size = get2l(attr,off+2);
+ printf("%*crevision %d\n",-level,marker,attr[off]);
+ printf("%*cACL size %d\n",-level,marker,size);
+ cnt = get2l(attr,off+4);
+ printf("%*cACE cnt %d\n",-level,marker,cnt);
+ x = 8;
+ for (i=0; (i<cnt) && (x < size); i++) {
+ printf("%*cACE %d at 0x%x\n",-level,marker,i + 1,off+x);
+ showace(attr,off + x,isdir,level+4);
+ x += get2l(attr,off + x + 2);
+ }
+}
+
+void showdacl(const char *attr, int isdir, int level)
+{
+ int off;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ off = get4l(attr,16);
+ if (off) {
+ if (level)
+ printf("%*c",-level,marker);
+ printf("DACL\n");
+ showacl(attr,off,isdir,level+4);
+ } else {
+ if (level)
+ printf("%*c",-level,marker);
+ printf("No DACL\n");
+ }
+}
+
+void showsacl(const char *attr, int isdir, int level)
+{
+ int off;
+ char marker;
+
+ if (opt_b)
+ marker = '#';
+ else
+ marker = ' ';
+ off = get4l(attr,12);
+ if (off) {
+ if (level)
+ printf("%*c",-level,marker);
+ printf("SACL\n");
+ showacl(attr,off,isdir,level+4);
+ }
+ else {
+ if (level)
+ printf("%*c",-level,marker);
+ printf("No SACL\n");
+ }
+}
+
+void showall(const char *attr, int level)
+{
+ BOOL isdir;
+
+ isdir = guess_dir(attr);
+ showheader(attr,level);
+ showusid(attr,level);
+ showgsid(attr,level);
+ showdacl(attr,isdir,level);
+ showsacl(attr,isdir,level);
+}
+
+#if POSIXACLS
+/*
+ * Display a Posix descriptor
+ */
+
+void showposix(const struct POSIX_SECURITY *pxdesc)
+{
+ char txperm[4];
+ const char *txtag;
+ const char *txtype;
+ const struct POSIX_ACL *acl;
+ const struct POSIX_ACE *pxace;
+ int acccnt;
+ int defcnt;
+ int firstdef;
+ int perms;
+ u16 tag;
+ s32 id;
+ int k,l;
+
+ if (pxdesc) {
+ acccnt = pxdesc->acccnt;
+ defcnt = pxdesc->defcnt;
+ firstdef = pxdesc->firstdef;
+ acl = &pxdesc->acl;
+ printf("Posix descriptor :\n");
+ printf(" acccnt %d\n",acccnt);
+ printf(" defcnt %d\n",defcnt);
+ printf(" firstdef %d\n",firstdef);
+ printf(" mode : 0%03o\n",(int)pxdesc->mode);
+ printf(" tagsset : 0x%02x\n",(int)pxdesc->tagsset);
+ printf("Posix ACL :\n");
+ printf(" version %d\n",(int)acl->version);
+ printf(" flags 0x%02x\n",(int)acl->flags);
+ for (k=0; k<(acccnt + defcnt); k++) {
+ if (k < acccnt)
+ l = k;
+ else
+ l = firstdef + k - acccnt;
+ pxace = &acl->ace[l];
+ tag = pxace->tag;
+ perms = pxace->perms;
+ if (tag == POSIX_ACL_SPECIAL) {
+ txperm[0] = (perms & S_ISVTX ? 's' : '-');
+ txperm[1] = (perms & S_ISUID ? 'u' : '-');
+ txperm[2] = (perms & S_ISGID ? 'g' : '-');
+ } else {
+ txperm[0] = (perms & 4 ? 'r' : '-');
+ txperm[1] = (perms & 2 ? 'w' : '-');
+ txperm[2] = (perms & 1 ? 'x' : '-');
+ }
+ txperm[3] = 0;
+ if (k >= acccnt)
+ txtype = "default";
+ else
+ txtype = "access ";
+ switch (tag) {
+ case POSIX_ACL_USER :
+ txtag = "USER ";
+ break;
+ case POSIX_ACL_USER_OBJ :
+ txtag = "USR-O";
+ break;
+ case POSIX_ACL_GROUP :
+ txtag = "GROUP";
+ break;
+ case POSIX_ACL_GROUP_OBJ :
+ txtag = "GRP-O";
+ break;
+ case POSIX_ACL_MASK :
+ txtag = "MASK ";
+ break;
+ case POSIX_ACL_OTHER :
+ txtag = "OTHER";
+ break;
+ case POSIX_ACL_SPECIAL :
+ txtag = "SPECL";
+ break;
+ default :
+ txtag = "UNKWN";
+ break;
+ }
+ id = pxace->id;
+ printf("ace %d : %s %s %4ld perms 0%03o %s\n",
+ l,txtype,txtag,(long)id,
+ perms,txperm);
+ }
+ } else
+ printf("** NULL ACL\n");
+}
+
+#endif /* POSIXACLS */
+
+#if defined(WIN32) | defined(STSC)
+
+#else
+
+/*
+ * Relay to get usid as defined during mounting
+ */
+
+const SID *relay_find_usid(const struct MAPPING *usermapping __attribute__((unused)),
+ uid_t uid, SID *defusid)
+{
+ return (ntfs_get_usid(ntfs_context,uid,(char*)defusid) ?
+ defusid : (SID*)NULL);
+}
+
+/*
+ * Relay to get gsid as defined during mounting
+ */
+
+const SID *relay_find_gsid(const struct MAPPING *groupmapping __attribute__((unused)),
+ uid_t gid, SID *defgsid)
+{
+ return (ntfs_get_usid(ntfs_context,gid,(char*)defgsid) ?
+ defgsid : (SID*)NULL);
+}
+
+/*
+ * Relay to get uid as defined during mounting
+ */
+
+uid_t relay_find_user(const struct MAPPING *mapping __attribute__((unused)),
+ const SID *usid)
+{
+ int uid;
+
+ uid = ntfs_get_user(ntfs_context,(const char*)usid);
+ return (uid < 0 ? 0 : uid);
+}
+
+/*
+ * Relay to get gid as defined during mounting
+ */
+
+gid_t relay_find_group(const struct MAPPING *mapping __attribute__((unused)),
+ const SID *gsid)
+{
+ int gid;
+
+ gid = ntfs_get_group(ntfs_context,(const char*)gsid);
+ return (gid < 0 ? 0 : gid);
+}
+
+#endif
+
+#if defined(WIN32) | defined(STSC)
+
+/*
+ * Dummy get uid from user name, out of a Linux environment
+ */
+
+struct passwd *getpwnam(const char *user)
+{
+ ntfs_log_error("Cannot interpret id \"%s\"", user);
+ ntfs_log_error("please use numeric uids in UserMapping file\n");
+ return ((struct passwd*)NULL);
+}
+
+/*
+ * Dummy get gid from group name, out of a Linux environment
+ */
+
+struct group *getgrnam(const char *group)
+{
+ ntfs_log_error("Cannot interpret id \"%s\"", group);
+ ntfs_log_error("please use numeric gids in UserMapping file\n");
+ return ((struct group*)NULL);
+}
+
+#endif /* defined(WIN32) | defined(STSC) */
+
+#if POSIXACLS
+
+struct POSIX_SECURITY *linux_permissions_posix(const char *attr, BOOL isdir)
+{
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+#if OWNERFROMACL
+ const SID *osid;
+#endif
+ const SID *usid;
+ const SID *gsid;
+ struct POSIX_SECURITY *posix_desc;
+
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
+#if OWNERFROMACL
+ osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+ usid = ntfs_acl_owner((const char*)attr);
+#if SELFTESTS & !USESTUBS
+ if (!opt_t && !ntfs_same_sid(usid,osid))
+ printf("== Linux owner is different from Windows owner\n");
+#else
+ if (!ntfs_same_sid(usid,osid))
+ printf("== Linux owner is different from Windows owner\n");
+#endif
+#else
+ usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+#endif
+ posix_desc = ntfs_build_permissions_posix(context.mapping,
+ (const char*)attr, usid, gsid, isdir);
+ if (!posix_desc) {
+ printf("** Failed to build a Posix descriptor\n");
+ errors++;
+ }
+ return (posix_desc);
+}
+
+#endif /* POSIXACLS */
+
+int linux_permissions(const char *attr, BOOL isdir)
+{
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+#if OWNERFROMACL
+ const SID *osid;
+#endif
+ const SID *usid;
+ const SID *gsid;
+ int perm;
+
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
+#if OWNERFROMACL
+ osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+ usid = ntfs_acl_owner((const char*)attr);
+#if SELFTESTS & !USESTUBS
+ if (!opt_t && !ntfs_same_sid(usid,osid))
+ printf("== Linux owner is different from Windows owner\n");
+#else
+ if (!ntfs_same_sid(usid,osid))
+ printf("== Linux owner is different from Windows owner\n");
+#endif
+#else
+ usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+#endif
+ perm = ntfs_build_permissions((const char*)attr, usid, gsid, isdir);
+ if (perm < 0) {
+ printf("** Failed to build permissions\n");
+ errors++;
+ }
+ return (perm);
+}
+
+uid_t linux_owner(const char *attr)
+{
+ const SID *usid;
+ uid_t uid;
+
+#if OWNERFROMACL
+ usid = ntfs_acl_owner((const char*)attr);
+#else
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+#endif
+#if defined(WIN32) | defined(STSC)
+ uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
+#else
+ if (mappingtype == MAPEXTERN)
+ uid = relay_find_user(context.mapping[MAPUSERS],usid);
+ else
+ uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
+#endif
+ return (uid);
+}
+
+gid_t linux_group(const char *attr)
+{
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+ const SID *gsid;
+ gid_t gid;
+
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
+#if defined(WIN32) | defined(STSC)
+ gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
+#else
+ if (mappingtype == MAPEXTERN)
+ gid = relay_find_group(context.mapping[MAPGROUPS],gsid);
+ else
+ gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
+#endif
+ return (gid);
+}
+
+void newblock(s32 key)
+{
+ struct SECURITY_DATA *psecurdata;
+ int i;
+
+ if ((key > 0) && (key < MAXSECURID) && !securdata[key >> SECBLKSZ]) {
+ securdata[key >> SECBLKSZ] =
+ (struct SECURITY_DATA*)malloc((1 << SECBLKSZ)*sizeof(struct SECURITY_DATA));
+ if (securdata[key >> SECBLKSZ])
+ for (i=0; i<(1 << SECBLKSZ); i++) {
+ psecurdata = &securdata[key >> SECBLKSZ][i];
+ psecurdata->filecount = 0;
+ psecurdata->mode = 0;
+ psecurdata->flags = 0;
+ psecurdata->attr = (char*)NULL;
+ }
+ }
+}
+
+void freeblocks(void)
+{
+ int i,j;
+ struct SECURITY_DATA *psecurdata;
+
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ if (securdata[i]) {
+ for (j=0; j<(1 << SECBLKSZ); j++) {
+ psecurdata = &securdata[i][j];
+ if (psecurdata->attr)
+ free(psecurdata->attr);
+ }
+ free(securdata[i]);
+ }
+}
+
+/*
+ * Basic read from a user mapping file (Win32)
+ */
+
+int basicread(void *fileid, char *buf, size_t size,
+ off_t pos __attribute__((unused)))
+{
+ return (read(*(int*)fileid, buf, size));
+}
+
+#if SELFTESTS & !USESTUBS
+
+/*
+ * Read a dummy mapping file for tests
+ */
+
+int dummyread(void *fileid __attribute__((unused)),
+ char *buf, size_t size, off_t pos)
+{
+ size_t sz;
+
+ if (pos >= (off_t)(sizeof(dummymapping) - 1))
+ sz = 0;
+ else
+ if ((size + pos) >= (sizeof(dummymapping) - 1))
+ sz = sizeof(dummymapping) - 1 - pos;
+ else
+ sz = size;
+ if (sz > 0)
+ memcpy(buf,&dummymapping[pos],sz);
+ return (sz);
+}
+
+#endif /* POSIXACLS & SELFTESTS & !USESTUBS */
+
+/*
+ * Apply default single user mapping
+ * returns zero if successful
+ */
+
+static int do_default_mapping(struct MAPPING *mapping[],
+ const SID *usid)
+{
+ struct MAPPING *usermapping;
+ struct MAPPING *groupmapping;
+ SID *sid;
+ int sidsz;
+ int res;
+
+ res = -1;
+ sidsz = ntfs_sid_size(usid);
+#if USESTUBS
+ sid = (SID*)stdmalloc(sidsz); /* will be freed within the library */
+#else
+ sid = (SID*)ntfs_malloc(sidsz);
+#endif
+ if (sid) {
+ memcpy(sid,usid,sidsz);
+#if USESTUBS
+ usermapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING));
+#else
+ usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
+#endif
+ if (usermapping) {
+#if USESTUBS
+ groupmapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING));
+#else
+ groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
+#endif
+ if (groupmapping) {
+ usermapping->sid = sid;
+ usermapping->xid = 0;
+ usermapping->next = (struct MAPPING*)NULL;
+ groupmapping->sid = sid;
+ groupmapping->xid = 0;
+ groupmapping->next = (struct MAPPING*)NULL;
+ mapping[MAPUSERS] = usermapping;
+ mapping[MAPGROUPS] = groupmapping;
+ res = 0;
+ }
+ }
+ }
+ return (res);
+}
+
+/*
+ * Build the user mapping
+ * - according to a mapping file if defined (or default present),
+ * - or try default single user mapping if possible
+ *
+ * The mapping is specific to a mounted device
+ * No locking done, mounting assumed non multithreaded
+ *
+ * returns zero if mapping is successful
+ * (failure should not be interpreted as an error)
+ */
+
+int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path)
+{
+#ifdef WIN32
+ char mapfile[sizeof(MAPDIR) + sizeof(MAPFILE) + 6];
+#else
+ char *mapfile;
+ char *p;
+#endif
+ int fd;
+ struct MAPLIST *item;
+ struct MAPLIST *firstitem = (struct MAPLIST*)NULL;
+ struct MAPPING *usermapping;
+ struct MAPPING *groupmapping;
+ static struct {
+ u8 revision;
+ u8 levels;
+ be16 highbase;
+ be32 lowbase;
+ le32 level1;
+ le32 level2;
+ le32 level3;
+ le32 level4;
+ le32 level5;
+ } defmap = {
+ 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
+ const_cpu_to_le32(21),
+ const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
+ const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
+ } ;
+
+ /* be sure not to map anything until done */
+ mapping[MAPUSERS] = (struct MAPPING*)NULL;
+ mapping[MAPGROUPS] = (struct MAPPING*)NULL;
+
+ if (usermap_path) {
+#ifdef WIN32
+/* TODO : check whether the device can store acls */
+ strcpy(mapfile,"x:\\" MAPDIR "\\" MAPFILE);
+ if (((le16*)usermap_path)[1] == ':')
+ mapfile[0] = usermap_path[0];
+ else
+ mapfile[0] = getdrive() + 'A' - 1;
+ fd = open(mapfile,O_RDONLY);
+#else
+ fd = 0;
+ mapfile = (char*)malloc(MAXFILENAME);
+ if (mapfile) {
+ /* build a full path to locate the mapping file */
+ if ((usermap_path[0] != '/')
+ && getcwd(mapfile,MAXFILENAME)) {
+ strcat(mapfile,"/");
+ strcat(mapfile,usermap_path);
+ } else
+ strcpy(mapfile,usermap_path);
+ p = strrchr(mapfile,'/');
+ if (p)
+ do {
+ strcpy(p,"/" MAPDIR "/" MAPFILE);
+ fd = open(mapfile,O_RDONLY);
+ if (fd <= 0) {
+ *p = 0;
+ p = strrchr(mapfile,'/');
+ if (p == mapfile)
+ p = (char*)NULL;
+ }
+ } while ((fd <= 0) && p);
+ free(mapfile);
+ if (!p) {
+ printf("** Could not find the user mapping file\n");
+ if (usermap_path[0] != '/')
+ printf(" Retry with full path of file\n");
+ errors++;
+ }
+ }
+#endif
+ if (fd > 0) {
+ firstitem = ntfs_read_mapping(basicread, (void*)&fd);
+ close(fd);
+ }
+ } else {
+#if SELFTESTS & !USESTUBS
+ firstitem = ntfs_read_mapping(dummyread, (void*)NULL);
+#endif
+ }
+
+ if (firstitem) {
+ usermapping = ntfs_do_user_mapping(firstitem);
+ groupmapping = ntfs_do_group_mapping(firstitem);
+ if (usermapping && groupmapping) {
+ mapping[MAPUSERS] = usermapping;
+ mapping[MAPGROUPS] = groupmapping;
+ } else
+ ntfs_log_error("There were no valid user or no valid group\n");
+ /* now we can free the memory copy of input text */
+ /* and rely on internal representation */
+ while (firstitem) {
+ item = firstitem->next;
+#if USESTUBS
+ stdfree(firstitem); /* allocated within library */
+#else
+ free(firstitem);
+#endif
+ firstitem = item;
+ }
+ } else {
+ do_default_mapping(mapping,(const SID*)&defmap);
+ }
+ if (mapping[MAPUSERS])
+ mappingtype = MAPLOCAL;
+ return (!mapping[MAPUSERS]);
+}
+
+/*
+ * Get an hexadecimal number (source with MSB first)
+ */
+
+u32 getmsbhex(const char *text)
+{
+ u32 v;
+ int b;
+ BOOL ok;
+
+ v = 0;
+ ok = TRUE;
+ do {
+ b = *text++;
+ if ((b >= '0') && (b <= '9'))
+ v = (v << 4) + b - '0';
+ else
+ if ((b >= 'a') && (b <= 'f'))
+ v = (v << 4) + b - 'a' + 10;
+ else
+ if ((b >= 'A') && (b <= 'F'))
+ v = (v << 4) + b - 'A' + 10;
+ else ok = FALSE;
+ } while (ok);
+ return (v);
+}
+
+
+/*
+ * Get an hexadecimal number (source with LSB first)
+ * An odd number of digits might yield a strange result
+ */
+
+u32 getlsbhex(const char *text)
+{
+ u32 v;
+ int b;
+ BOOL ok;
+ int pos;
+
+ v = 0;
+ ok = TRUE;
+ pos = 0;
+ do {
+ b = *text++;
+ if ((b >= '0') && (b <= '9'))
+ v |= (u32)(b - '0') << (pos ^ 4);
+ else
+ if ((b >= 'a') && (b <= 'f'))
+ v |= (u32)(b - 'a' + 10) << (pos ^ 4);
+ else
+ if ((b >= 'A') && (b <= 'F'))
+ v |= (u32)(b - 'A' + 10) << (pos ^ 4);
+ else ok = FALSE;
+ pos += 4;
+ } while (ok);
+ return (v);
+}
+
+
+/*
+ * Check whether a line looks like an hex dump
+ */
+
+BOOL ishexdump(const char *line, int first, int lth)
+{
+ BOOL ok;
+ int i;
+ int b;
+
+ ok = (first >= 0) && (lth >= (first + 16));
+ for (i=0; ((first+i)<lth) && ok; i++) {
+ b = line[first + i];
+ if ((i == 6)
+ || (i == 7)
+ || (i == 16)
+ || (i == 25)
+ || (i == 34)
+ || (i == 43))
+ ok = (b == ' ') || (b == '\n');
+ else
+ ok = ((b >= '0') && (b <= '9'))
+ || ((b >= 'a') && (b <= 'f'))
+ || ((b >= 'A') && (b <= 'F'));
+ }
+ return (ok);
+}
+
+
+/*
+ * Display security descriptors from a file
+ * This is typically to convert a verbose output to a very verbose one
+ */
+
+void showhex(FILE *fd)
+{
+ static char attr[MAXATTRSZ];
+ char line[MAXLINE+1];
+#if POSIXACLS
+ struct POSIX_SECURITY *pxdesc;
+#endif
+ int lth;
+ int first;
+ unsigned int pos;
+ u32 v;
+ int c;
+ int isdir;
+ int mode;
+ unsigned int off;
+ int i;
+ BOOL isdump;
+ BOOL done;
+
+ pos = 0;
+ off = 0;
+ done = FALSE;
+ do {
+ /* input a (partial) line without displaying */
+ lth = 0;
+ first = -1;
+ do {
+ c = getc(fd);
+ if ((c != ' ') && (first < 0))
+ first = lth;
+ if (c == EOF)
+ done = TRUE;
+ else
+ if (c != '\r')
+ line[lth++] = c;
+ } while (!done && (c != '\n') && (lth < MAXLINE));
+ /* check whether this looks like an hexadecimal dump */
+ isdump = ishexdump(line, first, lth);
+ if (isdump) off = getmsbhex(&line[first]);
+ /* line is not an hexadecimal dump */
+ /* display what we have in store */
+ if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) {
+ printf(" Computed hash : 0x%08lx\n",
+ (unsigned long)hash((le32*)attr,
+ ntfs_attr_size(attr)));
+ isdir = guess_dir(attr);
+ printf(" Estimated type : %s\n",(isdir ? "directory" : "file"));
+ showheader(attr,4);
+ showusid(attr,4);
+ showgsid(attr,4);
+ showdacl(attr,isdir,4);
+ showsacl(attr,isdir,4);
+ mode = linux_permissions(attr,isdir);
+ printf("Interpreted Unix mode 0%03o\n",mode);
+#if POSIXACLS
+ /*
+ * Posix display not possible when user
+ * mapping is not available (option -h)
+ */
+ if (mappingtype != MAPNONE) {
+ pxdesc = linux_permissions_posix(attr,isdir);
+ if (pxdesc) {
+ showposix(pxdesc);
+ free(pxdesc);
+ }
+ }
+#endif
+ pos = 0;
+ }
+ if (isdump && !off)
+ pos = off;
+ /* line looks like an hexadecimal dump */
+ /* decode it into attribute */
+ if (isdump && (off == pos)) {
+ for (i=first+8; i<lth; i+=9) {
+ v = getlsbhex(&line[i]);
+ *(le32*)&attr[pos] = cpu_to_le32(v);
+ pos += 4;
+ }
+ }
+ /* display (full) current line */
+ if (lth) printf("! ");
+ for (i=0; i<lth; i++) {
+ c = line[i];
+ putchar(c);
+ }
+ while (!done && (c != '\n')) {
+ c = getc(fd);
+ if (c == EOF)
+ done = TRUE;
+ else
+ putchar(c);
+ }
+ } while (!done);
+}
+
+BOOL applyattr(const char *fullname, const char *attr,
+ BOOL withattr, int attrib, s32 key)
+{
+ struct SECURITY_DATA *psecurdata;
+ const char *curattr;
+ char *newattr;
+ int selection;
+ BOOL bad;
+ BOOL badattrib;
+ BOOL err;
+
+ err = FALSE;
+ psecurdata = (struct SECURITY_DATA*)NULL;
+ curattr = (const char*)NULL;
+ newattr = (char*)NULL;
+ if ((key > 0) && (key < MAXSECURID)) {
+ if (!securdata[key >> SECBLKSZ])
+ newblock(key);
+ if (securdata[key >> SECBLKSZ]) {
+ psecurdata = &securdata[key >> SECBLKSZ]
+ [key & ((1 << SECBLKSZ) - 1)];
+ }
+ }
+
+ /* If we have a usable attrib value. Try applying */
+ badattrib = FALSE;
+ if (opt_e && (attrib != INVALID_FILE_ATTRIBUTES)) {
+#ifdef WIN32
+ badattrib = !SetFileAttributesW((LPCWSTR)fullname, attrib);
+#else
+ badattrib = !ntfs_set_file_attributes(ntfs_context, fullname, attrib);
+#endif
+ if (badattrib) {
+ printf("** Could not set Windows attrib of ");
+ printname(stdout,fullname);
+ printf(" to 0x%x\n", attrib);
+ printerror(stdout);
+ warnings++;
+ }
+ }
+
+ if (withattr) {
+ if (psecurdata) {
+ newattr = (char*)malloc(ntfs_attr_size(attr));
+ if (newattr) {
+ memcpy(newattr,attr,ntfs_attr_size(attr));
+ psecurdata->attr = newattr;
+ }
+ }
+ curattr = attr;
+ } else
+ /*
+ * No explicit attr in backup, use attr defined
+ * previously for the same id
+ */
+ if (psecurdata)
+ curattr = psecurdata->attr;
+
+
+ if (curattr) {
+#ifdef WIN32
+ /* SACL currently not set, need some special privilege */
+ selection = OWNER_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | DACL_SECURITY_INFORMATION;
+ bad = !SetFileSecurityW((LPCWSTR)fullname,
+ selection, (char*)curattr);
+ if (bad)
+ switch (GetLastError()) {
+ case 1307 :
+ case 1314 :
+ printf("** Could not set owner of ");
+ printname(stdout,fullname);
+ printf(", retrying with no owner setting\n");
+ warnings++;
+ bad = !SetFileSecurityW((LPCWSTR)fullname,
+ selection & ~OWNER_SECURITY_INFORMATION, (char*)curattr);
+ break;
+ default :
+ break;
+ }
+#else
+ selection = OWNER_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | DACL_SECURITY_INFORMATION
+ | SACL_SECURITY_INFORMATION;
+ bad = !ntfs_set_file_security(ntfs_context,fullname,
+ selection, (const char*)curattr);
+#endif
+ if (bad) {
+ printf("** Could not set the ACL of ");
+ printname(stdout,fullname);
+ printf("\n");
+ printerror(stdout);
+ err = TRUE;
+ } else
+ if (opt_v) {
+ if (opt_e && !badattrib)
+ printf("ACL and attrib have been applied to ");
+ else
+ printf("ACL has been applied to ");
+ printname(stdout,fullname);
+ printf("\n");
+
+ }
+ } else {
+ printf("** There was no valid ACL for ");
+ printname(stdout,fullname);
+ printf("\n");
+ err = TRUE;
+ }
+ return (!err);
+}
+
+/*
+ * Restore security descriptors from a file
+ */
+
+BOOL restore(FILE *fd)
+{
+ static char attr[MAXATTRSZ];
+ char line[MAXFILENAME+25];
+ char fullname[MAXFILENAME+25];
+ SECURITY_DESCRIPTOR_RELATIVE *phead;
+ int lth;
+ int first;
+ unsigned int pos;
+ int c;
+ int isdir;
+ int mode;
+ s32 key;
+ BOOL isdump;
+ unsigned int off;
+ u32 v;
+ u32 oldhash;
+ int i;
+ int count;
+ int attrib;
+ BOOL withattr;
+ BOOL done;
+
+ pos = 0;
+ off = 0;
+ done = FALSE;
+ withattr = FALSE;
+ oldhash = 0;
+ key = 0;
+ errors = 0;
+ count = 0;
+ fullname[0] = 0;
+ attrib = INVALID_FILE_ATTRIBUTES;
+ do {
+ /* input a (partial) line without processing */
+ lth = 0;
+ first = -1;
+ do {
+ c = getc(fd);
+ if ((c != ' ') && (first < 0))
+ first = lth;
+ if (c == EOF)
+ done = TRUE;
+ else
+ if (c != '\r')
+ line[lth++] = c;
+ } while (!done && (c != '\n') && (lth < (MAXFILENAME + 24)));
+ /* check whether this looks like an hexadecimal dump */
+ isdump = ishexdump(line, first, lth);
+ if (isdump) off = getmsbhex(&line[first]);
+ /* line is not an hexadecimal dump */
+ /* apply what we have in store */
+ if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) {
+ withattr = TRUE;
+ if (opt_v >= 2) {
+ printf(" Computed hash : 0x%08lx\n",
+ (unsigned long)hash((le32*)attr,
+ ntfs_attr_size(attr)));
+ isdir = guess_dir(attr);
+ printf(" Estimated type : %s\n",(isdir ? "directory" : "file"));
+ showheader(attr,4);
+ showusid(attr,4);
+ showgsid(attr,4);
+ showdacl(attr,isdir,4);
+ showsacl(attr,isdir,4);
+ mode = linux_permissions(attr,isdir);
+ printf("Interpreted Unix mode 0%03o\n",mode);
+ }
+ pos = 0;
+ }
+ if (isdump && !off)
+ pos = off;
+ /* line looks like an hexadecimal dump */
+ /* decode it into attribute */
+ if (isdump && (off == pos)) {
+ for (i=first+8; i<lth; i+=9) {
+ v = getlsbhex(&line[i]);
+ *(le32*)&attr[pos] = cpu_to_le32(v);
+ pos += 4;
+ }
+ }
+ /* display (full) current line unless dump or verbose */
+ if (!isdump || opt_v) {
+ if(lth) printf("! ");
+ for (i=0; i<lth; i++) {
+ c = line[i];
+ putchar(c);
+ }
+ }
+ while (!done && (c != '\n')) {
+ c = getc(fd);
+ if (c == EOF)
+ done = TRUE;
+ else
+ if (!isdump || opt_v)
+ putchar(c);
+ }
+
+ line[lth] = 0;
+ while ((lth > 0)
+ && ((line[lth-1] == '\n') || (line[lth-1] == '\r')))
+ line[--lth] = 0;
+ if (!strncmp(line,"Computed hash : 0x",18))
+ oldhash = getmsbhex(&line[18]);
+ if (!strncmp(line,"Security key : 0x",17))
+ key = getmsbhex(&line[17]);
+ if (!strncmp(line,"Windows attrib : 0x",19))
+ attrib = getmsbhex(&line[19]);
+ if (done
+ || !strncmp(line,"File ",5)
+ || !strncmp(line,"Directory ",10)) {
+ /*
+ * New file or directory (or end of file) :
+ * apply attribute just collected
+ * or apply attribute defined from current key
+ */
+
+ if (withattr
+ && oldhash
+ && (hash((const le32*)attr,ntfs_attr_size(attr)) != oldhash)) {
+ printf("** ACL rejected, its hash is not as expected\n");
+ errors++;
+ } else
+ if (fullname[0]) {
+ phead = (SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ /* set the request for auto-inheritance */
+ if (phead->control & SE_DACL_AUTO_INHERITED)
+ phead->control |= SE_DACL_AUTO_INHERIT_REQ;
+ if (!applyattr(fullname,attr,withattr,
+ attrib,key))
+ errors++;
+ else
+ count++;
+ }
+ /* save current file or directory name */
+ withattr = FALSE;
+ key = 0;
+ oldhash = 0;
+ attrib = INVALID_FILE_ATTRIBUTES;
+ if (!done) {
+#ifdef WIN32
+ if (!strncmp(line,"File ",5))
+ makeutf16(fullname,&line[5]);
+ else
+ makeutf16(fullname,&line[10]);
+#else
+ if (!strncmp(line,"File ",5))
+ strcpy(fullname,&line[5]);
+ else
+ strcpy(fullname,&line[10]);
+#endif
+ }
+ }
+ } while (!done);
+ printf("%d ACLs have been applied\n",count);
+ return (FALSE);
+}
+
+/*
+ * Open the security API in rw mode for an ACL restoration
+ */
+
+#ifdef WIN32
+#else
+BOOL dorestore(const char *volume, FILE *fd)
+{
+ BOOL err;
+
+ err = FALSE;
+ if (!getuid()) {
+ if (open_security_api()) {
+ if (open_volume(volume,MS_NONE)) {
+ if (restore(fd)) err = TRUE;
+ close_volume(volume);
+ } else {
+ fprintf(stderr,"Could not open volume %s\n",volume);
+ printerror(stderr);
+ err = TRUE;
+ }
+ close_security_api();
+ } else {
+ fprintf(stderr,"Could not open security API\n");
+ printerror(stderr);
+ err = TRUE;
+ }
+ } else {
+ fprintf(stderr,"Restore is only possible as root\n");
+ err = TRUE;
+ }
+ return (err);
+}
+#endif /* WIN32 */
+
+#if POSIXACLS & SELFTESTS & !USESTUBS
+
+/*
+ * Merge Posix ACL rights into an u32 (self test only)
+ *
+ * Result format : -----rwxrwxrwxrwxrwx---rwxrwxrwx
+ * U1 U2 G1 G2 M o g w
+ *
+ * Only two users (U1, U2) and two groups (G1, G2) taken into account
+ */
+u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def)
+{
+ const struct POSIX_ACE *pxace;
+ int i;
+ int users;
+ int groups;
+ int first;
+ int last;
+ u32 rights;
+
+ rights = 0;
+ users = 0;
+ groups = 0;
+ if (def) {
+ first = pxdesc->firstdef;
+ last = pxdesc->firstdef + pxdesc->defcnt - 1;
+ } else {
+ first = 0;
+ last = pxdesc->acccnt - 1;
+ }
+ pxace = pxdesc->acl.ace;
+ for (i=first; i<=last; i++) {
+ switch (pxace[i].tag) {
+ case POSIX_ACL_USER_OBJ :
+ rights |= (pxace[i].perms & 7) << 6;
+ break;
+ case POSIX_ACL_USER :
+ if (users < 2)
+ rights |= ((u32)pxace[i].perms & 7) << (24 - 3*users);
+ users++;
+ break;
+ case POSIX_ACL_GROUP_OBJ :
+ rights |= (pxace[i].perms & 7) << 3;
+ break;
+ case POSIX_ACL_GROUP :
+ if (groups < 2)
+ rights |= ((u32)pxace[i].perms & 7) << (18 - 3*groups);
+ groups++;
+ break;
+ case POSIX_ACL_MASK :
+ rights |= ((u32)pxace[i].perms & 7) << 12;
+ break;
+ case POSIX_ACL_OTHER :
+ rights |= (pxace[i].perms & 7);
+ break;
+ default :
+ break;
+ }
+ }
+ return (rights);
+}
+
+void tryposix(struct POSIX_SECURITY *pxdesc)
+{
+ le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
+ cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
+ } ;
+ le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
+ cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
+ } ;
+
+ char *attr;
+ BOOL isdir;
+ mode_t mode;
+ struct POSIX_SECURITY *newpxdesc;
+ struct POSIX_SECURITY *oldpxdesc;
+ static char *oldattr = (char*)NULL;
+
+ isdir = FALSE;
+ if (oldattr) {
+ oldpxdesc = linux_permissions_posix(oldattr, isdir);
+ newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
+ if (!newpxdesc)
+ newpxdesc = pxdesc;
+ free(oldpxdesc);
+ if (opt_v) {
+ printf("merged descriptors :\n");
+ showposix(newpxdesc);
+ }
+ } else
+ newpxdesc = pxdesc;
+ attr = ntfs_build_descr_posix(context.mapping,newpxdesc,
+ isdir,(SID*)owner_sid,(SID*)group_sid);
+ if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
+ if (opt_v)
+ hexdump(attr,ntfs_attr_size(attr),8);
+ if (opt_v >= 2) {
+ showheader(attr,4);
+ showusid(attr,4);
+ showgsid(attr,4);
+ showdacl(attr,isdir,4);
+ showsacl(attr,isdir,4);
+ mode = linux_permissions(attr,isdir);
+ printf("Interpreted Unix mode 0%03o\n",mode);
+ printf("Interpreted back Posix descriptor :\n");
+ newpxdesc = linux_permissions_posix(attr,isdir);
+ showposix(newpxdesc);
+ free(newpxdesc);
+ }
+ if (oldattr) free(oldattr);
+ oldattr = attr;
+ }
+}
+
+static BOOL same_posix(struct POSIX_SECURITY *pxdesc1,
+ struct POSIX_SECURITY *pxdesc2)
+{
+ BOOL same;
+ int i;
+
+ same = pxdesc1
+ && pxdesc2
+ && (pxdesc1->mode == pxdesc2->mode)
+ && (pxdesc1->acccnt == pxdesc2->acccnt)
+ && (pxdesc1->defcnt == pxdesc2->defcnt)
+ && (pxdesc1->firstdef == pxdesc2->firstdef)
+ && (pxdesc1->tagsset == pxdesc2->tagsset)
+ && (pxdesc1->acl.version == pxdesc2->acl.version)
+ && (pxdesc1->acl.flags == pxdesc2->acl.flags);
+ i = 0;
+ while (same && (i < pxdesc1->acccnt)) {
+ same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
+ && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
+ && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
+ i++;
+ }
+ i = pxdesc1->firstdef;
+ while (same && (i < pxdesc1->firstdef + pxdesc1->defcnt)) {
+ same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
+ && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
+ && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
+ i++;
+ }
+ return (same);
+}
+
+#endif /* POSIXACLS & SELFTESTS & !USESTUBS */
+
+#if SELFTESTS & !USESTUBS
+
+/*
+ * Build a dummy security descriptor
+ * returns descriptor in allocated memory, must free() after use
+ */
+
+static char *build_dummy_descr(BOOL isdir __attribute__((unused)),
+ const SID *usid, const SID *gsid,
+ int cnt,
+ /* seq of int allow, SID *sid, int flags, u32 mask */
+ ...)
+{
+ char *attr;
+ int attrsz;
+ SECURITY_DESCRIPTOR_RELATIVE *pnhead;
+ ACL *pacl;
+ ACCESS_ALLOWED_ACE *pace;
+ va_list ap;
+ const SID *sid;
+ u32 umask;
+ le32 mask;
+ int flags;
+ BOOL allow;
+ int pos;
+ int usidsz;
+ int gsidsz;
+ int sidsz;
+ int aclsz;
+ int i;
+
+ if (usid)
+ usidsz = ntfs_sid_size(usid);
+ else
+ usidsz = 0;
+ if (gsid)
+ gsidsz = ntfs_sid_size(gsid);
+ else
+ gsidsz = 0;
+
+
+ /* allocate enough space for the new security attribute */
+ attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */
+ + usidsz + gsidsz /* usid and gsid */
+ + sizeof(ACL) /* acl header */
+ + cnt*40;
+
+ attr = (char*)ntfs_malloc(attrsz);
+ if (attr) {
+ /* build the main header part */
+ pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) attr;
+ pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
+ pnhead->alignment = 0;
+ /*
+ * The flag SE_DACL_PROTECTED prevents the ACL
+ * to be changed in an inheritance after creation
+ */
+ pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
+ | SE_SELF_RELATIVE;
+ /*
+ * Windows prefers ACL first, do the same to
+ * get the same hash value and avoid duplication
+ */
+ /* build the ACL header */
+ pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
+ pacl = (ACL*)&attr[pos];
+ pacl->revision = ACL_REVISION;
+ pacl->alignment1 = 0;
+ pacl->size = cpu_to_le16(0); /* fixed later */
+ pacl->ace_count = cpu_to_le16(cnt);
+ pacl->alignment2 = cpu_to_le16(0);
+
+ /* enter the ACEs */
+
+ pos += sizeof(ACL);
+ aclsz = sizeof(ACL);
+ va_start(ap,cnt);
+ for (i=0; i<cnt; i++) {
+ pace = (ACCESS_ALLOWED_ACE*)&attr[pos];
+ allow = va_arg(ap,int);
+ sid = va_arg(ap,SID*);
+ flags = va_arg(ap,int);
+ umask = va_arg(ap,u32);
+ mask = cpu_to_le32(umask);
+ sidsz = ntfs_sid_size(sid);
+ pace->type = (allow ? ACCESS_ALLOWED_ACE_TYPE : ACCESS_DENIED_ACE_TYPE);
+ pace->flags = flags;
+ pace->size = cpu_to_le16(sidsz + 8);
+ pace->mask = mask;
+ memcpy(&pace->sid,sid,sidsz);
+ aclsz += sidsz + 8;
+ pos += sidsz + 8;
+ }
+ va_end(ap);
+
+ /* append usid and gsid if defined */
+ /* positions of ACL, USID and GSID into header */
+ pnhead->owner = cpu_to_le32(0);
+ pnhead->group = cpu_to_le32(0);
+ if (usid) {
+ memcpy(&attr[pos], usid, usidsz);
+ pnhead->owner = cpu_to_le32(pos);
+ }
+ if (gsid) {
+ memcpy(&attr[pos + usidsz], gsid, gsidsz);
+ pnhead->group = cpu_to_le32(pos + usidsz);
+ }
+ /* positions of DACL and SACL into header */
+ pnhead->sacl = cpu_to_le32(0);
+ if (cnt) {
+ pacl->size = cpu_to_le16(aclsz);
+ pnhead->dacl =
+ cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
+ } else
+ pnhead->dacl = cpu_to_le32(0);
+ if (!ntfs_valid_descr(attr,pos+usidsz+gsidsz)) {
+ printf("** Bad sample descriptor\n");
+ free(attr);
+ attr = (char*)NULL;
+ errors++;
+ }
+ } else
+ errno = ENOMEM;
+ return (attr);
+}
+
+/*
+ * Check a few samples with special conditions
+ */
+
+void check_samples()
+{
+ char *descr = (char*)NULL;
+ BOOL isdir = FALSE;
+ mode_t perms;
+ mode_t expect = 0;
+ int cnt;
+ u32 expectacc;
+ u32 expectdef;
+#if POSIXACLS
+ u32 accrights;
+ u32 defrights;
+ mode_t mixmode;
+ struct POSIX_SECURITY *pxdesc;
+ struct POSIX_SECURITY *pxsample;
+ const char *txsample;
+#endif
+ le32 owner1[] = /* S-1-5-21-1833069642-4243175381-1340018762-1003 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(1833069642), cpu_to_le32(4243175381),
+ cpu_to_le32(1340018762), cpu_to_le32(1003)
+ } ;
+ le32 group1[] = /* S-1-5-21-1833069642-4243175381-1340018762-513 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(1833069642), cpu_to_le32(4243175381),
+ cpu_to_le32(1340018762), cpu_to_le32(513)
+ } ;
+ le32 group2[] = /* S-1-5-21-1607551490-981732888-1819828000-513 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(1607551490), cpu_to_le32(981732888),
+ cpu_to_le32(1819828000), cpu_to_le32(513)
+ } ;
+ le32 owner3[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
+ cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
+ } ;
+ le32 group3[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
+ cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
+ } ;
+
+#if POSIXACLS
+ struct {
+ struct POSIX_SECURITY head;
+ struct POSIX_ACE ace[4];
+ } sampletry1 =
+ {
+ { 0645, 4, 0, 4, 0x35,
+ { POSIX_VERSION, 0, 0 }
+ },
+ {
+ { 1, 6, -1 },
+ { 4, 5, -1 },
+ { 16, 4, -1 },
+ { 32, 5, -1 }
+ }
+ } ;
+
+ struct {
+ struct POSIX_SECURITY head;
+ struct POSIX_ACE ace[6];
+ } sampletry3 =
+ {
+ { 0100, 6, 0, 6, 0x3f,
+ { POSIX_VERSION, 0, 0 }
+ },
+ {
+ { 1, 1, -1 },
+ { 2, 3, 1000 },
+ { 4, 1, -1 },
+ { 8, 3, 1002 },
+ { 16, 0, -1 },
+ { 32, 0, -1 }
+ }
+ } ;
+
+ struct {
+ struct POSIX_SECURITY head;
+ struct POSIX_ACE ace[8];
+ } sampletry4 =
+ {
+ { 0140, 8, 0, 8, 0x3f,
+ { POSIX_VERSION, 0, 0 }
+ },
+ {
+ { 1, 1, -1 },
+ { 2, 3, 516 },
+ { 2, 6, 1000 },
+ { 4, 1, -1 },
+ { 8, 6, 500 },
+ { 8, 3, 1002 },
+ { 16, 4, -1 },
+ { 32, 0, -1 }
+ }
+ } ;
+
+ struct {
+ struct POSIX_SECURITY head;
+ struct POSIX_ACE ace[6];
+ } sampletry5 =
+ {
+ { 0454, 6, 0, 6, 0x3f,
+ { POSIX_VERSION, 0, 0 }
+ },
+ {
+ { 1, 4, -1 },
+ { 2, 5, 516 },
+ { 4, 4, -1 },
+ { 8, 6, 500 },
+ { 16, 5, -1 },
+ { 32, 4, -1 }
+ }
+ } ;
+
+ struct {
+ struct POSIX_SECURITY head;
+ struct POSIX_ACE ace[8];
+ } sampletry6 =
+ {
+ { 0332, 8, 0, 8, 0x3f,
+ { POSIX_VERSION, 0, 0 }
+ },
+ {
+ { 1, 3, -1 },
+ { 2, 1, 0 },
+ { 2, 2, 1000 },
+ { 4, 6, -1 },
+ { 8, 4, 0 },
+ { 8, 5, 1002 },
+ { 16, 3, -1 },
+ { 32, 2, -1 }
+ }
+ } ;
+
+ struct {
+ struct POSIX_SECURITY head;
+ struct POSIX_ACE ace[4];
+ } sampletry8 =
+ {
+ { 0677, 4, 0, 4, 0x35,
+ { POSIX_VERSION, 0, 0 }
+ },
+ {
+ { 1, 6, -1 },
+ { 4, 7, -1 },
+ { 16, 7, -1 },
+ { 32, 7, -1 }
+ }
+ } ;
+
+#endif /* POSIXACLS */
+
+
+#if POSIXACLS
+ for (cnt=1; cnt<=8; cnt++) {
+ switch (cnt) {
+ case 1 :
+ pxsample = &sampletry1.head;
+ txsample = "sampletry1-a";
+ isdir = FALSE;
+ descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
+ isdir, (const SID*)owner3, (const SID*)group3);
+ break;
+ case 2 :
+ pxsample = &sampletry1.head;
+ txsample = "sampletry1-b";
+ isdir = FALSE;
+ descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
+ isdir, (const SID*)adminsid, (const SID*)group3);
+ break;
+ case 3 :
+ isdir = FALSE;
+ pxsample = &sampletry3.head;
+ txsample = "sampletry3";
+ descr = ntfs_build_descr_posix(context.mapping,pxsample,
+ isdir, (const SID*)group3, (const SID*)group3);
+ break;
+ case 4 :
+ isdir = FALSE;
+ pxsample = &sampletry4.head;
+ txsample = "sampletry4";
+ descr = ntfs_build_descr_posix(context.mapping,pxsample,
+ isdir, (const SID*)owner3, (const SID*)group3);
+ break;
+ case 5 :
+ isdir = FALSE;
+ pxsample = &sampletry5.head;
+ txsample = "sampletry5";
+ descr = ntfs_build_descr_posix(context.mapping,pxsample,
+ isdir, (const SID*)owner3, (const SID*)group3);
+ break;
+ case 6 :
+ isdir = FALSE;
+ pxsample = &sampletry6.head;
+ txsample = "sampletry6-a";
+ descr = ntfs_build_descr_posix(context.mapping,pxsample,
+ isdir, (const SID*)owner3, (const SID*)group3);
+ break;
+ case 7 :
+ isdir = FALSE;
+ pxsample = &sampletry6.head;
+ txsample = "sampletry6-b";
+ descr = ntfs_build_descr_posix(context.mapping,pxsample,
+ isdir, (const SID*)adminsid, (const SID*)adminsid);
+ break;
+ case 8 :
+ pxsample = &sampletry8.head;
+ txsample = "sampletry8";
+ isdir = FALSE;
+ descr = ntfs_build_descr_posix(context.mapping,&sampletry8.head,
+ isdir, (const SID*)owner3, (const SID*)group3);
+ break;
+ default :
+ pxsample = (struct POSIX_SECURITY*)NULL;
+ txsample = (const char*)NULL;
+ }
+ /* check we get original back */
+ if (descr)
+ pxdesc = linux_permissions_posix(descr, isdir);
+ else
+ pxdesc = (struct POSIX_SECURITY*)NULL;
+ if (!descr || !pxdesc || !same_posix(pxsample,pxdesc)) {
+ printf("** Error in %s\n",txsample);
+ showposix(pxsample);
+ showall(descr,0);
+ showposix(pxdesc);
+ errors++;
+ }
+ free(descr);
+ free(pxdesc);
+ }
+
+#endif /* POSIXACLS */
+
+
+ /*
+ * Check a few samples built by Windows,
+ * which cannot be generated by Linux
+ */
+
+ for (cnt=1; cnt<=8; cnt++) {
+ switch(cnt) {
+ case 1 : /* hp/tmp */
+ isdir = TRUE;
+ descr = build_dummy_descr(isdir,
+ (const SID*)owner1, (const SID*)group1,
+ 1,
+ (int)TRUE, worldsid, (int)0x3, (u32)0x1f01ff);
+ expectacc = expect = 0777;
+ expectdef = 0;
+ break;
+ case 2 : /* swsetup */
+ isdir = TRUE;
+ descr = build_dummy_descr(isdir, adminsid, (const SID*)group2,
+ 2,
+ (int)TRUE, worldsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, worldsid, (int)0xb, (u32)0x1f01ff);
+ expectacc = expect = 0777;
+ expectdef = 0777;
+ break;
+ case 3 : /* Dr Watson */
+ isdir = TRUE;
+ descr = build_dummy_descr(isdir, (const SID*)owner3, (const SID*)group3,
+ 0);
+ expectacc = expect = 0700;
+ expectdef = 0;
+ break;
+ case 4 :
+ isdir = FALSE;
+ descr = build_dummy_descr(isdir,
+ (const SID*)owner3, (const SID*)group3,
+ 4,
+ (int)TRUE, (const SID*)owner3, 0,
+ le32_to_cpu(FILE_READ_DATA | OWNER_RIGHTS),
+ (int)TRUE, (const SID*)group3, 0,
+ le32_to_cpu(FILE_WRITE_DATA),
+ (int)TRUE, (const SID*)group2, 0,
+ le32_to_cpu(FILE_WRITE_DATA | FILE_READ_DATA),
+ (int)TRUE, (const SID*)worldsid, 0,
+ le32_to_cpu(FILE_EXECUTE));
+ expect = 0731;
+ expectacc = 07070731;
+ expectdef = 0;
+ break;
+ case 5 : /* Vista/JP */
+ isdir = TRUE;
+ descr = build_dummy_descr(isdir, systemsid, systemsid,
+ 6,
+ (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, owner1, (int)0xb, (u32)0x10000000,
+ (int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
+ (int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
+ expectacc = expect = 0700;
+ expectdef = 0700;
+ break;
+ case 6 : /* Vista/JP2 */
+ isdir = TRUE;
+ descr = build_dummy_descr(isdir, systemsid, systemsid,
+ 7,
+ (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, owner1, (int)0xb, (u32)0x1f01ff,
+ (int)TRUE, systemsid, (int)0xb, (u32)0x1f01ff,
+ (int)TRUE, adminsid, (int)0xb, (u32)0x1f01ff,
+ (int)TRUE, owner3, (int)0x3, (u32)0x1200a9);
+ expectacc = 0500070700;
+ expectdef = 0700;
+ expect = 0700;
+ break;
+ case 7 : /* WinXP/JP */
+ isdir = TRUE;
+ descr = build_dummy_descr(isdir, adminsid, systemsid,
+ 6,
+ (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, owner1, (int)0xb, (u32)0x10000000,
+ (int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
+ (int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
+ expectacc = expect = 0700;
+ expectdef = 0700;
+ break;
+ case 8 : /* WinXP/JP2 */
+ isdir = TRUE;
+ descr = build_dummy_descr(isdir, adminsid, systemsid,
+ 6,
+ (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
+ (int)TRUE, owner1, (int)0xb, (u32)0x10000000,
+ (int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
+ (int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
+ expectacc = expect = 0700;
+ expectdef = 0700;
+ break;
+ default :
+ expectacc = expectdef = 0;
+ break;
+ }
+ if (descr) {
+ perms = linux_permissions(descr, isdir);
+ if (perms != expect) {
+ printf("** Error in sample %d, perms 0%03o expected 0%03o\n",
+ cnt,perms,expect);
+ showall(descr,0);
+ errors++;
+ } else {
+#if POSIXACLS
+ pxdesc = linux_permissions_posix(descr, isdir);
+ if (pxdesc) {
+ accrights = merge_rights(pxdesc,FALSE);
+ defrights = merge_rights(pxdesc,TRUE);
+ if (!(pxdesc->tagsset & ~(POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)))
+ mixmode = expect;
+ else
+ mixmode = (expect & 07707) | ((accrights >> 9) & 070);
+ if ((pxdesc->mode != mixmode)
+ || (accrights != expectacc)
+ || (defrights != expectdef)) {
+ printf("** Error in sample %d : mode %03o expected 0%03o\n",
+ cnt,pxdesc->mode,mixmode);
+ printf(" Posix access rights 0%03lo expected 0%03lo\n",
+ (long)accrights,(long)expectacc);
+ printf(" default rights 0%03lo expected 0%03lo\n",
+ (long)defrights,(long)expectdef);
+ showall(descr,0);
+ showposix(pxdesc);
+exit(1);
+ }
+ free(pxdesc);
+ }
+#endif
+ }
+ free(descr);
+ }
+ }
+}
+
+
+/*
+ * Check whether any basic permission setting is interpreted
+ * back exactly as set
+ */
+
+void basictest(int kind, BOOL isdir, const SID *owner, const SID *group)
+{
+ char *attr;
+ mode_t perm;
+ mode_t gotback;
+ u32 count;
+ u32 acecount;
+ u32 globhash;
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+ const ACL *pacl;
+ enum { ERRNO,
+ ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
+ ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
+ } err;
+ u32 expectcnt[] = {
+ 27800, 31896,
+ 24064, 28160,
+ 24064, 28160,
+ 24064, 28160,
+ 25416, 29512
+ } ;
+ u32 expecthash[] = {
+ 0x8f80865b, 0x7bc7960,
+ 0x8fd9ecfe, 0xddd4db0,
+ 0xa8b07400, 0xa189c20,
+ 0xc5689a00, 0xb6c09000,
+ 0x94bfb419, 0xa4311791
+ } ;
+#if POSIXACLS
+ struct POSIX_SECURITY *pxdesc;
+ char *pxattr;
+ u32 pxcount;
+ u32 pxacecount;
+ u32 pxglobhash;
+#endif
+
+ count = 0;
+ acecount = 0;
+ globhash = 0;
+#if POSIXACLS
+ pxcount = 0;
+ pxacecount = 0;
+ pxglobhash = 0;
+#endif
+ for (perm=0; (perm<=07777) && (errors < 10); perm++) {
+ err = ERRNO;
+ /* file owned by plain user and group */
+ attr = ntfs_build_descr(perm,isdir,owner,(const SID*)group);
+ if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
+ acecount += le16_to_cpu(pacl->ace_count);
+ globhash += hash((const le32*)attr,ntfs_attr_size(attr));
+ count++;
+#if POSIXACLS
+ /*
+ * Build a NTFS ACL from a mode, and
+ * decode to a Posix ACL, expecting to
+ * get the original mode back.
+ */
+ pxdesc = linux_permissions_posix(attr, isdir);
+ if (!pxdesc || (pxdesc->mode != perm)) {
+ err = ERRAP;
+ if (pxdesc)
+ gotback = pxdesc->mode;
+ else
+ gotback = 0;
+ } else {
+ /*
+ * Build a NTFS ACL from the Posix ACL, expecting to
+ * get exactly the same NTFS ACL, then decode to a
+ * mode, expecting to get the original mode back.
+ */
+ pxattr = ntfs_build_descr_posix(context.mapping,
+ pxdesc,isdir,owner,
+ (const SID*)group);
+ if (pxattr && !memcmp(pxattr,attr,
+ ntfs_attr_size(attr))) {
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
+ pxacecount += le16_to_cpu(pacl->ace_count);
+ pxglobhash += hash((const le32*)attr,ntfs_attr_size(attr));
+ pxcount++;
+ gotback = linux_permissions(pxattr, isdir);
+ if (gotback != perm)
+ err = ERRAM;
+ else
+ free(pxattr);
+ } else
+ err = ERRPA;
+ free(attr);
+ }
+ free(pxdesc);
+#else
+ gotback = linux_permissions(attr, isdir);
+ if (gotback != perm)
+ err = ERRAM;
+ else
+ free(attr);
+#endif /* POSIXACLS */
+ } else
+ err = ERRMA;
+
+ switch (err) {
+ case ERRMA :
+ printf("** no or wrong permission settings "
+ "for kind %d perm %03o\n",kind,perm);
+ if (attr && opt_v)
+ hexdump(attr,ntfs_attr_size(attr),8);
+ if (attr && (opt_v >= 2)) {
+ showheader(attr,4);
+ showusid(attr,4);
+ showgsid(attr,4);
+ showdacl(attr,isdir,4);
+ showsacl(attr,isdir,4);
+ }
+ errors++;
+ break;
+ case ERRPA :
+ printf("** no or wrong permission settings from PX "
+ "for kind %d perm %03o\n",kind,perm);
+ errors++;
+ break;
+#if POSIXACLS
+ case ERRAM :
+ printf("** wrong permission settings, "
+ "kind %d perm 0%03o, gotback %03o\n",
+ kind, perm, gotback);
+ if (opt_v)
+ hexdump(pxattr,ntfs_attr_size(pxattr),8);
+ if (opt_v >= 2) {
+ showheader(pxattr,4);
+ showusid(pxattr,4);
+ showgsid(pxattr,4);
+ showdacl(pxattr,isdir,4);
+ showsacl(pxattr,isdir,4);
+ }
+ errors++;
+ break;
+ case ERRAP :
+ /* continued */
+#else
+ case ERRAM :
+ case ERRAP :
+#endif /* POSIXACLS */
+ printf("** wrong permission settings, "
+ "kind %d perm 0%03o, gotback %03o\n",
+ kind, perm, gotback);
+ if (opt_v)
+ hexdump(attr,ntfs_attr_size(attr),8);
+ if (opt_v >= 2) {
+ showheader(attr,4);
+ showusid(attr,4);
+ showgsid(attr,4);
+ showdacl(attr,isdir,4);
+ showsacl(attr,isdir,4);
+ }
+ errors++;
+ free(attr);
+ break;
+ default :
+ break;
+ }
+ }
+ printf("%lu ACLs built from mode, %lu ACE built, mean count %lu.%02lu\n",
+ (unsigned long)count,(unsigned long)acecount,
+ (unsigned long)acecount/count,acecount*100L/count%100L);
+ if (acecount != expectcnt[kind]) {
+ printf("** Error : expected ACE count %lu\n",
+ (unsigned long)expectcnt[kind]);
+ errors++;
+ }
+ if (globhash != expecthash[kind]) {
+ printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
+ (unsigned long)globhash, (unsigned long)expecthash[kind]);
+ errors++;
+ }
+#if POSIXACLS
+ printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
+ (unsigned long)pxcount,(unsigned long)pxacecount,
+ (unsigned long)pxacecount/pxcount,pxacecount*100L/pxcount%100L);
+ if (pxacecount != expectcnt[kind]) {
+ printf("** Error : expected ACE count %lu\n",
+ (unsigned long)expectcnt[kind]);
+ errors++;
+ }
+ if (pxglobhash != expecthash[kind]) {
+ printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
+ (unsigned long)pxglobhash, (unsigned long)expecthash[kind]);
+ errors++;
+ }
+#endif /* POSIXACLS */
+}
+
+#if POSIXACLS
+
+/*
+ * Check whether Posix ACL settings are interpreted
+ * back exactly as set
+ */
+
+void posixtest(int kind, BOOL isdir,
+ const SID *owner, const SID *group)
+{
+ struct POSIX_SECURITY *pxdesc;
+ struct {
+ struct POSIX_SECURITY pxdesc;
+ struct POSIX_ACE aces[10];
+ } desc;
+ int ownobj;
+ int grpobj;
+ int usr;
+ int grp;
+ int wrld;
+ int mask;
+ int mindes, maxdes;
+ int minmsk, maxmsk;
+ char *pxattr;
+ u32 count;
+ u32 acecount;
+ u32 globhash;
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+ const ACL *pacl;
+ struct POSIX_SECURITY *gotback;
+ enum { ERRNO,
+ ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
+ ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
+ } err;
+ u32 expectcnt[] = {
+#ifdef STSC
+ 32400, 34992,
+ 25920, 28512,
+ 25920, 28512,
+ 25920, 28512,
+ 26460, 29052,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 24516, 27108,
+ 20736, 23328,
+ 20736, 23328,
+ 20736, 23328,
+ 21060, 23652,
+#else
+ 252720, 273456,
+ 199584, 220320,
+ 199584, 220320,
+ 199584, 220320,
+ 203904, 224640,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 196452, 217188,
+ 165888, 186624,
+ 165888, 186624,
+ 165888, 186624,
+ 168480, 189216,
+#endif
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 16368, 18672,
+ 0, 0,
+ 13824, 0,
+ 0, 0,
+ 14640, 0
+ } ;
+ u32 expecthash[] = {
+#ifdef STSC
+ 0xf9f82115, 0x40666647,
+ 0xde826d30, 0xa181b660,
+ 0x952d4500, 0x8ac49450,
+ 0xf80acee0, 0xbd9ec6c0,
+ 0xfe09b868, 0xde24e84d,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0x2381438d, 0x3ab42dc6,
+ 0x7cccf6f8, 0x108ad430,
+ 0x5e448840, 0x83ab6c40,
+ 0x9b037100, 0x8f7c3b40,
+ 0x04a359dc, 0xa4619609,
+#else
+ 0x1808a6cd, 0xd82f7c60,
+ 0x5ad29e85, 0x518c7620,
+ 0x188ce270, 0x7e44e590,
+ 0x48a64800, 0x5bdf0030,
+ 0x1c64aec6, 0x8b0168fa,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0x169fb80e, 0x382d9a59,
+ 0xf9c28164, 0x1855d352,
+ 0xf9685700, 0x44d16700,
+ 0x587ebe90, 0xf7c51480,
+ 0x2cb1b518, 0x52408df6,
+#endif
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0x905f2e38, 0xd40c22f0,
+ 0, 0,
+ 0xdd76da00, 0,
+ 0, 0,
+ 0x718e34a0, 0
+ };
+
+ count = 0;
+ acecount = 0;
+ globhash = 0;
+ /* fill headers */
+ pxdesc = &desc.pxdesc;
+ pxdesc->mode = 0;
+ pxdesc->defcnt = 0;
+ if (kind & 32) {
+ pxdesc->acccnt = 4;
+ pxdesc->firstdef = 4;
+ pxdesc->tagsset = 0x35;
+ } else {
+ pxdesc->acccnt = 6;;
+ pxdesc->firstdef = 6;
+ pxdesc->tagsset = 0x3f;
+ }
+ pxdesc->acl.version = POSIX_VERSION;
+ pxdesc->acl.flags = 0;
+ pxdesc->acl.filler = 0;
+ /* prefill aces */
+ pxdesc->acl.ace[0].tag = POSIX_ACL_USER_OBJ;
+ pxdesc->acl.ace[0].id = -1;
+ if (kind & 32) {
+ pxdesc->acl.ace[1].tag = POSIX_ACL_GROUP_OBJ;
+ pxdesc->acl.ace[1].id = -1;
+ pxdesc->acl.ace[2].tag = POSIX_ACL_MASK;
+ pxdesc->acl.ace[2].id = -1;
+ pxdesc->acl.ace[3].tag = POSIX_ACL_OTHER;
+ pxdesc->acl.ace[3].id = -1;
+ } else {
+ pxdesc->acl.ace[1].tag = POSIX_ACL_USER;
+ pxdesc->acl.ace[1].id = (kind & 16 ? 0 : 1000);
+ pxdesc->acl.ace[2].tag = POSIX_ACL_GROUP_OBJ;
+ pxdesc->acl.ace[2].id = -1;
+ pxdesc->acl.ace[3].tag = POSIX_ACL_GROUP;
+ pxdesc->acl.ace[3].id = (kind & 16 ? 0 : 1002);
+ pxdesc->acl.ace[4].tag = POSIX_ACL_MASK;
+ pxdesc->acl.ace[4].id = -1;
+ pxdesc->acl.ace[5].tag = POSIX_ACL_OTHER;
+ pxdesc->acl.ace[5].id = -1;
+ }
+
+ mindes = 3;
+ maxdes = (kind & 32 ? mindes : 6);
+#ifdef STSC
+ minmsk = (kind & 32 ? 0 : 3);
+ maxmsk = (kind & 32 ? 7 : 3);
+#else
+ minmsk = 0;
+ maxmsk = 7;
+#endif
+ for (mask=minmsk; mask<=maxmsk; mask++)
+ for (ownobj=1; ownobj<7; ownobj++)
+ for (grpobj=1; grpobj<7; grpobj++)
+ for (wrld=0; wrld<8; wrld++)
+ for (usr=mindes; usr<=maxdes; usr++)
+ if (usr != 4)
+ for (grp=mindes; grp<=maxdes; grp++)
+ if (grp != 4) {
+ pxdesc->mode = (ownobj << 6) | (mask << 3) | wrld;
+
+ pxdesc->acl.ace[0].perms = ownobj;
+ if (kind & 32) {
+ pxdesc->acl.ace[1].perms = grpobj;
+ pxdesc->acl.ace[2].perms = mask;
+ pxdesc->acl.ace[3].perms = wrld;
+ } else {
+ pxdesc->acl.ace[1].perms = usr;
+ pxdesc->acl.ace[2].perms = grpobj;
+ pxdesc->acl.ace[3].perms = grp;
+ pxdesc->acl.ace[4].perms = mask;
+ pxdesc->acl.ace[5].perms = wrld;
+ }
+
+ err = ERRNO;
+ gotback = (struct POSIX_SECURITY*)NULL;
+ pxattr = ntfs_build_descr_posix(context.mapping,
+ pxdesc,isdir,owner,group);
+ if (pxattr && ntfs_valid_descr(pxattr, ntfs_attr_size(pxattr))) {
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)pxattr;
+ pacl = (const ACL*)&pxattr[le32_to_cpu(phead->dacl)];
+ acecount += le16_to_cpu(pacl->ace_count);
+ globhash += hash((const le32*)pxattr,ntfs_attr_size(pxattr));
+ count++;
+ gotback = linux_permissions_posix(pxattr, isdir);
+ if (gotback) {
+ if (ntfs_valid_posix(gotback)) {
+ if (!same_posix(pxdesc,gotback)) {
+ printf("Non matching got back Posix ACL\n");
+ printf("input ACL\n");
+ showposix(pxdesc);
+ printf("NTFS owner\n");
+ showusid(pxattr,4);
+ printf("NTFS group\n");
+ showgsid(pxattr,4);
+ printf("NTFS DACL\n");
+ showdacl(pxattr,isdir,4);
+ printf("gotback ACL\n");
+ showposix(gotback);
+ errors++;
+exit(1);
+ }
+ } else {
+ printf("Got back an invalid Posix ACL\n");
+ errors++;
+ }
+ free(gotback);
+ } else {
+ printf("Could not get Posix ACL back\n");
+ errors++;
+ }
+
+ } else {
+ printf("NTFS ACL incorrect or not build\n");
+ printf("input ACL\n");
+ showposix(pxdesc);
+ printf("NTFS DACL\n");
+ if (pxattr)
+ showdacl(pxattr,isdir,4);
+ else
+ printf(" (none)\n");
+ if (gotback) {
+ printf("gotback ACL\n");
+ showposix(gotback);
+ } else
+ printf("no gotback ACL\n");
+ errors++;
+ }
+ if (pxattr)
+ free(pxattr);
+ }
+ printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
+ (unsigned long)count,(unsigned long)acecount,
+ (unsigned long)acecount/count,acecount*100L/count%100L);
+ if (acecount != expectcnt[kind]) {
+ printf("** Error ! expected ACE count %lu\n",
+ (unsigned long)expectcnt[kind]);
+ errors++;
+ }
+ if (globhash != expecthash[kind]) {
+ printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
+ (unsigned long)globhash, (unsigned long)expecthash[kind]);
+ errors++;
+ }
+}
+
+#endif /* POSIXACLS */
+
+void selftests(void)
+{
+ le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
+ cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
+ } ;
+ le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
+ {
+ cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
+ cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
+ cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
+ } ;
+#if POSIXACLS
+#ifdef STSC
+ unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 16, 17, 18, 20, 22, 24,
+ 32, 33, 36, 40 } ;
+#else
+ unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 16, 17, 18, 20, 22, 24, 19, 21, 23, 25,
+ 32, 33, 36, 40 } ;
+#endif
+ unsigned int k;
+#endif /* POSIXACLS */
+ int kind;
+ const SID *owner;
+ const SID *group;
+ BOOL isdir;
+
+#if POSIXACLS
+ local_build_mapping(context.mapping, (const char*)NULL);
+#endif
+ /* first check samples */
+ mappingtype = MAPDUMMY;
+ check_samples();
+if (errors) exit(1);
+ /*
+ * kind is oring of :
+ * 1 : directory
+ * 2 : owner is root
+ * 4 : group is root
+ * 8 : group is owner
+ * 16 : root is designated user/group
+ * 32 : mask present with no designated user/group
+ */
+ for (kind=0; (kind<10) && (errors<10); kind++) {
+ isdir = kind & 1;
+ if (kind & 8)
+ owner = (const SID*)group_sid;
+ else
+ owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
+ group = (kind & 4 ? adminsid : (const SID*)group_sid);
+ basictest(kind, isdir, owner, group);
+ }
+#if POSIXACLS
+ for (k=0; (k<sizeof(kindlist)) && (errors<10); k++) {
+ kind = kindlist[k];
+ isdir = kind & 1;
+ if (kind & 8)
+ owner = (const SID*)group_sid;
+ else
+ owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
+ group = (kind & 4 ? adminsid : (const SID*)group_sid);
+ posixtest(kind, isdir, owner, group);
+ }
+ ntfs_free_mapping(context.mapping);
+#endif
+ if (errors >= 10)
+ printf("** too many errors, test aborted\n");
+}
+#endif /* SELFTESTS & !USESTUBS */
+
+#ifdef WIN32
+
+/*
+ * Get the security descriptor of a file (Windows version)
+ */
+
+unsigned int getfull(char *attr, const char *fullname)
+{
+ static char part[MAXATTRSZ];
+ BIGSID ownsid;
+ int xowner;
+ int ownersz;
+ u16 ownerfl;
+ ULONG attrsz;
+ ULONG partsz;
+ BOOL overflow;
+
+ attrsz = 0;
+ partsz = 0;
+ overflow = FALSE;
+ if (GetFileSecurityW((LPCWSTR)fullname,OWNER_SECURITY_INFORMATION,
+ (char*)part,MAXATTRSZ,&partsz)) {
+ xowner = get4l(part,4);
+ if (xowner) {
+ ownerfl = get2l(part,2);
+ ownersz = ntfs_sid_size((SID*)&part[xowner]);
+ if (ownersz <= (int)sizeof(BIGSID))
+ memcpy(ownsid,&part[xowner],ownersz);
+ else
+ overflow = TRUE;
+ } else {
+ ownerfl = 0;
+ ownersz = 0;
+ }
+ /*
+ * SACL : just feed in or clean
+ */
+ if (!GetFileSecurityW((LPCWSTR)fullname,SACL_SECURITY_INFORMATION,
+ (char*)attr,MAXATTRSZ,&attrsz)) {
+ attrsz = 20;
+ set4l(attr,0);
+ attr[0] = SECURITY_DESCRIPTOR_REVISION;
+ set4l(&attr[12],0);
+ if (opt_v >= 2)
+ printf(" No SACL\n");
+ }
+ /*
+ * append DACL and merge its flags
+ */
+ partsz = 0;
+ set4l(&attr[16],0);
+ if (GetFileSecurityW((LPCWSTR)fullname,DACL_SECURITY_INFORMATION,
+ (char*)part,MAXATTRSZ,&partsz)) {
+ if ((attrsz + partsz - 20) <= MAXATTRSZ) {
+ memcpy(&attr[attrsz],&part[20],partsz-20);
+ set4l(&attr[16],(partsz > 20 ? attrsz : 0));
+ set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
+ & const_le16_to_cpu(SE_DACL_PROTECTED
+ | SE_DACL_AUTO_INHERITED
+ | SE_DACL_PRESENT)));
+ attrsz += partsz - 20;
+ } else
+ overflow = TRUE;
+ } else
+ if (partsz > MAXATTRSZ)
+ overflow = TRUE;
+ else {
+ if (opt_b)
+ printf("# No discretionary access control list\n");
+ else
+ printf(" No discretionary access control list\n");
+ warnings++;
+ }
+
+ /*
+ * append owner and merge its flag
+ */
+ if (xowner && !overflow) {
+ memcpy(&attr[attrsz],ownsid,ownersz);
+ set4l(&attr[4],attrsz);
+ set2l(&attr[2],get2l(attr,2)
+ | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED)));
+ attrsz += ownersz;
+ } else
+ set4l(&attr[4],0);
+ /*
+ * append group
+ */
+ partsz = 0;
+ set4l(&attr[8],0);
+ if (GetFileSecurityW((LPCWSTR)fullname,GROUP_SECURITY_INFORMATION,
+ (char*)part,MAXATTRSZ,&partsz)) {
+ if ((attrsz + partsz - 20) <= MAXATTRSZ) {
+ memcpy(&attr[attrsz],&part[20],partsz-20);
+ set4l(&attr[8],(partsz > 20 ? attrsz : 0));
+ set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
+ & const_le16_to_cpu(SE_GROUP_DEFAULTED)));
+ attrsz += partsz - 20;
+ } else
+ overflow = TRUE;
+ } else
+ if (partsz > MAXATTRSZ)
+ overflow = TRUE;
+ else {
+ printf("** No group SID\n");
+ warnings++;
+ }
+ set2l(&attr[2],get2l(attr,2)
+ | const_le16_to_cpu(SE_SELF_RELATIVE));
+ if (overflow) {
+ printf("** Descriptor was too long (> %d)\n",MAXATTRSZ);
+ warnings++;
+ attrsz = 0;
+ } else
+ if (!ntfs_valid_descr((char*)attr,attrsz)) {
+ printf("** Descriptor for ");
+ printname(stdout,fullname);
+ printf(" is not valid\n");
+ errors++;
+ attrsz = 0;
+ }
+
+ } else {
+ printf("** Could not get owner of ");
+ printname(stdout,fullname);
+ printf(", partsz %d\n",partsz);
+ printerror(stdout);
+ warnings++;
+ attrsz = 0;
+ }
+ return (attrsz);
+}
+
+/*
+ * Update a security descriptor (Windows version)
+ */
+
+BOOL updatefull(const char *name, DWORD flags, char *attr)
+{
+ BOOL bad;
+
+ bad = !SetFileSecurityW((LPCWSTR)name, flags, attr);
+ if (bad
+ && (flags & OWNER_SECURITY_INFORMATION)
+ && (GetLastError() == 1307)) {
+ printf("** Could not set owner of ");
+ printname(stdout,name);
+ printf(", retrying with no owner setting\n");
+ warnings++;
+ bad = !SetFileSecurityW((LPCWSTR)name,
+ flags & ~OWNER_SECURITY_INFORMATION, (char*)attr);
+ }
+ if (bad) {
+ printf("** Could not change attributes of ");
+ printname(stdout,name);
+ printf("\n");
+ printerror(stdout);
+ errors++;
+ }
+ return (!bad);
+}
+
+#else
+
+/*
+ * Get the security descriptor of a file (Linux version)
+ */
+
+unsigned int getfull(char *attr, const char *fullname)
+{
+ static char part[MAXATTRSZ];
+ BIGSID ownsid;
+ int xowner;
+ int ownersz;
+ u16 ownerfl;
+ u32 attrsz;
+ u32 partsz;
+ BOOL overflow;
+
+ attrsz = 0;
+ partsz = 0;
+ overflow = FALSE;
+ if (ntfs_get_file_security(ntfs_context,fullname,
+ OWNER_SECURITY_INFORMATION,
+ (char*)part,MAXATTRSZ,&partsz)) {
+ xowner = get4l(part,4);
+ if (xowner) {
+ ownerfl = get2l(part,2);
+ ownersz = ntfs_sid_size((SID*)&part[xowner]);
+ if (ownersz <= (int)sizeof(BIGSID))
+ memcpy(ownsid,&part[xowner],ownersz);
+ else
+ overflow = TRUE;
+ } else {
+ ownerfl = 0;
+ ownersz = 0;
+ }
+ /*
+ * SACL : just feed in or clean
+ */
+ if (!ntfs_get_file_security(ntfs_context,fullname,
+ SACL_SECURITY_INFORMATION,
+ (char*)attr,MAXATTRSZ,&attrsz)) {
+ attrsz = 20;
+ set4l(attr,0);
+ attr[0] = SECURITY_DESCRIPTOR_REVISION;
+ set4l(&attr[12],0);
+ if (opt_v >= 2)
+ printf(" No SACL\n");
+ }
+ /*
+ * append DACL and merge its flags
+ */
+ partsz = 0;
+ set4l(&attr[16],0);
+ if (ntfs_get_file_security(ntfs_context,fullname,
+ DACL_SECURITY_INFORMATION,
+ (char*)part,MAXATTRSZ,&partsz)) {
+ if ((attrsz + partsz - 20) <= MAXATTRSZ) {
+ memcpy(&attr[attrsz],&part[20],partsz-20);
+ set4l(&attr[16],(partsz > 20 ? attrsz : 0));
+ set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
+ & const_le16_to_cpu(SE_DACL_PROTECTED
+ | SE_DACL_AUTO_INHERITED
+ | SE_DACL_PRESENT)));
+ attrsz += partsz - 20;
+ } else
+ overflow = TRUE;
+ } else
+ if (partsz > MAXATTRSZ)
+ overflow = TRUE;
+ else {
+ if (opt_b)
+ printf("# No discretionary access control list\n");
+ else
+ printf(" No discretionary access control list\n");
+ warnings++;
+ }
+
+ /*
+ * append owner and merge its flag
+ */
+ if (xowner && !overflow) {
+ memcpy(&attr[attrsz],ownsid,ownersz);
+ set4l(&attr[4],attrsz);
+ set2l(&attr[2],get2l(attr,2)
+ | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED)));
+ attrsz += ownersz;
+ } else
+ set4l(&attr[4],0);
+ /*
+ * append group
+ */
+ partsz = 0;
+ set4l(&attr[8],0);
+ if (ntfs_get_file_security(ntfs_context,fullname,
+ GROUP_SECURITY_INFORMATION,
+ (char*)part,MAXATTRSZ,&partsz)) {
+ if ((attrsz + partsz - 20) <= MAXATTRSZ) {
+ memcpy(&attr[attrsz],&part[20],partsz-20);
+ set4l(&attr[8],(partsz > 20 ? attrsz : 0));
+ set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
+ & const_le16_to_cpu(SE_GROUP_DEFAULTED)));
+ attrsz += partsz - 20;
+ } else
+ overflow = TRUE;
+ } else
+ if (partsz > MAXATTRSZ)
+ overflow = TRUE;
+ else {
+ printf("** No group SID\n");
+ warnings++;
+ }
+ if (overflow) {
+ printf("** Descriptor was too long (> %d)\n",MAXATTRSZ);
+ warnings++;
+ attrsz = 0;
+ } else
+ if (!ntfs_valid_descr((char*)attr,attrsz)) {
+ printf("** Descriptor for %s is not valid\n",fullname);
+ errors++;
+ attrsz = 0;
+ }
+
+ } else {
+ printf("** Could not get owner of %s\n",fullname);
+ warnings++;
+ attrsz = 0;
+ }
+ return (attrsz);
+}
+
+/*
+ * Update a security descriptor (Linux version)
+ */
+
+BOOL updatefull(const char *name, DWORD flags, char *attr)
+{
+ BOOL ok;
+
+ ok = !ntfs_set_file_security(ntfs_context, name, flags, attr);
+ if (ok) {
+ printf("** Could not change attributes of %s\n",name);
+ printerror(stdout);
+ errors++;
+ }
+ return (ok);
+}
+
+
+#endif
+
+#if POSIXACLS
+
+/*
+ * Set all the parameters associated to a file
+ */
+
+BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc,
+ BOOL isdir)
+{
+ static char attr[MAXATTRSZ];
+ struct POSIX_SECURITY *oldpxdesc;
+ struct POSIX_SECURITY *newpxdesc;
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+ char *newattr;
+ int err;
+ unsigned int attrsz;
+ int newattrsz;
+ const SID *usid;
+ const SID *gsid;
+#if OWNERFROMACL
+ const SID *osid;
+#endif
+
+ printf("%s ",(isdir ? "Directory" : "File"));
+ printname(stdout,fullname);
+ if (pxdesc->acccnt)
+ printf("\n");
+ else
+ printf(" mode 0%03o\n",pxdesc->mode);
+
+ err = FALSE;
+ attrsz = getfull(attr, fullname);
+ if (attrsz) {
+ oldpxdesc = linux_permissions_posix(attr, isdir);
+ if (opt_v >= 2) {
+ printf("Posix equivalent of old ACL :\n");
+ showposix(oldpxdesc);
+ }
+ if (oldpxdesc) {
+ if (!pxdesc->defcnt
+ && !(pxdesc->tagsset &
+ (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK))) {
+ if (!ntfs_merge_mode_posix(oldpxdesc,pxdesc->mode))
+ newpxdesc = oldpxdesc;
+ else {
+ newpxdesc = (struct POSIX_SECURITY*)NULL;
+ free(oldpxdesc);
+ }
+ } else {
+ newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
+ free(oldpxdesc);
+ }
+ if (opt_v) {
+ printf("New Posix ACL :\n");
+ showposix(newpxdesc);
+ }
+ } else
+ newpxdesc = (struct POSIX_SECURITY*)NULL;
+ if (newpxdesc) {
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
+#if OWNERFROMACL
+ osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+ usid = ntfs_acl_owner((const char*)attr);
+ if (!ntfs_same_sid(usid,osid))
+ printf("== Windows owner might change\n");
+#else
+ usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+#endif
+ newattr = ntfs_build_descr_posix(context.mapping,
+ newpxdesc,isdir,usid,gsid);
+ free(newpxdesc);
+ } else
+ newattr = (char*)NULL;
+ if (newattr) {
+ newattrsz = ntfs_attr_size(newattr);
+ if (opt_v) {
+ printf("New NTFS security descriptor\n");
+ hexdump(newattr,newattrsz,4);
+ }
+ if (opt_v >= 2) {
+ printf("Expected hash : 0x%08lx\n",
+ (unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
+ showheader(newattr,0);
+ showusid(newattr,0);
+ showgsid(newattr,0);
+ showdacl(newattr,isdir,0);
+ showsacl(newattr,isdir,0);
+ }
+
+#ifdef WIN32
+ /*
+ * avoid getting a set owner error on Windows
+ * owner should not be changed anyway
+ */
+ if (!updatefull(fullname,
+ DACL_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | OWNER_SECURITY_INFORMATION,
+ newattr))
+#else
+ if (!updatefull(fullname,
+ DACL_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | OWNER_SECURITY_INFORMATION,
+ newattr))
+#endif
+ err = TRUE;
+/*
+{
+struct POSIX_SECURITY *interp;
+printf("Reinterpreted new Posix :\n");
+interp = linux_permissions_posix(newattr,isdir);
+showposix(interp);
+free(interp);
+}
+*/
+ free(newattr);
+ } else
+ err = TRUE;
+ } else
+ err = TRUE;
+ return (!err);
+}
+
+#endif
+
+BOOL setfull(const char *fullname, int mode, BOOL isdir)
+{
+ static char attr[MAXATTRSZ];
+ const SECURITY_DESCRIPTOR_RELATIVE *phead;
+ char *newattr;
+ int err;
+ unsigned int attrsz;
+ int newattrsz;
+ const SID *usid;
+ const SID *gsid;
+#if OWNERFROMACL
+ const SID *osid;
+#endif
+
+ printf("%s ",(isdir ? "Directory" : "File"));
+ printname(stdout,fullname);
+ printf(" mode 0%03o\n",mode);
+ attrsz = getfull(attr, fullname);
+ err = FALSE;
+ if (attrsz) {
+ phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
+ gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
+#if OWNERFROMACL
+ osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+ usid = ntfs_acl_owner((const char*)attr);
+ if (!ntfs_same_sid(usid,osid))
+ printf("== Windows owner might change\n");
+#else
+ usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
+#endif
+ newattr = ntfs_build_descr(mode,isdir,usid,gsid);
+ if (newattr) {
+ newattrsz = ntfs_attr_size(newattr);
+ if (opt_v) {
+ printf("Security descriptor\n");
+ hexdump(newattr,newattrsz,4);
+ }
+ if (opt_v >= 2) {
+ printf("Expected hash : 0x%08lx\n",
+ (unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
+ showheader(newattr,0);
+ showusid(newattr,0);
+ showgsid(newattr,0);
+ showdacl(newattr,isdir,0);
+ showsacl(newattr,isdir,0);
+ }
+
+#ifdef WIN32
+ /*
+ * avoid getting a set owner error on Windows
+ * owner should not be changed anyway
+ */
+ if (!updatefull(fullname,
+ DACL_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | OWNER_SECURITY_INFORMATION,
+ newattr))
+#else
+ if (!updatefull(fullname,
+ DACL_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION
+ | OWNER_SECURITY_INFORMATION,
+ newattr))
+#endif
+ err = TRUE;
+ free(newattr);
+ }
+
+ } else
+ err = TRUE;
+ return (err);
+}
+
+#ifdef WIN32
+
+/*
+ * Check whether a directory with reparse data is a junction
+ * or a symbolic link
+ */
+
+BOOL islink(const char *filename)
+{
+ WIN32_FIND_DATAW found;
+ HANDLE search;
+ BOOL link;
+
+ link = FALSE;
+ search = FindFirstFileW((LPCWSTR)filename, &found);
+ if (search != INVALID_HANDLE_VALUE) {
+ link = (found.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)
+ || (found.dwReserved0 == IO_REPARSE_TAG_SYMLINK);
+ FindClose(search);
+ }
+ return (link);
+}
+
+#if POSIXACLS
+BOOL iterate(RECURSE call, const char *fullname, const struct POSIX_SECURITY *pxdesc)
+#else
+BOOL iterate(RECURSE call, const char *fullname, mode_t mode)
+#endif
+{
+ WIN32_FIND_DATAW found;
+ HANDLE search;
+ BOOL err;
+ unsigned int len;
+ char *filter;
+ char *inner;
+
+ err = FALSE;
+ filter = (char*)malloc(MAXFILENAME);
+ inner = (char*)malloc(MAXFILENAME);
+ if (filter && inner) {
+ len = utf16len(fullname);
+ memcpy(filter, fullname, 2*len);
+ makeutf16(&filter[2*len],"\\*.*");
+ search = FindFirstFileW((LPCWSTR)filter, &found);
+ if (search != INVALID_HANDLE_VALUE) {
+ do {
+ if (found.cFileName[0] != UNICODE('.')) {
+ memcpy(inner, fullname, 2*len);
+ inner[2*len] = '\\';
+ inner[2*len+1] = 0;
+ memcpy(&inner[2*len+2],found.cFileName,
+ 2*utf16len((char*)found.cFileName)+2);
+ if (opt_v)
+ if (opt_b)
+ printf("#\n#\n");
+ else
+ printf("\n\n");
+ switch (call) {
+ case RECSHOW :
+ if (recurseshow(inner))
+ err = TRUE;
+ break;
+#if POSIXACLS
+ case RECSETPOSIX :
+ if (recurseset_posix(inner,pxdesc))
+ err = TRUE;
+ break;
+#else
+ case RECSET :
+ if (recurseset(inner,mode))
+ err = TRUE;
+ break;
+#endif
+ default :
+ err = TRUE;
+ }
+ }
+ } while (FindNextFileW(search, &found));
+ FindClose(search);
+ }
+ free(filter);
+ free(inner);
+ } else {
+ printf("** Cannot process deeper : not enough memory\n");
+ errors++;
+ err = TRUE;
+ }
+ return (err);
+}
+
+
+
+/*
+ * Display all the parameters associated to a file (Windows version)
+ */
+
+void showfull(const char *fullname, BOOL isdir)
+{
+ static char attr[MAXATTRSZ];
+#if POSIXACLS
+ struct POSIX_SECURITY *pxdesc;
+#endif
+ ULONG attrsz;
+ int mode;
+ uid_t uid;
+ gid_t gid;
+ int attrib;
+ int level;
+
+ printf("%s ",(isdir ? "Directory" : "File"));
+ printname(stdout,fullname);
+ printf("\n");
+
+ /* get individual parameters, as when trying to get them */
+ /* all, and one (typically SACL) is missing, we get none, */
+ /* and concatenate them, to be able to compute the hash code */
+
+ attrsz = getfull(attr, fullname);
+ if (attrsz) {
+ if (opt_v || opt_b) {
+ hexdump(attr,attrsz,8);
+ printf("Computed hash : 0x%08lx\n",
+ (unsigned long)hash((le32*)attr,attrsz));
+ }
+ if (opt_v && opt_b) {
+ printf("# %s ",(isdir ? "Directory" : "File"));
+ printname(stdout,fullname);
+ printf(" hash 0x%lx\n",
+ (unsigned long)hash((le32*)attr,attrsz));
+ }
+ attrib = GetFileAttributesW((LPCWSTR)fullname);
+ if (attrib == INVALID_FILE_ATTRIBUTES) {
+ printf("** Could not get file attrib\n");
+ errors++;
+ } else
+ printf("Windows attrib : 0x%x\n",attrib);
+ if (ntfs_valid_descr(attr,attrsz)) {
+#if POSIXACLS
+ pxdesc = linux_permissions_posix(attr,isdir);
+ if (pxdesc)
+ mode = pxdesc->mode;
+ else
+ mode = 0;
+#else
+ mode = linux_permissions(attr,isdir);
+#endif
+ if (opt_v >= 2) {
+ level = (opt_b ? 4 : 0);
+ showheader(attr,level);
+ showusid(attr,level);
+ showgsid(attr,level);
+ showdacl(attr,isdir,level);
+ showsacl(attr,isdir,level);
+ }
+ uid = linux_owner(attr);
+ gid = linux_group(attr);
+ if (opt_b) {
+ printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n",
+ (int)uid,(int)gid,mode);
+ } else {
+ printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
+ (int)uid,(int)gid,mode);
+ }
+#if POSIXACLS
+ if (pxdesc) {
+ if (!opt_b
+ && (pxdesc->defcnt
+ || (pxdesc->tagsset
+ & (POSIX_ACL_USER
+ | POSIX_ACL_GROUP
+ | POSIX_ACL_MASK))))
+ showposix(pxdesc);
+ free(pxdesc);
+ }
+#endif
+ } else
+ if (opt_b)
+ printf("# Descriptor fails sanity check\n");
+ else
+ printf("Descriptor fails sanity check\n");
+ }
+}
+
+BOOL recurseshow(const char *fullname)
+{
+ int attrib;
+ int err;
+ BOOL isdir;
+
+ err = FALSE;
+ attrib = GetFileAttributesW((LPCWSTR)fullname);
+ if (attrib != INVALID_FILE_ATTRIBUTES) {
+ isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ showfull(fullname,isdir);
+ if (isdir
+ && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT)
+ && islink(fullname))) {
+#if POSIXACLS
+ err = iterate(RECSHOW, fullname, (struct POSIX_SECURITY*)NULL);
+#else
+ err = iterate(RECSHOW, fullname, 0);
+#endif
+ }
+ } else {
+ printf("** Could not access ");
+ printname(stdout,fullname);
+ printf("\n");
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (err);
+}
+
+
+BOOL singleshow(const char *fullname)
+{
+ int attrib;
+ int err;
+ BOOL isdir;
+
+ err = FALSE;
+ attrib = GetFileAttributesW((LPCWSTR)fullname);
+ if (attrib != INVALID_FILE_ATTRIBUTES) {
+ isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ showfull(fullname,isdir);
+ } else {
+ printf("** Could not access ");
+ printname(stdout,fullname);
+ printf("\n");
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (err);
+}
+
+#if POSIXACLS
+
+BOOL recurseset_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc)
+{
+ int attrib;
+ int err;
+ BOOL isdir;
+
+ err = FALSE;
+ attrib = GetFileAttributesW((LPCWSTR)fullname);
+ if (attrib != INVALID_FILE_ATTRIBUTES) {
+ isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ err = !setfull_posix(fullname,pxdesc,isdir);
+ if (err) {
+ printf("** Failed to update ");
+ printname(stdout,fullname);
+ printf("\n");
+ errors++;
+ } else
+ if (isdir
+ && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT)
+ && islink(fullname)))
+ iterate(RECSETPOSIX, fullname, pxdesc);
+ } else {
+ err = GetLastError();
+ printf("** Could not access ");
+ printname(stdout,fullname);
+ printf("\n");
+ printerror(stdout);
+ err = TRUE;
+ errors++;
+ }
+ return (err);
+}
+
+#else
+
+BOOL recurseset(const char *fullname, int mode)
+{
+ int attrib;
+ int err;
+ BOOL isdir;
+
+ err = FALSE;
+ attrib = GetFileAttributesW((LPCWSTR)fullname);
+ if (attrib != INVALID_FILE_ATTRIBUTES) {
+ isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ setfull(fullname,mode,isdir);
+ if (isdir
+ && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT)
+ && islink(fullname)))
+ iterate(RECSETPOSIX, fullname, mode);
+ } else {
+ err = GetLastError();
+ printf("** Could not access ");
+ printname(stdout,fullname);
+ printf("\n");
+ printerror(stdout);
+ err = TRUE;
+ errors++;
+ }
+ return (err);
+}
+
+#endif
+
+#if POSIXACLS
+
+BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
+{
+ BOOL isdir;
+ BOOL err;
+ int attrib;
+
+ err = FALSE;
+ attrib = GetFileAttributesW((LPCWSTR)path);
+ if (attrib != INVALID_FILE_ATTRIBUTES) {
+ isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY);
+ err = !setfull_posix(path,pxdesc,isdir);
+ if (err) {
+ printf("** Failed to update ");
+ printname(stdout,path);
+ printf("\n");
+ errors++;
+ }
+ } else {
+ printf("** Could not access ");
+ printname(stdout,path);
+ printf("\n");
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (!err);
+}
+
+#endif
+
+BOOL singleset(const char *path, int mode)
+{
+ BOOL isdir;
+ BOOL err;
+ int attrib;
+
+ err = FALSE;
+ attrib = GetFileAttributesW((LPCWSTR)path);
+ if (attrib != INVALID_FILE_ATTRIBUTES) {
+ isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY);
+ setfull(path,mode,isdir);
+ } else {
+ printf("** Could not access ");
+ printname(stdout,path);
+ printf("\n");
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (!err);
+}
+
+#else
+
+/*
+ * Display all the parameters associated to a file (Linux version)
+ */
+
+void showfull(const char *fullname, BOOL isdir)
+{
+ static char attr[MAXATTRSZ];
+ static char part[MAXATTRSZ];
+#if POSIXACLS
+ struct POSIX_SECURITY *pxdesc;
+#endif
+ struct SECURITY_DATA *psecurdata;
+ char *newattr;
+ int securindex;
+ int mode;
+ int level;
+ int attrib;
+ u32 attrsz;
+ u32 partsz;
+ uid_t uid;
+ gid_t gid;
+
+ if (opt_v || opt_b)
+ printf("%s %s\n",(isdir ? "Directory" : "File"),fullname);
+
+ /* get individual parameters, as when trying to get them */
+ /* all, and one (typically SACL) is missing, we get none */
+ /* and concatenate them, to be able to compute the checksum */
+
+ partsz = 0;
+ securindex = ntfs_get_file_security(ntfs_context,fullname,
+ OWNER_SECURITY_INFORMATION,
+ (char*)part,MAXATTRSZ,&partsz);
+
+ attrib = ntfs_get_file_attributes(ntfs_context, fullname);
+ if (attrib == INVALID_FILE_ATTRIBUTES) {
+ printf("** Could not get file attrib\n");
+ errors++;
+ }
+ if ((securindex < 0)
+ || (securindex >= MAXSECURID)
+ || ((securindex > 0)
+ && ((!opt_r && !opt_b)
+ || !securdata[securindex >> SECBLKSZ]
+ || !securdata[securindex >> SECBLKSZ][securindex & ((1 << SECBLKSZ) - 1)].filecount)))
+ {
+ if (opt_v || opt_b) {
+ if ((securindex < -1) || (securindex >= MAXSECURID))
+ printf("Security key : 0x%x out of range\n",securindex);
+ else
+ if (securindex == -1)
+ printf("Security key : none\n");
+ else
+ printf("Security key : 0x%x\n",securindex);
+ } else {
+ printf("%s %s",(isdir ? "Directory" : "File"),fullname);
+ if ((securindex < -1) || (securindex >= MAXSECURID))
+ printf(" : key 0x%x out of range\n",securindex);
+ else
+ if (securindex == -1)
+ printf(" : no key\n");
+ else
+ printf(" : key 0x%x\n",securindex);
+ }
+
+ attrsz = getfull(attr, fullname);
+ if (attrsz) {
+ psecurdata = (struct SECURITY_DATA*)NULL;
+ if ((securindex < MAXSECURID) && (securindex > 0)) {
+ if (!securdata[securindex >> SECBLKSZ])
+ newblock(securindex);
+ if (securdata[securindex >> SECBLKSZ])
+ psecurdata = &securdata[securindex >> SECBLKSZ]
+ [securindex & ((1 << SECBLKSZ) - 1)];
+ }
+ if (opt_v && (opt_a || opt_b) && psecurdata) {
+ newattr = (char*)malloc(attrsz);
+ printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"),
+ fullname,
+ (unsigned long)hash((le32*)attr,attrsz));
+ if (newattr) {
+ memcpy(newattr,attr,attrsz);
+ psecurdata->attr = newattr;
+ }
+ }
+ if ((opt_v || opt_b)
+ && ((securindex >= MAXSECURID)
+ || (securindex <= 0)
+ || !psecurdata
+ || (!psecurdata->filecount
+ && !psecurdata->flags))) {
+ hexdump(attr,attrsz,8);
+ printf("Computed hash : 0x%08lx\n",
+ (unsigned long)hash((le32*)attr,attrsz));
+ }
+ if (ntfs_valid_descr((char*)attr,attrsz)) {
+#if POSIXACLS
+ pxdesc = linux_permissions_posix(attr,isdir);
+ if (pxdesc)
+ mode = pxdesc->mode;
+ else
+ mode = 0;
+#else
+ mode = linux_permissions(attr,isdir);
+#endif
+ attrib = ntfs_get_file_attributes(ntfs_context,fullname);
+ if (opt_v >= 2) {
+ level = (opt_b ? 4 : 0);
+ showheader(attr,level);
+ showusid(attr,level);
+ showgsid(attr,level);
+ showdacl(attr,isdir,level);
+ showsacl(attr,isdir,level);
+ }
+ if (attrib != INVALID_FILE_ATTRIBUTES)
+ printf("Windows attrib : 0x%x\n",attrib);
+ uid = linux_owner(attr);
+ gid = linux_group(attr);
+ if (opt_b) {
+ printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n",
+ (int)uid,(int)gid,mode);
+ } else {
+ printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
+ (int)uid,(int)gid,mode);
+ }
+#if POSIXACLS
+ if (pxdesc) {
+ if (!opt_b
+ && (pxdesc->defcnt
+ || (pxdesc->tagsset
+ & (POSIX_ACL_USER
+ | POSIX_ACL_GROUP
+ | POSIX_ACL_MASK))))
+ showposix(pxdesc);
+#if USESTUBS
+ stdfree(pxdesc); /* allocated within library */
+#else
+ free(pxdesc);
+#endif
+ }
+#endif
+ if ((opt_r || opt_b) && (securindex < MAXSECURID)
+ && (securindex > 0) && psecurdata) {
+ psecurdata->filecount++;
+ psecurdata->mode = mode;
+ }
+ } else {
+ printf("** Descriptor fails sanity check\n");
+ errors++;
+ }
+ }
+ } else
+ if (securindex > 0) {
+ if (securdata[securindex >> SECBLKSZ]) {
+ psecurdata = &securdata[securindex >> SECBLKSZ]
+ [securindex & ((1 << SECBLKSZ) - 1)];
+ psecurdata->filecount++;
+ if (opt_b || opt_r) {
+ if (!opt_b && !opt_v)
+ printf("%s %s\n",(isdir ? "Directory" : "File"),fullname);
+ printf("Security key : 0x%x mode %03o (already displayed)\n",
+ securindex,psecurdata->mode);
+ if (attrib != INVALID_FILE_ATTRIBUTES)
+ printf("Windows attrib : 0x%x\n",attrib);
+ } else {
+ printf("%s %s",(isdir ? "Directory" : "File"),fullname);
+ printf(" : key 0x%x\n",securindex);
+ }
+ if ((opt_a || opt_b) && opt_v
+ && psecurdata && psecurdata->attr) {
+ printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"),
+ fullname,
+ (unsigned long)hash((le32*)psecurdata->attr,
+ ntfs_attr_size(psecurdata->attr)));
+ }
+ }
+ } else {
+ if (!opt_v && !opt_b)
+ printf("%s %s",(isdir ? "Directory" : "File"),fullname);
+ printf(" (Failed)\n");
+ printf("** Could not get security data of %s, partsz %d\n",
+ fullname,partsz);
+ printerror(stdout);
+ errors++;
+ }
+}
+
+BOOL recurseshow(const char *path)
+{
+ struct CALLBACK dircontext;
+ struct LINK *current;
+ BOOL isdir;
+ BOOL err;
+
+ err = FALSE;
+ dircontext.head = (struct LINK*)NULL;
+ dircontext.dir = path;
+ isdir = ntfs_read_directory(ntfs_context, path,
+ callback, &dircontext);
+ if (isdir) {
+ showfull(path,TRUE);
+ if (opt_v) {
+ if (opt_b)
+ printf("#\n#\n");
+ else
+ printf("\n\n");
+ }
+ while (dircontext.head) {
+ current = dircontext.head;
+ if (recurseshow(current->name)) err = TRUE;
+ dircontext.head = dircontext.head->next;
+ free(current);
+ }
+ } else
+ if (errno == ENOTDIR) {
+ showfull(path,FALSE);
+ if (opt_v) {
+ if (opt_b)
+ printf("#\n#\n");
+ else
+ printf("\n\n");
+ }
+ } else {
+ printf("** Could not access %s\n",path);
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (!err);
+}
+
+
+BOOL singleshow(const char *path)
+{
+ BOOL isdir;
+ BOOL err;
+
+ err = FALSE;
+ isdir = ntfs_read_directory(ntfs_context, path,
+ callback, (struct CALLBACK*)NULL);
+ if (isdir || (errno == ENOTDIR))
+ showfull(path,isdir);
+ else {
+ printf("** Could not access %s\n",path);
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (err);
+}
+
+#ifdef HAVE_SETXATTR
+
+static ssize_t ntfs_getxattr(const char *path, const char *name, void *value, size_t size)
+{
+#if defined(__APPLE__) || defined(__DARWIN__)
+ return getxattr(path, name, value, size, 0, 0);
+#else
+ return getxattr(path, name, value, size);
+#endif
+}
+
+/*
+ * Display all the parameters associated to a mounted file
+ */
+
+void showmounted(const char *fullname)
+{
+
+ static char attr[MAXATTRSZ];
+ struct stat st;
+#if POSIXACLS
+ struct POSIX_SECURITY *pxdesc;
+#endif
+ BOOL mapped;
+ int attrsz;
+ int mode;
+ uid_t uid;
+ gid_t gid;
+ u32 attrib;
+ int level;
+ BOOL isdir;
+
+ if (!stat(fullname,&st)) {
+ isdir = S_ISDIR(st.st_mode);
+ printf("%s ",(isdir ? "Directory" : "File"));
+ printname(stdout,fullname);
+ printf("\n");
+
+ attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ);
+ if (attrsz > 0) {
+ if (opt_v) {
+ hexdump(attr,attrsz,8);
+ printf("Computed hash : 0x%08lx\n",
+ (unsigned long)hash((le32*)attr,attrsz));
+ }
+ if (ntfs_getxattr(fullname,"system.ntfs_attrib",&attrib,4) != 4) {
+ printf("** Could not get file attrib\n");
+ errors++;
+ } else
+ printf("Windows attrib : 0x%x\n",(int)attrib);
+ if (ntfs_valid_descr(attr,attrsz)) {
+ mapped = !local_build_mapping(context.mapping,fullname);
+#if POSIXACLS
+ if (mapped) {
+ pxdesc = linux_permissions_posix(attr,isdir);
+ if (pxdesc)
+ mode = pxdesc->mode;
+ else
+ mode = 0;
+ } else {
+ pxdesc = (struct POSIX_SECURITY*)NULL;
+ mode = linux_permissions(attr,isdir);
+ printf("No user mapping : "
+ "cannot display the Posix ACL\n");
+ }
+#else
+ mode = linux_permissions(attr,isdir);
+#endif
+ if (opt_v >= 2) {
+ level = (opt_b ? 4 : 0);
+ showheader(attr,level);
+ showusid(attr,level);
+ showgsid(attr,level);
+ showdacl(attr,isdir,level);
+ showsacl(attr,isdir,level);
+ }
+ if (mapped) {
+ uid = linux_owner(attr);
+ gid = linux_group(attr);
+ printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
+ (int)uid,(int)gid,mode);
+ } else {
+ printf("Interpreted Unix mode 0%03o (owner and group are unmapped)\n",
+ mode);
+ }
+#if POSIXACLS
+ if (pxdesc) {
+ if ((pxdesc->defcnt
+ || (pxdesc->tagsset
+ & (POSIX_ACL_USER
+ | POSIX_ACL_GROUP
+ | POSIX_ACL_MASK))))
+ showposix(pxdesc);
+#if USESTUBS
+ stdfree(pxdesc); /* allocated within library */
+#else
+ free(pxdesc);
+#endif
+ }
+ if (mapped)
+ ntfs_free_mapping(context.mapping);
+#endif
+ } else
+ printf("Descriptor fails sanity check\n");
+ } else {
+ printf("** Could not get the NTFS ACL, check whether file is on NTFS\n");
+ errors++;
+ }
+ } else
+ printf("%s not found\n",fullname);
+}
+
+#else /* HAVE_SETXATTR */
+
+void showmounted(const char *fullname __attribute__((unused)))
+{
+ fprintf(stderr,"Not possible on this configuration\n");
+}
+
+#endif /* HAVE_SETXATTR */
+
+#if POSIXACLS
+
+BOOL recurseset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
+{
+ struct CALLBACK dircontext;
+ struct LINK *current;
+ BOOL isdir;
+ BOOL err;
+
+ err = FALSE;
+ dircontext.head = (struct LINK*)NULL;
+ dircontext.dir = path;
+ isdir = ntfs_read_directory(ntfs_context, path,
+ callback, &dircontext);
+ if (isdir) {
+ err = !setfull_posix(path,pxdesc,TRUE);
+ if (err) {
+ printf("** Failed to update %s\n",path);
+ printerror(stdout);
+ errors++;
+ } else {
+ if (opt_b)
+ printf("#\n#\n");
+ else
+ printf("\n\n");
+ while (dircontext.head) {
+ current = dircontext.head;
+ recurseset_posix(current->name,pxdesc);
+ dircontext.head = dircontext.head->next;
+ free(current);
+ }
+ }
+ } else
+ if (errno == ENOTDIR) {
+ err = !setfull_posix(path,pxdesc,FALSE);
+ if (err) {
+ printf("** Failed to update %s\n",path);
+ printerror(stdout);
+ errors++;
+ }
+ } else {
+ printf("** Could not access %s\n",path);
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (!err);
+}
+
+#else
+
+BOOL recurseset(const char *path, int mode)
+{
+ struct CALLBACK dircontext;
+ struct LINK *current;
+ BOOL isdir;
+ BOOL err;
+
+ err = FALSE;
+ dircontext.head = (struct LINK*)NULL;
+ dircontext.dir = path;
+ isdir = ntfs_read_directory(ntfs_context, path,
+ callback, &dircontext);
+ if (isdir) {
+ setfull(path,mode,TRUE);
+ if (opt_b)
+ printf("#\n#\n");
+ else
+ printf("\n\n");
+ while (dircontext.head) {
+ current = dircontext.head;
+ recurseset(current->name,mode);
+ dircontext.head = dircontext.head->next;
+ free(current);
+ }
+ } else
+ if (errno == ENOTDIR)
+ setfull(path,mode,FALSE);
+ else {
+ printf("** Could not access %s\n",path);
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (!err);
+}
+
+#endif
+
+#if POSIXACLS
+
+BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
+{
+ BOOL isdir;
+ BOOL err;
+
+ err = FALSE;
+ isdir = ntfs_read_directory(ntfs_context, path,
+ callback, (struct CALLBACK*)NULL);
+ if (isdir || (errno == ENOTDIR)) {
+ err = !setfull_posix(path,pxdesc,isdir);
+ if (err) {
+ printf("** Failed to update %s\n",path);
+ printerror(stdout);
+ errors++;
+ }
+ } else {
+ printf("** Could not access %s\n",path);
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (!err);
+}
+
+#endif
+
+BOOL singleset(const char *path, int mode)
+{
+ BOOL isdir;
+ BOOL err;
+
+ err = FALSE;
+ isdir = ntfs_read_directory(ntfs_context, path,
+ callback, (struct CALLBACK*)NULL);
+ if (isdir || (errno == ENOTDIR))
+ setfull(path,mode,isdir);
+ else {
+ printf("** Could not access %s\n",path);
+ printerror(stdout);
+ errors++;
+ err = TRUE;
+ }
+ return (!err);
+}
+
+int callback(struct CALLBACK *dircontext, char *ntfsname,
+ int length, int type,
+ long long pos __attribute__((unused)), u64 mft_ref __attribute__((unused)),
+ unsigned int dt_type __attribute__((unused)))
+{
+ struct LINK *linkage;
+ char *name;
+ int newlth;
+ int size;
+
+ size = utf8size(ntfsname,length);
+ if (dircontext
+ && (type != 2) /* 2 : dos name (8+3) */
+ && (size > 0) /* chars convertible to utf8 */
+ && ((length > 2)
+ || (ntfsname[0] != '.')
+ || (ntfsname[1] != '\0')
+ || ((ntfsname[2] || ntfsname[3])
+ && ((ntfsname[2] != '.') || (ntfsname[3] != '\0'))))) {
+ linkage = (struct LINK*)malloc(sizeof(struct LINK)
+ + strlen(dircontext->dir)
+ + size + 2);
+ if (linkage) {
+ /* may find ".fuse_hidden*" files */
+ /* recommendation is not to hide them, so that */
+ /* the user has a clue to delete them */
+ strcpy(linkage->name,dircontext->dir);
+ if (linkage->name[strlen(linkage->name) - 1] != '/')
+ strcat(linkage->name,"/");
+ name = &linkage->name[strlen(linkage->name)];
+ newlth = makeutf8(name,ntfsname,length);
+ name[newlth] = 0;
+ linkage->next = dircontext->head;
+ dircontext->head = linkage;
+ }
+ }
+ return (0);
+}
+
+#endif
+
+#ifdef WIN32
+
+/*
+ * Backup security descriptors in a directory tree (Windows mode)
+ */
+
+BOOL backup(const char *root)
+{
+ BOOL err;
+ time_t now;
+ const char *txtime;
+
+ now = time((time_t*)NULL);
+ txtime = ctime(&now);
+ printf("#\n# Recursive ACL collection on %s#\n",txtime);
+ err = recurseshow(root);
+ return (err);
+}
+
+#else
+
+/*
+ * Backup security descriptors in a directory tree (Linux mode)
+ */
+
+BOOL backup(const char *volume, const char *root)
+{
+ BOOL err;
+ int count;
+ int i,j;
+ time_t now;
+ const char *txtime;
+
+ now = time((time_t*)NULL);
+ txtime = ctime(&now);
+ if (!getuid() && open_security_api()) {
+ if (open_volume(volume,MS_RDONLY)) {
+ printf("#\n# Recursive ACL collection on %s#\n",txtime);
+ err = recurseshow(root);
+ count = 0;
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ if (securdata[i])
+ for (j=0; j<(1 << SECBLKSZ); j++)
+ if (securdata[i][j].filecount) {
+ count++;
+ }
+ printf("# %d security keys\n",count);
+ close_volume(volume);
+ } else {
+ fprintf(stderr,"Could not open volume %s\n",volume);
+ printerror(stdout);
+ err = TRUE;
+ }
+ close_security_api();
+ } else {
+ if (getuid())
+ fprintf(stderr,"This is only possible as root\n");
+ else
+ fprintf(stderr,"Could not open security API\n");
+ err = TRUE;
+ }
+ return (err);
+}
+
+#endif
+
+#ifdef WIN32
+
+/*
+ * List security descriptors in a directory tree (Windows mode)
+ */
+
+BOOL listfiles(const char *root)
+{
+ BOOL err;
+
+ if (opt_r) {
+ printf("\nRecursive file check\n");
+ err = recurseshow(root);
+ } else
+ err = singleshow(root);
+ return (err);
+}
+
+#else
+
+/*
+ * List security descriptors in a directory tree (Linux mode)
+ */
+
+BOOL listfiles(const char *volume, const char *root)
+{
+ BOOL err;
+ int i,j;
+ int count;
+
+ if (!getuid() && open_security_api()) {
+ if (open_volume(volume,MS_RDONLY)) {
+ if (opt_r) {
+ printf("\nRecursive file check\n");
+ err = recurseshow(root);
+ printf("Summary\n");
+ count = 0;
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ if (securdata[i])
+ for (j=0; j<(1 << SECBLKSZ); j++)
+ if (securdata[i][j].filecount) {
+ printf("Key 0x%x : %d files, mode 0%03o\n",
+ i*(1 << SECBLKSZ)+j,securdata[i][j].filecount,
+ securdata[i][j].mode);
+ count++;
+ }
+ printf("%d security keys\n",count);
+ } else
+ err = singleshow(root);
+ close_volume(volume);
+ } else {
+ fprintf(stderr,"Could not open volume %s\n",volume);
+ printerror(stdout);
+ err = TRUE;
+ }
+ close_security_api();
+ } else {
+ if (getuid())
+ fprintf(stderr,"This is only possible as root\n");
+ else
+ fprintf(stderr,"Could not open security API\n");
+ err = TRUE;
+ }
+ return (err);
+}
+
+#endif
+
+#ifndef WIN32
+
+/*
+ * Check whether a SDS entry is valid
+ */
+
+BOOL valid_sds(const char *attr, unsigned int offset,
+ unsigned int entrysz, unsigned int size, u32 prevkey,
+ BOOL second)
+{
+ BOOL unsane;
+ u32 comphash;
+ u32 key;
+
+ unsane = FALSE;
+ if (!get4l(attr,0) && !get4l(attr,4)) {
+ printf("Entry at 0x%lx was deleted\n",(long)offset);
+ } else {
+ if ((ntfs_attr_size(&attr[20]) + 20) > entrysz) {
+ printf("** Entry is truncated (expected size %ld)\n",
+ (long)ntfs_attr_size(&attr[20] + 20));
+ unsane = TRUE;
+ errors++;
+ }
+ if ((ntfs_attr_size(&attr[20]) + 20) < entrysz) {
+ printf("** Extra data appended to entry (expected size %ld)\n",
+ (long)ntfs_attr_size(&attr[20]) + 20);
+ warnings++;
+ }
+ if (!unsane && !ntfs_valid_descr((const char*)&attr[20],size)) {
+ printf("** General sanity check has failed\n");
+ unsane = TRUE;
+ errors++;
+ }
+ if (!unsane) {
+ comphash = hash((const le32*)&attr[20],entrysz-20);
+ if ((u32)get4l(attr,0) == comphash) {
+ if (opt_v >= 2)
+ printf("Hash 0x%08lx (correct)\n",
+ (unsigned long)comphash);
+ } else {
+ printf("** hash 0x%08lx (computed : 0x%08lx)\n",
+ (unsigned long)get4l(attr,0),
+ (unsigned long)comphash);
+ unsane = TRUE;
+ errors++;
+ }
+ }
+ if (!unsane) {
+ if ((second ? get8l(attr,8) + 0x40000 : get8l(attr,8)) == offset) {
+ if (opt_v >= 2)
+ printf("Offset 0x%lx (correct)\n",(long)offset);
+ } else {
+ printf("** offset 0x%llx (expected : 0x%llx)\n",
+ (long long)get8l(attr,8),
+ (long long)(second ? get8l(attr,8) - 0x40000 : get8l(attr,8)));
+// unsane = TRUE;
+ errors++;
+ }
+ }
+ if (!unsane) {
+ key = get4l(attr,4);
+ if (opt_v >= 2)
+ printf("Key 0x%x\n",(int)key);
+ if (key) {
+ if (key <= prevkey) {
+ printf("** Unordered key 0x%lx after 0x%lx\n",
+ (long)key,(long)prevkey);
+ unsane = TRUE;
+ errors++;
+ }
+ }
+ }
+ }
+ return (!unsane);
+}
+
+/*
+ * Check whether a SDS entry is consistent with other known data
+ * and store current data for subsequent checks
+ */
+
+int consist_sds(const char *attr, unsigned int offset,
+ unsigned int entrysz, BOOL second)
+{
+ int errcnt;
+ u32 key;
+ u32 comphash;
+ struct SECURITY_DATA *psecurdata;
+
+ errcnt = 0;
+ key = get4l(attr,4);
+ if ((key > 0) && (key < MAXSECURID)) {
+ printf("Valid entry at 0x%lx for key 0x%lx\n",
+ (long)offset,(long)key);
+ if (!securdata[key >> SECBLKSZ])
+ newblock(key);
+ if (securdata[key >> SECBLKSZ]) {
+ psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
+ comphash = hash((const le32*)&attr[20],entrysz-20);
+ if (psecurdata->flags & INSDS1) {
+ if (psecurdata->hash != comphash) {
+ printf("** Different hash values : $SDS-1 0x%08lx $SDS-2 0x%08lx\n",
+ (unsigned long)psecurdata->hash,
+ (unsigned long)comphash);
+ errcnt++;
+ errors++;
+ }
+ if (psecurdata->offset != get8l(attr,8)) {
+ printf("** Different offsets : $SDS-1 0x%llx $SDS-2 0x%llx\n",
+ (long long)psecurdata->offset,(long long)get8l(attr,8));
+ errcnt++;
+ errors++;
+ }
+ if (psecurdata->length != get4l(attr,16)) {
+ printf("** Different lengths : $SDS-1 0x%lx $SDS-2 0x%lx\n",
+ (long)psecurdata->length,(long)get4l(attr,16));
+ errcnt++;
+ errors++;
+ }
+ } else {
+ if (second) {
+ printf("** Entry was not present in $SDS-1\n");
+ errcnt++;
+ errors++;
+ }
+ psecurdata->hash = comphash;
+ psecurdata->offset = get8l(attr,8);
+ psecurdata->length = get4l(attr,16);
+ }
+ psecurdata->flags |= (second ? INSDS2 : INSDS1);
+ }
+ } else
+ if (key || get4l(attr,0)) {
+ printf("** Security_id 0x%x out of bounds\n",key);
+ warnings++;
+ }
+ return (errcnt);
+}
+
+
+/*
+ * Auditing of $SDS (Linux only)
+ */
+
+int audit_sds(BOOL second)
+{
+ static char attr[MAXATTRSZ + 20];
+ BOOL isdir;
+ BOOL done;
+ BOOL unsane;
+ u32 prevkey;
+ int errcnt;
+ int size;
+ unsigned int entrysz;
+ unsigned int entryalsz;
+ unsigned int offset;
+ int count;
+ int deleted;
+ int mode;
+
+ if (second)
+ printf("\nAuditing $SDS-2\n");
+ else
+ printf("\nAuditing $SDS-1\n");
+ errcnt = 0;
+ offset = (second ? 0x40000 : 0);
+ count = 0;
+ deleted = 0;
+ done = FALSE;
+ prevkey = 0;
+
+ /* get size of first record */
+
+ size = ntfs_read_sds(ntfs_context,(char*)attr,20,offset);
+ if (size != 20) {
+ if ((size < 0) && (errno == ENOTSUP))
+ printf("** There is no $SDS-%d in this volume\n",
+ (second ? 2 : 1));
+ else {
+ printf("** Could not open $SDS-%d, size %d\n",
+ (second ? 2 : 1),size);
+ errors++;
+ errcnt++;
+ }
+ } else
+ do {
+ entrysz = get4l(attr,16);
+ entryalsz = ((entrysz - 1) | 15) + 1;
+ if (entryalsz <= (MAXATTRSZ + 20)) {
+ /* read next header in anticipation, to get its size */
+ size = ntfs_read_sds(ntfs_context,
+ (char*)&attr[20],entryalsz,offset + 20);
+ if (opt_v)
+ printf("\nAt offset 0x%lx got %lu bytes\n",(long)offset,(long)size);
+ } else {
+ printf("** Security attribute is too long (%ld bytes) - stopping\n",
+ (long)entryalsz);
+ errcnt++;
+ }
+ if ((entryalsz > (MAXATTRSZ + 20)) || (size < (int)(entrysz - 20)))
+ done = TRUE;
+ else {
+ if (opt_v) {
+ printf("Entry size %d bytes\n",entrysz);
+ hexdump(&attr[20],size,8);
+ }
+
+ unsane = !valid_sds(attr,offset,entrysz,
+ size,prevkey,second);
+ if (!unsane) {
+ if (!get4l(attr,0) && !get4l(attr,4))
+ deleted++;
+ else
+ count++;
+ errcnt += consist_sds(attr,offset,
+ entrysz, second);
+ if (opt_v >= 2) {
+ isdir = guess_dir(&attr[20]);
+ printf("Assuming %s descriptor\n",(isdir ? "directory" : "file"));
+ showheader(&attr[20],0);
+ showusid(&attr[20],0);
+ showgsid(&attr[20],0);
+ showdacl(&attr[20],isdir,0);
+ showsacl(&attr[20],isdir,0);
+ mode = linux_permissions(
+ &attr[20],isdir);
+ printf("Interpreted Unix mode 0%03o\n",mode);
+ }
+ prevkey = get4l(attr,4);
+ }
+ if (!unsane) {
+ memcpy(attr,&attr[entryalsz],20);
+ offset += entryalsz;
+ if (!get4l(attr,16)
+ || ((((offset - 1) | 0x3ffff) - offset + 1) < 20)) {
+ if (second)
+ offset = ((offset - 1) | 0x7ffff) + 0x40001;
+ else
+ offset = ((offset - 1) | 0x7ffff) + 1;
+ if (opt_v)
+ printf("Trying next SDS-%d block at offset 0x%lx\n",
+ (second ? 2 : 1), (long)offset);
+ size = ntfs_read_sds(ntfs_context,
+ (char*)attr,20,offset);
+ if (size != 20) {
+ if (opt_v)
+ printf("Assuming end of $SDS, got %d bytes\n",size);
+ done = TRUE;
+ }
+ }
+ } else {
+ printf("** Sanity check failed - stopping there\n");
+ errcnt++;
+ errors++;
+ done = TRUE;
+ }
+ }
+ } while (!done);
+ if (count || deleted || errcnt) {
+ printf("%d valid and %d deleted entries in $SDS-%d\n",
+ count,deleted,(second ? 2 : 1));
+ printf("%d errors in $SDS-%c\n",errcnt,(second ? '2' : '1'));
+ }
+ return (errcnt);
+}
+
+/*
+ * Check whether a SII entry is sane
+ */
+
+BOOL valid_sii(const char *entry, u32 prevkey)
+{
+ BOOL valid;
+ u32 key;
+
+ valid = TRUE;
+ key = get4l(entry,16);
+ if (key <= prevkey) {
+ printf("** Unordered key 0x%lx after 0x%lx\n",
+ (long)key,(long)prevkey);
+ valid = FALSE;
+ errors++;
+ }
+ prevkey = key;
+ if (get2l(entry,0) != 20) {
+ printf("** offset %d (instead of 20)\n",(int)get2l(entry,0));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,2) != 20) {
+ printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
+ valid = FALSE;
+ errors++;
+ }
+ if (get4l(entry,4) != 0) {
+ printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,12) & 1) {
+ if (get2l(entry,8) != 48) {
+ printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
+ valid = FALSE;
+ errors++;
+ }
+ } else
+ if (get2l(entry,8) != 40) {
+ printf("** index size %d (instead of 40)\n",(int)get2l(entry,8));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,10) != 4) {
+ printf("** index key size %d (instead of 4)\n",(int)get2l(entry,10));
+ valid = FALSE;
+ errors++;
+ }
+ if ((get2l(entry,12) & ~3) != 0) {
+ printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,14) != 0) {
+ printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
+ valid = FALSE;
+ errors++;
+ }
+ if (get4l(entry,24) != key) {
+ printf("** key 0x%x (instead of 0x%x)\n",
+ (int)get4l(entry,24),(int)key);
+ valid = FALSE;
+ errors++;
+ }
+ return (valid);
+}
+
+/*
+ * Check whether a SII entry is consistent with other known data
+ */
+
+int consist_sii(const char *entry)
+{
+ int errcnt;
+ u32 key;
+ struct SECURITY_DATA *psecurdata;
+
+ errcnt = 0;
+ key = get4l(entry,16);
+ if ((key > 0) && (key < MAXSECURID)) {
+ printf("Valid entry for key 0x%lx\n",(long)key);
+ if (!securdata[key >> SECBLKSZ])
+ newblock(key);
+ if (securdata[key >> SECBLKSZ]) {
+ psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
+ psecurdata->flags |= INSII;
+ if (psecurdata->flags & (INSDS1 | INSDS2)) {
+ if ((u32)get4l(entry,20) != psecurdata->hash) {
+ printf("** hash 0x%x (instead of 0x%x)\n",
+ (unsigned int)get4l(entry,20),
+ (unsigned int)psecurdata->hash);
+ errors++;
+ }
+ if (get8l(entry,28) != psecurdata->offset) {
+ printf("** offset 0x%llx (instead of 0x%llx)\n",
+ (long long)get8l(entry,28),
+ (long long)psecurdata->offset);
+ errors++;
+ }
+ if (get4l(entry,36) != psecurdata->length) {
+ printf("** length 0x%lx (instead of %ld)\n",
+ (long)get4l(entry,36),
+ (long)psecurdata->length);
+ errors++;
+ }
+ } else {
+ printf("** Entry was not present in $SDS\n");
+ errors++;
+ psecurdata->hash = get4l(entry,20);
+ psecurdata->offset = get8l(entry,28);
+ psecurdata->length = get4l(entry,36);
+ if (opt_v) {
+ printf(" hash 0x%x\n",(unsigned int)psecurdata->hash);
+ printf(" offset 0x%llx\n",(long long)psecurdata->offset);
+ printf(" length %ld\n",(long)psecurdata->length);
+ }
+ errcnt++;
+ }
+ }
+ } else {
+ printf("** Security_id 0x%x out of bounds\n",key);
+ warnings++;
+ }
+ return (errcnt);
+}
+
+
+/*
+ * Auditing of $SII (Linux only)
+ */
+
+int audit_sii()
+{
+ char *entry;
+ int errcnt;
+ u32 prevkey;
+ BOOL valid;
+ BOOL done;
+ int count;
+
+ printf("\nAuditing $SII\n");
+ errcnt = 0;
+ count = 0;
+ entry = (char*)NULL;
+ prevkey = 0;
+ done = FALSE;
+ do {
+ entry = (char*)ntfs_read_sii(ntfs_context,(void*)entry);
+ if (entry) {
+ valid = valid_sii(entry,prevkey);
+ if (valid) {
+ count++;
+ errcnt += consist_sii(entry);
+ prevkey = get4l(entry,16);
+ } else
+ errcnt++;
+ } else
+ if ((errno == ENOTSUP) && !prevkey)
+ printf("** There is no $SII in this volume\n");
+ } while (entry && !done);
+ if (count || errcnt) {
+ printf("%d valid entries in $SII\n",count);
+ printf("%d errors in $SII\n",errcnt);
+ }
+ return (errcnt);
+}
+
+/*
+ * Check whether a SII entry is sane
+ */
+
+BOOL valid_sdh(const char *entry, u32 prevkey, u32 prevhash)
+{
+ BOOL valid;
+ u32 key;
+ u32 currhash;
+
+ valid = TRUE;
+ currhash = get4l(entry,16);
+ key = get4l(entry,20);
+ if ((currhash < prevhash)
+ || ((currhash == prevhash) && (key <= prevkey))) {
+ printf("** Unordered hash and key 0x%x 0x%x after 0x%x 0x%x\n",
+ (unsigned int)currhash,(unsigned int)key,
+ (unsigned int)prevhash,(unsigned int)prevkey);
+ valid = FALSE;
+ errors++;
+ }
+ if ((opt_v >= 2) && (currhash == prevhash))
+ printf("Hash collision (not an error)\n");
+
+ if (get2l(entry,0) != 24) {
+ printf("** offset %d (instead of 24)\n",(int)get2l(entry,0));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,2) != 20) {
+ printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
+ valid = FALSE;
+ errors++;
+ }
+ if (get4l(entry,4) != 0) {
+ printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,12) & 1) {
+ if (get2l(entry,8) != 56) {
+ printf("** index size %d (instead of 56)\n",(int)get2l(entry,8));
+ valid = FALSE;
+ errors++;
+ }
+ } else
+ if (get2l(entry,8) != 48) {
+ printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,10) != 8) {
+ printf("** index key size %d (instead of 8)\n",(int)get2l(entry,10));
+ valid = FALSE;
+ errors++;
+ }
+ if ((get2l(entry,12) & ~3) != 0) {
+ printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
+ valid = FALSE;
+ errors++;
+ }
+ if (get2l(entry,14) != 0) {
+ printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
+ valid = FALSE;
+ errors++;
+ }
+ if ((u32)get4l(entry,24) != currhash) {
+ printf("** hash 0x%x (instead of 0x%x)\n",
+ (unsigned int)get4l(entry,24),(unsigned int)currhash);
+ valid = FALSE;
+ errors++;
+ }
+ if (get4l(entry,28) != key) {
+ printf("** key 0x%x (instead of 0x%x)\n",
+ (int)get4l(entry,28),(int)key);
+ valid = FALSE;
+ errors++;
+ }
+ if (get4l(entry,44)
+ && (get4l(entry,44) != 0x490049)) {
+ printf("** fill3 0x%lx (instead of 0 or 0x490049)\n",
+ (long)get4l(entry,44));
+ valid = FALSE;
+ errors++;
+ }
+ return (valid);
+}
+
+/*
+ * Check whether a SDH entry is consistent with other known data
+ */
+
+int consist_sdh(const char *entry)
+{
+ int errcnt;
+ u32 key;
+ struct SECURITY_DATA *psecurdata;
+
+ errcnt = 0;
+ key = get4l(entry,20);
+ if ((key > 0) && (key < MAXSECURID)) {
+ printf("Valid entry for key 0x%lx\n",(long)key);
+ if (!securdata[key >> SECBLKSZ])
+ newblock(key);
+ if (securdata[key >> SECBLKSZ]) {
+ psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
+ psecurdata->flags |= INSDH;
+ if (psecurdata->flags & (INSDS1 | INSDS2 | INSII)) {
+ if ((u32)get4l(entry,24) != psecurdata->hash) {
+ printf("** hash 0x%x (instead of 0x%x)\n",
+ (unsigned int)get4l(entry,24),
+ (unsigned int)psecurdata->hash);
+ errors++;
+ }
+ if (get8l(entry,32) != psecurdata->offset) {
+ printf("** offset 0x%llx (instead of 0x%llx)\n",
+ (long long)get8l(entry,32),
+ (long long)psecurdata->offset);
+ errors++;
+ }
+ if (get4l(entry,40) != psecurdata->length) {
+ printf("** length %ld (instead of %ld)\n",
+ (long)get4l(entry,40),
+ (long)psecurdata->length);
+ errors++;
+ }
+ } else {
+ printf("** Entry was not present in $SDS nor in $SII\n");
+ errors++;
+ psecurdata->hash = get4l(entry,24);
+ psecurdata->offset = get8l(entry,32);
+ psecurdata->length = get4l(entry,40);
+ if (opt_v) {
+ printf(" offset 0x%llx\n",(long long)psecurdata->offset);
+ printf(" length %ld\n",(long)psecurdata->length);
+ }
+ errcnt++;
+ }
+ }
+ } else {
+ printf("** Security_id 0x%x out of bounds\n",key);
+ warnings++;
+ }
+ return (errcnt);
+}
+
+/*
+ * Auditing of $SDH (Linux only)
+ */
+
+int audit_sdh()
+{
+ char *entry;
+ int errcnt;
+ int count;
+ u32 prevkey;
+ u32 prevhash;
+ BOOL valid;
+ BOOL done;
+
+ printf("\nAuditing $SDH\n");
+ count = 0;
+ errcnt = 0;
+ prevkey = 0;
+ prevhash = 0;
+ entry = (char*)NULL;
+ done = FALSE;
+ do {
+ entry = (char*)ntfs_read_sdh(ntfs_context,(void*)entry);
+ if (entry) {
+ valid = valid_sdh(entry,prevkey,prevhash);
+ if (valid) {
+ count++;
+ errcnt += consist_sdh(entry);
+ prevhash = get4l(entry,16);
+ prevkey = get4l(entry,20);
+ } else
+ errcnt++;
+ } else
+ if ((errno == ENOTSUP) && !prevkey)
+ printf("** There is no $SDH in this volume\n");
+ } while (entry && !done);
+ if (count || errcnt) {
+ printf("%d valid entries in $SDH\n",count);
+ printf("%d errors in $SDH\n",errcnt);
+ }
+ return (errcnt);
+}
+
+/*
+ * Audit summary
+ */
+
+void audit_summary()
+{
+ int count;
+ int flags;
+ int cnt;
+ int found;
+ int i,j;
+
+ count = 0;
+ found = 0;
+ if (opt_r) printf("Summary of security key use :\n");
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ if (securdata[i])
+ for (j=0; j<(1 << SECBLKSZ); j++) {
+ flags = securdata[i][j].flags & (INSDS1 + INSDS2 + INSII + INSDH);
+ if (flags) found++;
+ if (flags
+ && (flags != (INSDS1 + INSDS2 + INSII + INSDH)))
+ {
+ if (!count && !opt_r)
+ printf("\n** Keys not present in all files :\n");
+ cnt = securdata[i][j].filecount;
+ if (opt_r)
+ printf("Key 0x%x used by %d %s, not in",
+ i*(1 << SECBLKSZ)+j,cnt,
+ (cnt > 1 ? "files" : "file"));
+ else
+ printf("Key 0x%x not in", i*(1 << SECBLKSZ)+j);
+ if (!(flags & INSDS1))
+ printf(" SDS-1");
+ if (!(flags & INSDS2))
+ printf(" SDS-2");
+ if (!(flags & INSII))
+ printf(" SII");
+ if (!(flags & INSDH))
+ printf(" SDH");
+ printf("\n");
+ count++;
+ } else {
+ cnt = securdata[i][j].filecount;
+ if (opt_r && cnt)
+ printf("Key 0x%x used by %d %s\n",
+ i*(1 << SECBLKSZ)+j,cnt,
+ (cnt > 1 ? "files" : "file"));
+ }
+ }
+ if (found) {
+ if (count)
+ printf("%d keys not present in all lists\n",count);
+ else
+ printf("All keys are present in all lists\n");
+ }
+}
+
+/*
+ * Auditing (Linux only)
+ */
+
+BOOL audit(const char *volume)
+{
+ BOOL err;
+
+ err = FALSE;
+ if (!getuid() && open_security_api()) {
+ if (open_volume(volume,MS_RDONLY)) {
+ if (audit_sds(FALSE)) err = TRUE;
+ if (audit_sds(TRUE)) err = TRUE;
+ if (audit_sii()) err = TRUE;
+ if (audit_sdh()) err = TRUE;
+ if (opt_r) recurseshow("/");
+
+ audit_summary();
+ close_volume(volume);
+ }
+ else {
+ fprintf(stderr,"Could not open volume %s\n",volume);
+ printerror(stdout);
+ err = TRUE;
+ }
+ close_security_api();
+ }
+ else {
+ if (getuid())
+ fprintf(stderr,"This is only possible as root\n");
+ else fprintf(stderr,"Could not open security API\n");
+ err = TRUE;
+ }
+ return (err);
+}
+
+#endif
+
+#if POSIXACLS
+
+/*
+ * Encode a Posix ACL string
+ * [d:]{ugmo}:uid[:perms],...
+ */
+
+struct POSIX_SECURITY *encode_posix_acl(const char *str)
+{
+ int acccnt;
+ int defcnt;
+ int i,k,l;
+ int c;
+ s32 id;
+ u16 perms;
+ u16 apermsset;
+ u16 dpermsset;
+ u16 tag;
+ u16 tagsset;
+ mode_t mode;
+ BOOL defacl;
+ BOOL dmask;
+ BOOL amask;
+ const char *p;
+ struct POSIX_ACL *acl;
+ struct POSIX_SECURITY *pxdesc;
+ enum { PXBEGIN, PXTAG, PXTAG1, PXID, PXID1, PXID2,
+ PXPERM, PXPERM1, PXPERM2, PXOCT, PXNEXT, PXEND, PXERR
+ } state;
+
+ /* raw evaluation of ACE count */
+ p = str;
+ amask = FALSE;
+ dmask = FALSE;
+ if (*p == 'd') {
+ acccnt = 0;
+ defcnt = 1;
+ } else {
+ if ((*p >= '0') && (*p <= '7'))
+ acccnt = 0;
+ else
+ acccnt = 1;
+ defcnt = 0;
+ }
+ while (*p)
+ if (*p++ == ',') {
+ if (*p == 'd') {
+ defcnt++;
+ if (p[1] && (p[2] == 'm'))
+ dmask = TRUE;
+ } else {
+ acccnt++;
+ if (*p == 'm')
+ amask = TRUE;
+ }
+ }
+ /* account for an implicit mask if none defined */
+ if (acccnt && !amask)
+ acccnt++;
+ if (defcnt && !dmask)
+ defcnt++;
+ pxdesc = (struct POSIX_SECURITY*)malloc(sizeof(struct POSIX_SECURITY)
+ + (acccnt + defcnt)*sizeof(struct POSIX_ACE));
+ if (pxdesc) {
+ pxdesc->acccnt = acccnt;
+ pxdesc->firstdef = acccnt;
+ pxdesc->defcnt = defcnt;
+ acl = &pxdesc->acl;
+ p = str;
+ state = PXBEGIN;
+ id = 0;
+ defacl = FALSE;
+ mode = 0;
+ apermsset = 0;
+ dpermsset = 0;
+ tag = 0;
+ perms = 0;
+ k = l = 0;
+ c = *p++;
+ while ((state != PXEND) && (state != PXERR)) {
+ switch (state) {
+ case PXBEGIN :
+ if (c == 'd') {
+ defacl = TRUE;
+ state = PXTAG1;
+ break;
+ } else
+ if ((c >= '0') && (c <= '7')) {
+ mode = c - '0';
+ state = PXOCT;
+ break;
+ }
+ defacl = FALSE;
+ /* fall through */
+ case PXTAG :
+ switch (c) {
+ case 'u' :
+ tag = POSIX_ACL_USER;
+ state = PXID;
+ break;
+ case 'g' :
+ tag = POSIX_ACL_GROUP;
+ state = PXID;
+ break;
+ case 'o' :
+ tag = POSIX_ACL_OTHER;
+ state = PXID;
+ break;
+ case 'm' :
+ tag = POSIX_ACL_MASK;
+ state = PXID;
+ break;
+ default :
+ state = PXERR;
+ break;
+ }
+ break;
+ case PXTAG1 :
+ if (c == ':')
+ state = PXTAG;
+ else
+ state = PXERR;
+ break;
+ case PXID :
+ if (c == ':') {
+ if ((tag == POSIX_ACL_OTHER)
+ || (tag == POSIX_ACL_MASK))
+ state = PXPERM;
+ else
+ state = PXID1;
+ } else
+ state = PXERR;
+ break;
+ case PXID1 :
+ if ((c >= '0') && (c <= '9')) {
+ id = c - '0';
+ state = PXID2;
+ } else
+ if (c == ':') {
+ id = -1;
+ if (tag == POSIX_ACL_USER)
+ tag = POSIX_ACL_USER_OBJ;
+ if (tag == POSIX_ACL_GROUP)
+ tag = POSIX_ACL_GROUP_OBJ;
+ state = PXPERM1;
+ } else
+ state = PXERR;
+ break;
+ case PXID2 :
+ if ((c >= '0') && (c <= '9'))
+ id = 10*id + c - '0';
+ else
+ if (c == ':')
+ state = PXPERM1;
+ else
+ state = PXERR;
+ break;
+ case PXPERM :
+ if (c == ':') {
+ id = -1;
+ state = PXPERM1;
+ } else
+ state = PXERR;
+ break;
+ case PXPERM1 :
+ if ((c >= '0') && (c <= '7')) {
+ perms = c - '0';
+ state = PXNEXT;
+ break;
+ }
+ state = PXPERM2;
+ perms = 0;
+ /* fall through */
+ case PXPERM2 :
+ switch (c) {
+ case 'r' :
+ perms |= POSIX_PERM_R;
+ break;
+ case 'w' :
+ perms |= POSIX_PERM_W;
+ break;
+ case 'x' :
+ perms |= POSIX_PERM_X;
+ break;
+ case ',' :
+ case '\0' :
+ if (defacl) {
+ i = acccnt + l++;
+ dpermsset |= perms;
+ } else {
+ i = k++;
+ apermsset |= perms;
+ }
+ acl->ace[i].tag = tag;
+ acl->ace[i].perms = perms;
+ acl->ace[i].id = id;
+ if (c == '\0')
+ state = PXEND;
+ else
+ state = PXBEGIN;
+ break;
+ }
+ break;
+ case PXNEXT :
+ if (!c || (c == ',')) {
+ if (defacl) {
+ i = acccnt + l++;
+ dpermsset |= perms;
+ } else {
+ i = k++;
+ apermsset |= perms;
+ }
+ acl->ace[i].tag = tag;
+ acl->ace[i].perms = perms;
+ acl->ace[i].id = id;
+ if (c == '\0')
+ state = PXEND;
+ else
+ state = PXBEGIN;
+ } else
+ state = PXERR;
+ break;
+ case PXOCT :
+ if ((c >= '0') && (c <= '7'))
+ mode = (mode << 3) + c - '0';
+ else
+ if (c == '\0')
+ state = PXEND;
+ else
+ state = PXBEGIN;
+ break;
+ default :
+ break;
+ }
+ c = *p++;
+ }
+ /* insert default mask if none defined */
+ if (acccnt && !amask) {
+ i = k++;
+ acl->ace[i].tag = POSIX_ACL_MASK;
+ acl->ace[i].perms = apermsset;
+ acl->ace[i].id = -1;
+ }
+ if (defcnt && !dmask) {
+ i = acccnt + l++;
+ acl->ace[i].tag = POSIX_ACL_MASK;
+ acl->ace[i].perms = dpermsset;
+ acl->ace[i].id = -1;
+ }
+ /* compute the mode and tagsset */
+ tagsset = 0;
+ for (i=0; i<acccnt; i++)
+ tagsset |= acl->ace[i].tag;
+ switch (acl->ace[i].tag) {
+ case POSIX_ACL_USER_OBJ :
+ mode |= acl->ace[i].perms << 6;
+ break;
+ case POSIX_ACL_GROUP_OBJ :
+ mode |= acl->ace[i].perms << 3;
+ break;
+ case POSIX_ACL_OTHER :
+ mode |= acl->ace[i].perms;
+ break;
+ default :
+ break;
+ }
+ pxdesc->mode = mode;
+ pxdesc->tagsset = tagsset;
+ pxdesc->acl.version = POSIX_VERSION;
+ pxdesc->acl.flags = 0;
+ pxdesc->acl.filler = 0;
+ if (state != PXERR)
+ ntfs_sort_posix(pxdesc);
+showposix(pxdesc);
+ if ((state == PXERR)
+ || (k != acccnt)
+ || (l != defcnt)
+ || !ntfs_valid_posix(pxdesc)) {
+ if (~pxdesc->tagsset
+ & (POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER))
+ fprintf(stderr,"User, group or other permissions missing\n");
+ else
+ fprintf(stderr,"Bad ACL description\n");
+ free(pxdesc);
+ pxdesc = (struct POSIX_SECURITY*)NULL;
+ } else
+ if (opt_v >= 2) {
+ printf("Interpreted input description :\n");
+ showposix(pxdesc);
+ }
+ } else
+ errno = ENOMEM;
+ return (pxdesc);
+}
+
+#endif /* POSIXACLS */
+
+
+int getoptions(int argc, char *argv[])
+{
+ int xarg;
+ int narg;
+ const char *parg;
+ BOOL err;
+
+ opt_a = FALSE;
+ opt_b = FALSE;
+ opt_e = FALSE;
+ opt_h = FALSE;
+#if FORCEMASK
+ opt_m = FALSE;
+#endif
+ opt_r = FALSE;
+ opt_s = FALSE;
+#if SELFTESTS & !USESTUBS
+ opt_t = FALSE;
+#endif
+ opt_v = 0;
+ xarg = 1;
+ err = FALSE;
+ while ((xarg < argc) && (argv[xarg][0] == '-')) {
+ parg = argv[xarg++];
+ while (*++parg)
+ switch (*parg)
+ {
+#ifndef WIN32
+ case 'a' :
+ opt_a = TRUE;
+ break;
+#endif
+ case 'b' :
+ opt_b = TRUE;
+ break;
+ case 'e' :
+ opt_e = TRUE;
+ break;
+ case 'h' :
+ opt_h = TRUE;
+ break;
+#if FORCEMASK
+ case 'm' :
+ opt_m = TRUE;
+ break;
+#endif
+ case 'r' :
+ case 'R' :
+ opt_r = TRUE;
+ break;
+ case 's' :
+ opt_s = TRUE;
+ break;
+#if SELFTESTS & !USESTUBS
+ case 't' :
+ opt_t = TRUE;
+ break;
+#endif
+ case 'v' :
+ opt_v++;
+ break;
+ default :
+ fprintf(stderr,"Invalid option -%c\n",*parg);
+ err = TRUE;
+ }
+ }
+ narg = argc - xarg;
+#ifdef WIN32
+ if ( ((opt_h || opt_s) && (narg > 1))
+ || ((opt_r || opt_b) && ((narg < 1) || (narg > 2)))
+#if SELFTESTS & !USESTUBS
+ || (opt_t && (narg > 0))
+#endif
+ || (opt_e && !opt_s)
+ || (!opt_h && !opt_r && !opt_b && !opt_s
+#if SELFTESTS & !USESTUBS
+ && !opt_t
+#endif
+ && ((narg < 1) || (narg > 2))))
+
+ err = TRUE;
+ if (err) {
+ xarg = 0;
+ fprintf(stderr,"Usage:\n");
+#if SELFTESTS & !USESTUBS
+ fprintf(stderr," secaudit -t\n");
+ fprintf(stderr," run self-tests\n");
+#endif
+ fprintf(stderr," secaudit -h [file]\n");
+ fprintf(stderr," display security descriptors within file\n");
+ fprintf(stderr," secaudit [-v] file\n");
+ fprintf(stderr," display the security parameters of file\n");
+ fprintf(stderr," secaudit -r[v] directory\n");
+ fprintf(stderr," display the security parameters of files in directory\n");
+ fprintf(stderr," secaudit -b[v] directory\n");
+ fprintf(stderr," backup the security parameters of files in directory\n");
+ fprintf(stderr," secaudit -s[ev] [backupfile]\n");
+ fprintf(stderr," set the security parameters as indicated in backup file\n");
+ fprintf(stderr," with -e also set extra parameters (Windows attrib)\n");
+ fprintf(stderr," secaudit perms file\n");
+ fprintf(stderr," set the security parameters of file to perms\n");
+ fprintf(stderr," secaudit -r[v] perms directory\n");
+ fprintf(stderr," set the security parameters of files in directory to perms\n");
+#if POSIXACLS
+ fprintf(stderr," Note: perms can be an octal mode or a Posix ACL description\n");
+#else
+ fprintf(stderr," Note: perms is an octal mode\n");
+#endif
+ fprintf(stderr," -v is for verbose, -vv for very verbose\n");
+ }
+#else
+ if ( (opt_h && (narg > 1))
+ || (opt_a && (narg != 1))
+ || ((opt_r || opt_b || opt_s) && ((narg < 1) || (narg > 3)))
+#if SELFTESTS & !USESTUBS
+ || (opt_t && (narg > 0))
+#endif
+ || (opt_e && !opt_s)
+ || (!opt_h && !opt_a && !opt_r && !opt_b && !opt_s
+#if SELFTESTS & !USESTUBS
+ && !opt_t
+#endif
+#ifdef HAVE_SETXATTR
+ && ((narg < 1) || (narg > 3))))
+#else
+ && ((narg < 2) || (narg > 3))))
+#endif
+ err = TRUE;
+ if (err) {
+ xarg = 0;
+ fprintf(stderr,"Usage:\n");
+#if SELFTESTS & !USESTUBS
+ fprintf(stderr," secaudit -t\n");
+ fprintf(stderr," run self-tests\n");
+#endif
+ fprintf(stderr," secaudit -h [file]\n");
+ fprintf(stderr," display security descriptors within file\n");
+ fprintf(stderr," secaudit -a[rv] volume\n");
+ fprintf(stderr," audit the volume\n");
+ fprintf(stderr," secaudit [-v] volume file\n");
+ fprintf(stderr," display the security parameters of file\n");
+ fprintf(stderr," secaudit -r[v] volume directory\n");
+ fprintf(stderr," display the security parameters of files in directory\n");
+ fprintf(stderr," secaudit -b[v] volume directory\n");
+ fprintf(stderr," backup the security parameters of files in directory\n");
+ fprintf(stderr," secaudit -s[ev] volume [backupfile]\n");
+ fprintf(stderr," set the security parameters as indicated in backup file\n");
+ fprintf(stderr," with -e also set extra parameters (Windows attrib)\n");
+ fprintf(stderr," secaudit volume perms file\n");
+ fprintf(stderr," set the security parameters of file to perms\n");
+ fprintf(stderr," secaudit -r[v] volume perms directory\n");
+ fprintf(stderr," set the security parameters of files in directory to perms\n");
+#ifdef HAVE_SETXATTR
+ fprintf(stderr," special case, does not require being root :\n");
+ fprintf(stderr," secaudit [-v] mounted-file\n");
+ fprintf(stderr," display the security parameters of a mounted file\n");
+#endif
+#if POSIXACLS
+ fprintf(stderr," Note: perms can be an octal mode or a Posix ACL description\n");
+#else
+ fprintf(stderr," Note: perms is an octal mode\n");
+#endif
+ fprintf(stderr," -v is for verbose, -vv for very verbose\n");
+ }
+#endif
+ if ((sizeof(SID) != 12) && !err) {
+ fprintf(stderr,"Possible alignment problem, check your compiler options\n");
+ err = TRUE;
+ xarg = 0;
+ }
+ return (xarg);
+}
+
+/*
+ * Memory allocation with checks
+ */
+
+#undef malloc
+#undef calloc
+#undef free
+#undef isalloc
+
+void dumpalloc(const char *txt)
+{
+ struct CHKALLOC *q;
+
+ if (firstalloc) {
+ printf("alloc table at %s\n",txt);
+ for (q=firstalloc; q; q=q->next)
+ printf("%08lx : %u bytes at %08lx allocated at %s line %d\n",
+ (long)q,(unsigned int)q->size,
+ (long)q->alloc,q->file,q->line);
+ }
+}
+
+void *chkmalloc(size_t size, const char *file, int line)
+{
+ void *p;
+ struct CHKALLOC *q;
+
+ p = (void*)malloc(size+1);
+ if (p) {
+ ((unsigned char*)p)[size] = 0xaa;
+ q = (struct CHKALLOC*)malloc(sizeof(struct CHKALLOC));
+ if (q) {
+ q->next = firstalloc;
+ q->alloc = p;
+ q->size = size;
+ q->file = file;
+ q->line = line;
+ firstalloc = q;
+ }
+ }
+ return (p);
+}
+
+void *chkcalloc(size_t cnt, size_t size, const char *file, int line)
+{
+ return (chkmalloc(cnt*size,file,line));
+}
+
+void chkfree(void *p, const char *file, int line)
+{
+ struct CHKALLOC *q;
+ struct CHKALLOC *r;
+
+ if (p) {
+ if (firstalloc && (firstalloc->alloc == p)) {
+ r = firstalloc;
+ firstalloc = firstalloc->next;
+ } else {
+ q = firstalloc;
+ if (q)
+ while (q->next && (q->next->alloc != p))
+ q = q->next;
+ if (q && q->next) {
+ r = q->next;
+ q->next = r->next;
+ } else {
+ r = (struct CHKALLOC*)NULL;
+ printf("** freeing unallocated memory in %s line %d\n",file,line);
+ if (!isatty(1))
+ fprintf(stderr,"** freeing unallocated memory in %s line %d\n",file,line);
+ }
+ }
+ if (r) {
+ if (((unsigned char*)p)[r->size] != 0xaa) {
+ printf("** memory corruption, alloc in %s line %d release in %s %d\n",
+ r->file,r->line,file,line);
+ if (!isatty(1))
+ fprintf(stderr,"** memory corruption, alloc in %s line %d release in %s %d\n",
+ r->file,r->line,file,line);
+ }
+ memset(p,0xaa,r->size);
+ free(r);
+ free(p);
+ }
+ }
+}
+
+void *stdmalloc(size_t size)
+{
+ return (malloc(size));
+}
+
+void stdfree(void *p)
+{
+ free(p);
+}
+
+BOOL chkisalloc(void *p, const char *file, int line)
+{
+ struct CHKALLOC *q;
+
+ if (p) {
+ q = firstalloc;
+ while (q && (q->alloc != p))
+ q = q->next;
+ } else
+ q = (struct CHKALLOC*)NULL;
+ if (!p || !q) {
+ printf("error in %s %d : 0x%lx not allocated\n",file,line,(long)p);
+ }
+ return (p && q);
+}
+
+
+
+
+#ifdef WIN32
+
+/*
+ * Windows version
+ */
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ FILE *fd;
+ int xarg;
+ int mode;
+ unsigned int size;
+ BOOL cmderr;
+ char *filename;
+ const char *p;
+ int i;
+#if POSIXACLS
+ struct POSIX_SECURITY *pxdesc;
+#endif
+
+ printf("%s\n",BANNER);
+ cmderr = FALSE;
+ errors = 0;
+ warnings = 0;
+ xarg = getoptions(argc,argv);
+ if (xarg) {
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ securdata[i] = (struct SECURITY_DATA*)NULL;
+#if POSIXACLS
+ context.mapping[MAPUSERS] = (struct MAPPING*)NULL;
+ context.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
+#endif
+ firstalloc = (struct CHKALLOC*)NULL;
+ mappingtype = MAPNONE;
+ switch (argc - xarg) {
+ case 0 :
+ if (opt_h)
+ showhex(stdin);
+ else
+ if (opt_s)
+ restore(stdin);
+#if SELFTESTS & !USESTUBS
+ if (opt_t)
+ selftests();
+#endif
+ break;
+ case 1 :
+ if (opt_h || opt_s) {
+ fd = fopen(argv[xarg],"r");
+ if (fd) {
+ if (opt_h)
+ showhex(fd);
+ else
+ restore(fd);
+ fclose(fd);
+ } else {
+ fprintf(stderr,"Could not open %s\n",argv[xarg]);
+ cmderr = TRUE;
+ }
+ } else {
+ size = utf16size(argv[xarg]);
+ if (size) {
+ filename = (char*)malloc(2*size + 2);
+ if (filename) {
+ makeutf16(filename,argv[xarg]);
+#if POSIXACLS
+ if (local_build_mapping(context.mapping,filename)) {
+ printf("*** Could not get user mapping data\n");
+ warnings++;
+ }
+#endif
+ if (opt_b)
+ cmderr = backup(filename);
+ else {
+ if (opt_r)
+ cmderr = listfiles(filename);
+ else
+ cmderr = singleshow(filename);
+ }
+#if POSIXACLS
+ ntfs_free_mapping(context.mapping);
+#endif
+ free(filename);
+ } else {
+ fprintf(stderr,"No more memory\n");
+ cmderr = TRUE;
+ }
+ } else {
+ fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg]);
+ cmderr = TRUE;
+ }
+ }
+ break;
+ case 2 :
+ mode = 0;
+ p = argv[xarg];
+#if POSIXACLS
+ pxdesc = encode_posix_acl(p);
+ if (pxdesc) {
+ size = utf16size(argv[xarg + 1]);
+ if (size) {
+ filename = (char*)malloc(2*size + 2);
+ if (filename) {
+ makeutf16(filename,argv[xarg + 1]);
+ if (local_build_mapping(context.mapping,filename)) {
+ printf("*** Could not get user mapping data\n");
+ warnings++;
+ }
+ if (opt_r) {
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ securdata[i] = (struct SECURITY_DATA*)NULL;
+ recurseset_posix(filename,pxdesc);
+ } else
+ singleset_posix(filename,pxdesc);
+ ntfs_free_mapping(context.mapping);
+ free(filename);
+ } else {
+ fprintf(stderr,"No more memory\n");
+ cmderr = TRUE;
+ }
+ chkfree(pxdesc,__FILE__,__LINE__);
+ } else {
+ fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]);
+ cmderr = TRUE;
+ }
+ }
+#else
+ while ((*p >= '0') && (*p <= '7'))
+ mode = (mode << 3) + (*p++) - '0';
+ if (*p) {
+ fprintf(stderr,"New mode should be given in octal\n");
+ cmderr = TRUE;
+ } else {
+ size = utf16size(argv[xarg + 1]);
+ if (size) {
+ filename = (char*)malloc(2*size + 2);
+ if (filename) {
+ makeutf16(filename,argv[xarg + 1]);
+#if POSIXACLS
+ if (local_build_mapping(&context,filename)) {
+ printf("*** Could not get user mapping data\n");
+ warnings++;
+ }
+#endif
+ if (opt_r) {
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ securdata[i] = (struct SECURITY_DATA*)NULL;
+ recurseset(filename,mode);
+ } else
+ singleset(filename,mode);
+ free(filename);
+ } else {
+ fprintf(stderr,"No more memory\n");
+ cmderr = TRUE;
+ }
+ } else {
+ fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]);
+ cmderr = TRUE;
+ }
+ }
+#endif
+ break;
+#if FORCEMASK
+ case 3 :
+ mode = 0;
+ forcemsk = 0;
+ p = argv[xarg];
+ while (*p) {
+ if ((*p >= '0') && (*p <= '9'))
+ forcemsk = (forcemsk << 4) + *p - '0';
+ else forcemsk = (forcemsk << 4) + (*p & 7) + 9;
+ p++;
+ }
+ p = argv[xarg + 1];
+ while ((*p >= '0') && (*p <= '7'))
+ mode = (mode << 3) + (*p++) - '0';
+ if (*p) {
+ fprintf(stderr,"New mode should be given in octal\n");
+ cmderr = TRUE;
+ } else {
+ if (opt_r) {
+ recurseset(argv[xarg + 2],mode);
+ }
+ else singleset(argv[xarg + 2],mode);
+ }
+ break;
+#endif
+ }
+ if (warnings)
+ printf("** %u %s signalled\n",warnings,
+ (warnings > 1 ? "warnings were" : "warning was"));
+ if (errors)
+ printf("** %u %s found\n",errors,
+ (errors > 1 ? "errors were" : "error was"));
+ else
+ printf("No errors were found\n");
+ if (!isatty(1)) {
+ fflush(stdout);
+ if (warnings)
+ fprintf(stderr,"** %u %s signalled\n",warnings,
+ (warnings > 1 ? "warnings were" : "warning was"));
+ if (errors)
+ fprintf(stderr,"** %u %s found\n",errors,
+ (errors > 1 ? "errors were" : "error was"));
+ else
+ fprintf(stderr,"No errors were found\n");
+ freeblocks();
+ }
+ }
+ dumpalloc("termination");
+ if (cmderr || errors)
+ exit(1);
+ return (0);
+}
+
+#else
+
+/*
+ * Linux version
+ */
+
+int main(int argc, char *argv[])
+{
+ FILE *fd;
+ const char *p;
+ int xarg;
+ BOOL cmderr;
+ int i;
+#if POSIXACLS
+ struct POSIX_SECURITY *pxdesc;
+#else
+ unsigned int mode;
+#endif
+
+ printf("%s\n",BANNER);
+ cmderr = FALSE;
+ errors = 0;
+ warnings = 0;
+ xarg = getoptions(argc,argv);
+ if (xarg) {
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ securdata[i] = (struct SECURITY_DATA*)NULL;
+#if POSIXACLS
+ context.mapping[MAPUSERS] = (struct MAPPING*)NULL;
+ context.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
+#endif
+ firstalloc = (struct CHKALLOC*)NULL;
+ mappingtype = MAPNONE;
+ switch (argc - xarg) {
+ case 0 :
+ if (opt_h)
+ showhex(stdin);
+#if SELFTESTS & !USESTUBS
+ if (opt_t)
+ selftests();
+#endif
+ break;
+ case 1 :
+ if (opt_a)
+ cmderr = audit(argv[xarg]);
+ else
+ if (opt_h) {
+ fd = fopen(argv[xarg],"rb");
+ if (fd) {
+ showhex(fd);
+ fclose(fd);
+ } else {
+ fprintf(stderr,"Could not open %s\n",argv[xarg]);
+ cmderr = TRUE;
+ }
+ } else
+ if (opt_b)
+ cmderr = backup(argv[xarg],"/");
+ else
+ if (opt_r)
+ cmderr = listfiles(argv[xarg],"/");
+ else
+ if (opt_s)
+ cmderr = dorestore(argv[xarg],stdin);
+ else
+ showmounted(argv[xarg]);
+ break;
+ case 2 :
+ if (opt_b)
+ cmderr = backup(argv[xarg],argv[xarg+1]);
+ else
+ if (opt_s) {
+ fd = fopen(argv[xarg+1],"rb");
+ if (fd) {
+ if (dorestore(argv[xarg],fd))
+ cmderr = TRUE;
+ fclose(fd);
+ } else {
+ fprintf(stderr,"Could not open %s\n",argv[xarg]);
+ cmderr = TRUE;
+ }
+ } else
+ cmderr = listfiles(argv[xarg],argv[xarg+1]);
+ break;
+ case 3 :
+ p = argv[xarg+1];
+#if POSIXACLS
+ pxdesc = encode_posix_acl(p);
+ if (pxdesc) {
+ if (!getuid() && open_security_api()) {
+ if (open_volume(argv[xarg],MS_NONE)) {
+ if (opt_r) {
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ securdata[i] = (struct SECURITY_DATA*)NULL;
+ recurseset_posix(argv[xarg + 2],pxdesc);
+ } else
+ singleset_posix(argv[xarg + 2],pxdesc);
+ close_volume(argv[xarg]);
+ } else {
+ fprintf(stderr,"Could not open volume %s\n",argv[xarg]);
+ printerror(stderr);
+ cmderr = TRUE;
+ }
+ close_security_api();
+ } else {
+ if (getuid())
+ fprintf(stderr,"This is only possible as root\n");
+ else
+ fprintf(stderr,"Could not open security API\n");
+ cmderr = TRUE;
+ }
+ chkfree(pxdesc,__FILE__,__LINE__);
+ } else
+ cmderr = TRUE;
+#else
+ mode = 0;
+ while ((*p >= '0') && (*p <= '7'))
+ mode = (mode << 3) + (*p++) - '0';
+ if (*p) {
+ fprintf(stderr,"New mode should be given in octal\n");
+ cmderr = TRUE;
+ } else
+ if (!getuid() && open_security_api()) {
+ if (open_volume(argv[xarg],MS_NONE)) {
+ if (opt_r) {
+ for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
+ securdata[i] = (struct SECURITY_DATA*)NULL;
+ recurseset(argv[xarg + 2],mode);
+ } else
+ singleset(argv[xarg + 2],mode);
+ close_volume(argv[xarg]);
+ } else {
+ fprintf(stderr,"Could not open volume %s\n",argv[xarg]);
+ printerror(stderr);
+ cmderr = TRUE;
+ }
+ close_security_api();
+ } else {
+ if (getuid())
+ fprintf(stderr,"This is only possible as root\n");
+ else
+ fprintf(stderr,"Could not open security API\n");
+ cmderr = TRUE;
+ }
+#endif
+ break;
+ }
+ if (warnings)
+ printf("** %u %s signalled\n",warnings,
+ (warnings > 1 ? "warnings were" : "warning was"));
+ if (errors)
+ printf("** %u %s found\n",errors,
+ (errors > 1 ? "errors were" : "error was"));
+ else
+ if (!cmderr)
+ printf("No errors were found\n");
+ if (!isatty(1)) {
+ fflush(stdout);
+ if (warnings)
+ fprintf(stderr,"** %u %s signalled\n",warnings,
+ (warnings > 1 ? "warnings were" : "warning was"));
+ if (errors)
+ fprintf(stderr,"** %u %s found\n",errors,
+ (errors > 1 ? "errors were" : "error was"));
+ else
+ if (!cmderr)
+ fprintf(stderr,"No errors were found\n");
+ }
+ freeblocks();
+ } else
+ cmderr = TRUE;
+ dumpalloc("termination");
+ if (cmderr || errors)
+ exit(1);
+ return (0);
+}
+
+#endif
diff --git a/src/secaudit.h b/src/secaudit.h
new file mode 100644
index 0000000..75c8ece
--- /dev/null
+++ b/src/secaudit.h
@@ -0,0 +1,736 @@
+/*
+ * General declarations for secaudit
+ *
+ * These declarations are organized to enable code sharing with ntfs-3g
+ * library, but should only be used to build tools runnable both
+ * on Linux (dynamic linking) and Windows (static linking)
+ *
+ * Copyright (c) 2007-2009 Jean-Pierre Andre
+ *
+ */
+
+/*
+ * 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * General parameters which may have to be adapted to needs
+ */
+
+#define SELFTESTS 1 /* include code for self-testing */
+#define POSIXACLS 0 /* include code for processing Posix ACLs */
+#define NOREVBOM 0 /* temporary */
+
+#define OWNERFROMACL 1 /* must match option in security.c */
+
+#define MAXATTRSZ 65536 /* Max sec attr size (16448 met for WinXP) */
+#define MAXSECURID 262144
+#define SECBLKSZ 8
+#define MAXFILENAME 4096
+#define FORCEMASK 0 /* Special (dangerous) option -m to force a mask */
+#define MAXLINE 80 /* maximum processed size of a line */
+#define BUFSZ 1024 /* buffer size to read mapping file */
+#define LINESZ 120 /* maximum useful size of a mapping line */
+
+/*
+ * Definitions for Linux
+ * Use explicit or implicit dynamic linking
+ */
+
+#ifdef HAVE_CONFIG_H
+#undef POSIXACLS /* override default by configure option */
+#define USESTUBS 1 /* API stubs generated at link time */
+#else
+#define USESTUBS 0 /* direct calls to API, based on following definitions */
+#define ENVNTFS3G "NTFS3G"
+#define LIBFILE64 "/lib64/libntfs-3g.so.4921"
+#define LIBFILE "/lib/libntfs-3g.so.4921"
+#endif
+
+#define MAPDIR ".NTFS-3G"
+#define MAPFILE "UserMapping"
+#define MAGIC_API 0x09042009
+
+#ifndef _NTFS_ENDIANS_H
+
+typedef char s8;
+typedef short s16;
+typedef long long s64;
+typedef unsigned char u8;
+typedef unsigned short le16, be16, u16;
+typedef unsigned long long u64;
+#ifdef STSC
+typedef long s32;
+typedef unsigned long le32, be32, u32;
+#else
+typedef int s32;
+typedef unsigned int le32, be32, u32;
+#endif
+
+#ifdef STSC
+#define endian_rev16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L))
+#define endian_rev32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \
+ + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L))
+#else
+#define endian_rev16(x) ((((x) & 255) << 8) + (((x) >> 8) & 255))
+#define endian_rev32(x) ((((x) & 255) << 24) + (((x) & 0xff00) << 8) \
+ + (((x) >> 8) & 0xff00) + (((x) >> 24) & 255))
+#endif
+#define endian_rev64(x) ((((x) & 255LL) << 56) + (((x) & 0xff00LL) << 40) \
+ + (((x) & 0xff0000LL) << 24) + (((x) & 0xff000000LL) << 8) \
+ + (((x) >> 8) & 0xff000000LL) + (((x) >> 24) & 0xff0000LL) \
+ + (((x) >> 40) & 0xff00LL) + (((x) >> 56) & 255LL))
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+#define cpu_to_be16(x) endian_rev16(x)
+#define cpu_to_be32(x) endian_rev32(x)
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_le64(x) (x)
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#define le64_to_cpu(x) (x)
+
+#else
+
+#define cpu_to_be16(x) (x)
+#define cpu_to_be32(x) (x)
+#define cpu_to_le16(x) endian_rev16(x)
+#define cpu_to_le32(x) endian_rev32(x)
+#define cpu_to_le64(x) endian_rev64(x)
+#define le16_to_cpu(x) endian_rev16(x)
+#define le32_to_cpu(x) endian_rev32(x)
+#define le64_to_cpu(x) endian_rev64(x)
+
+#endif
+
+#define const_le16_to_cpu(x) le16_to_cpu(x)
+#define const_cpu_to_le16(x) cpu_to_le16(x)
+#define const_cpu_to_le32(x) cpu_to_le32(x)
+#define const_cpu_to_be16(x) cpu_to_be16(x)
+#define const_cpu_to_be32(x) cpu_to_be32(x)
+
+#endif /* _NTFS_ENDIANS_H */
+
+#ifndef FALSE
+enum { FALSE, TRUE } ;
+#endif /* FALSE */
+
+#ifdef WIN32
+
+typedef unsigned short uid_t;
+typedef unsigned short gid_t;
+
+#define UNICODE(c) ((unsigned short)(c))
+
+#define __attribute__(x)
+
+#else
+
+#ifndef BOOL
+typedef int BOOL; /* Already defined in windows.h */
+#endif /* BOOL */
+
+#ifdef STSC
+
+#define ENOTSUP 95
+
+#endif /* STSC */
+
+typedef u32 DWORD; /* must be 32 bits whatever the platform */
+typedef DWORD *LPDWORD;
+
+#define MS_NONE 0 /* no flag for mounting the device */
+#define MS_RDONLY 1 /* flag for mounting the device read-only */
+
+#endif /* WIN32 */
+
+#if defined(WIN32) | defined(STSC)
+
+/*
+ * On non-Linux computers, there is no mount and the user mapping
+ * if fetched from a real file (or a dummy one for self tests)
+ */
+
+#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf)
+#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf)
+#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid)
+#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid)
+
+#else
+
+/*
+ * On Linux computers, there is a mount and the user mapping
+ * if either obtained through the mount process or fetched
+ * from a dummy file for self-tests
+ */
+
+#define NTFS_FIND_USID(map,uid,buf) (mappingtype != MAPEXTERN ? \
+ ntfs_find_usid(map,uid,buf) : relay_find_usid(map,uid,buf))
+#define NTFS_FIND_GSID(map,gid,buf) (mappingtype != MAPEXTERN ? \
+ ntfs_find_gsid(map,gid,buf) : relay_find_gsid(map,gid,buf))
+#define NTFS_FIND_USER(map,usid) (mappingtype != MAPEXTERN ? \
+ ntfs_find_user(map,usid) : relay_find_user(map,usid))
+#define NTFS_FIND_GROUP(map,gsid) (mappingtype != MAPEXTERN ? \
+ ntfs_find_group(map,gsid) : relay_find_group(map,gsid))
+
+#endif
+
+/*
+ * A few name hijackings or definitions
+ * needed for using code from ntfs-3g
+ */
+
+#ifdef WIN32
+#define ACL MY_ACL
+#define SID MY_SID
+#define ACCESS_ALLOWED_ACE MY_ACCESS_ALLOWED_ACE
+#define ACCESS_DENIED_ACE MY_ACCESS_DENIED_ACE
+#define FILE_ATTRIBUTE_REPARSE_POINT 0x400
+#define IO_REPARSE_TAG_MOUNT_POINT 0xa0000003
+#define IO_REPARSE_TAG_SYMLINK 0xa000000c
+#else
+#define SE_OWNER_DEFAULTED const_cpu_to_le16(1)
+#define SE_GROUP_DEFAULTED const_cpu_to_le16(2)
+#define SE_DACL_PRESENT const_cpu_to_le16(4)
+#define SE_SACL_PRESENT const_cpu_to_le16(0x10)
+#define SE_DACL_DEFAULTED const_cpu_to_le16(8)
+#define SE_SELF_RELATIVE const_cpu_to_le16(0x8000)
+#define SID_REVISION 1
+#endif /* WIN32 */
+#define SE_DACL_PROTECTED const_cpu_to_le16(0x1000)
+#define SE_SACL_PROTECTED const_cpu_to_le16(0x2000)
+#define SE_DACL_AUTO_INHERITED const_cpu_to_le16(0x400)
+#define SE_SACL_AUTO_INHERITED const_cpu_to_le16(0x800)
+#define SE_DACL_AUTO_INHERIT_REQ cpu_to_le16(0x100)
+#define SE_SACL_AUTO_INHERIT_REQ cpu_to_le16(0x200)
+
+typedef le16 ntfschar;
+
+typedef struct {
+ le32 a;
+ le16 b,c;
+ struct {
+ le16 m,n,o,p, q,r,s,t;
+ } ;
+} GUID;
+
+#define ntfs_log_error(args...) do { printf("** " args); if (!isatty(1)) fprintf(stderr,args); } while(0)
+
+/*
+ * Struct to hold the input mapping file
+ * (private to this module)
+ */
+
+struct MAPLIST {
+ struct MAPLIST *next;
+ char *uidstr; /* uid text from the same record */
+ char *gidstr; /* gid text from the same record */
+ char *sidstr; /* sid text from the same record */
+ char maptext[LINESZ + 1];
+};
+
+/*
+ * A few dummy declarations needed for using code from security.c
+ */
+
+#define MFT_RECORD_IS_DIRECTORY const_cpu_to_le16(1)
+
+struct SECURITY_DATA {
+ u64 offset;
+ char *attr;
+ u32 hash;
+ u32 length;
+ unsigned int filecount:16;
+ unsigned int mode:12;
+ unsigned int flags:4;
+} ;
+
+ /* default security sub-authorities */
+enum {
+ DEFSECAUTH1 = -1153374643, /* 3141592653 */
+ DEFSECAUTH2 = 589793238,
+ DEFSECAUTH3 = 462843383,
+ DEFSECBASE = 10000
+};
+
+#define OWNERID 1016
+#define GROUPID 513
+
+
+#define INSDS1 1
+#define INSDS2 2
+#define INSII 4
+#define INSDH 8
+
+#ifdef WIN32
+
+typedef enum { RECSHOW, RECSET, RECSETPOSIX } RECURSE;
+
+#endif
+
+/*
+ * A type large enough to hold any SID
+ */
+
+typedef char BIGSID[40];
+
+/*
+ * Declarations for memory allocation checks
+ */
+
+struct CHKALLOC
+ {
+ struct CHKALLOC *next;
+ void *alloc;
+ const char *file;
+ int line;
+ size_t size;
+ } ;
+
+#if defined(WIN32) | defined(STSC)
+
+#define S_ISVTX 01000
+#define S_ISGID 02000
+#define S_ISUID 04000
+#define S_IXUSR 0100
+#define S_IWUSR 0200
+#define S_IRUSR 0400
+#define S_IXGRP 010
+#define S_IWGRP 020
+#define S_IRGRP 040
+#define S_IXOTH 001
+#define S_IWOTH 002
+#define S_IROTH 004
+
+#endif
+
+#ifdef WIN32
+#else
+/*
+ *
+ * See http://msdn2.microsoft.com/en-us/library/aa379649.aspx
+ */
+
+typedef enum {
+ DACL_SECURITY_INFORMATION = 4, // The DACL of the object is being referenced.
+ SACL_SECURITY_INFORMATION = 8, // The SACL of the object is being referenced.
+ LABEL_SECURITY_INFORMATION = 8, // The mandatory integrity label is being referenced.
+ GROUP_SECURITY_INFORMATION = 2, // The primary group identifier of the object is being referenced.
+ OWNER_SECURITY_INFORMATION = 1, // The owner identifier of the object is being referenced.
+} SECURITY_INFORMATION;
+
+#define STANDARD_RIGHTS_READ cpu_to_le32(0x20000)
+#define STANDARD_RIGHTS_WRITE cpu_to_le32(0x20000)
+#define STANDARD_RIGHTS_EXECUTE cpu_to_le32(0x20000)
+#define STANDARD_RIGHTS_REQUIRED cpu_to_le32(0xf0000)
+
+#endif
+
+typedef struct SECHEAD {
+ s8 revision;
+ s8 alignment;
+ le16 control;
+ le32 owner;
+ le32 group;
+ le32 sacl;
+ le32 dacl;
+} SECURITY_DESCRIPTOR_RELATIVE;
+
+typedef struct ACL {
+ s8 revision;
+ s8 alignment1;
+ le16 size;
+ le16 ace_count;
+ le16 alignment2;
+} ACL;
+
+typedef struct {
+ union {
+ struct {
+ unsigned char revision;
+ unsigned char sub_authority_count;
+ } ;
+ struct {
+ /* evade an alignment problem when a 4 byte field */
+ /* in a struct implies alignment of the struct */
+ le16 dummy;
+ be16 high_part;
+ be32 low_part;
+ } identifier_authority;
+ } ;
+ le32 sub_authority[1];
+} SID;
+
+typedef u8 ACE_FLAGS;
+
+typedef struct ACE {
+ u8 type;
+ u8 flags;
+ le16 size;
+ le32 mask;
+ SID sid;
+} ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE;
+
+
+/*
+ * item in the mapping list
+ */
+
+struct MAPPING {
+ struct MAPPING *next;
+ int xid; /* linux id : uid or gid */
+ SID *sid; /* Windows id : usid or gsid */
+ int grcnt; /* group count (for users only) */
+ gid_t *groups; /* groups which the user is member of */
+};
+
+/*
+ * Posix ACL structures
+ */
+
+struct POSIX_ACE {
+ u16 tag;
+ u16 perms;
+ s32 id;
+} ;
+
+struct POSIX_ACL {
+ u8 version;
+ u8 flags;
+ u16 filler;
+ struct POSIX_ACE ace[0];
+} ;
+
+struct POSIX_SECURITY {
+ mode_t mode;
+ int acccnt;
+ int defcnt;
+ int firstdef;
+ u16 tagsset;
+ struct POSIX_ACL acl;
+} ;
+
+/*
+ * Posix tags, cpu-endian 16 bits
+ */
+
+enum {
+ POSIX_ACL_USER_OBJ = 1,
+ POSIX_ACL_USER = 2,
+ POSIX_ACL_GROUP_OBJ = 4,
+ POSIX_ACL_GROUP = 8,
+ POSIX_ACL_MASK = 16,
+ POSIX_ACL_OTHER = 32,
+ POSIX_ACL_SPECIAL = 64 /* internal use only */
+} ;
+
+/*
+ * Posix permissions, cpu-endian 16 bits
+ */
+
+enum {
+ POSIX_PERM_X = 1,
+ POSIX_PERM_W = 2,
+ POSIX_PERM_R = 4,
+ POSIX_PERM_DENIAL = 64 /* internal use only */
+} ;
+
+#define POSIX_VERSION 2
+
+/*
+ * A few definitions adapted from winnt.h
+ * (Windows version uses actual definitions from winnt.h, which are
+ * not compatible with code from security.c on a big-endian computer)
+ */
+
+#ifndef WIN32
+
+#define DELETE cpu_to_le32(0x00010000L)
+#define READ_CONTROL cpu_to_le32(0x00020000L)
+#define WRITE_DAC cpu_to_le32(0x00040000L)
+#define WRITE_OWNER cpu_to_le32(0x00080000L)
+#define SYNCHRONIZE cpu_to_le32(0x00100000L)
+
+
+#define FILE_READ_DATA cpu_to_le32( 0x0001 ) // file & pipe
+#define FILE_LIST_DIRECTORY cpu_to_le32( 0x0001 ) // directory
+
+#define FILE_WRITE_DATA cpu_to_le32( 0x0002 ) // file & pipe
+#define FILE_ADD_FILE cpu_to_le32( 0x0002 ) // directory
+
+#define FILE_APPEND_DATA cpu_to_le32( 0x0004 ) // file
+#define FILE_ADD_SUBDIRECTORY cpu_to_le32( 0x0004 ) // directory
+#define FILE_CREATE_PIPE_INSTANCE cpu_to_le32( 0x0004 ) // named pipe
+
+
+#define FILE_READ_EA cpu_to_le32( 0x0008 ) // file & directory
+
+#define FILE_WRITE_EA cpu_to_le32( 0x0010 ) // file & directory
+
+#define FILE_EXECUTE cpu_to_le32( 0x0020 ) // file
+#define FILE_TRAVERSE cpu_to_le32( 0x0020 ) // directory
+
+#define FILE_DELETE_CHILD cpu_to_le32( 0x0040 ) // directory
+
+#define FILE_READ_ATTRIBUTES cpu_to_le32( 0x0080 ) // all
+
+#define FILE_WRITE_ATTRIBUTES cpu_to_le32( 0x0100 ) // all
+
+#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \
+ cpu_to_le32(0x1FF))
+
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |\
+ FILE_READ_DATA |\
+ FILE_READ_ATTRIBUTES |\
+ FILE_READ_EA |\
+ SYNCHRONIZE)
+
+
+#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |\
+ FILE_WRITE_DATA |\
+ FILE_WRITE_ATTRIBUTES |\
+ FILE_WRITE_EA |\
+ FILE_APPEND_DATA |\
+ SYNCHRONIZE)
+
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |\
+ FILE_READ_ATTRIBUTES |\
+ FILE_EXECUTE |\
+ SYNCHRONIZE)
+
+#define GENERIC_READ cpu_to_le32(0x80000000L)
+#define GENERIC_WRITE cpu_to_le32(0x40000000L)
+#define GENERIC_EXECUTE cpu_to_le32(0x20000000L)
+#define GENERIC_ALL cpu_to_le32(0x10000000L)
+
+
+#define OBJECT_INHERIT_ACE (0x1)
+#define CONTAINER_INHERIT_ACE (0x2)
+#define NO_PROPAGATE_INHERIT_ACE (0x4)
+#define INHERIT_ONLY_ACE (0x8)
+#define VALID_INHERIT_FLAGS (0xF)
+
+/*
+ * Other useful definitions
+ */
+
+#define ACL_REVISION 2
+#define ACCESS_ALLOWED_ACE_TYPE 0
+#define ACCESS_DENIED_ACE_TYPE 1
+#define SECURITY_DESCRIPTOR_REVISION 1
+
+#endif /* !WIN32 */
+
+#ifndef ACL_REVISION_DS /* not always defined in <windows.h> */
+#define ACL_REVISION_DS 4
+#endif
+
+/*
+ * Matching of ntfs permissions to Linux permissions
+ * these constants are adapted to endianness
+ * when setting, set them all
+ * when checking, check one is present
+ */
+
+ /* flags which are set to mean exec, write or read */
+
+#define FILE_READ (FILE_READ_DATA)
+#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \
+ | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
+#define FILE_EXEC (FILE_EXECUTE)
+#define DIR_READ FILE_LIST_DIRECTORY
+#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \
+ | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
+#define DIR_EXEC (FILE_TRAVERSE)
+
+ /* flags tested for meaning exec, write or read */
+ /* tests for write allow for interpretation of a sticky bit */
+
+#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ)
+#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)
+#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE)
+#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ)
+#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE)
+#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE)
+
+ /* standard owner (and administrator) rights */
+
+#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \
+ | SYNCHRONIZE \
+ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \
+ | FILE_READ_EA | FILE_WRITE_EA)
+
+ /* standard world rights */
+
+#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \
+ | SYNCHRONIZE)
+
+ /* inheritance flags for files and directories */
+
+#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE
+#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)
+
+/*
+ * To identify NTFS ACL meaning Posix ACL granted to root
+ * we use rights always granted to anybody, so they have no impact
+ * either on Windows or on Linux.
+ */
+
+#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */
+#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */
+
+
+struct SII { /* this is an image of an $SII index entry */
+ le16 offs;
+ le16 size;
+ le32 fill1;
+ le16 indexsz;
+ le16 indexksz;
+ le16 flags;
+ le16 fill2;
+ le32 keysecurid;
+
+ /* did not find official description for the following */
+ le32 hash;
+ le32 securid;
+ le32 dataoffsl; /* documented as badly aligned */
+ le32 dataoffsh;
+ le32 datasize;
+} ;
+
+struct SDH { /* this is an image of an $SDH index entry */
+ le16 offs;
+ le16 size;
+ le32 fill1;
+ le16 indexsz;
+ le16 indexksz;
+ le16 flags;
+ le16 fill2;
+ le32 keyhash;
+ le32 keysecurid;
+
+ /* did not find official description for the following */
+ le32 hash;
+ le32 securid;
+ le32 dataoffsl;
+ le32 dataoffsh;
+ le32 datasize;
+ le32 fill3;
+ } ;
+
+#ifndef INVALID_FILE_ATTRIBUTES /* not defined in old windows.h */
+#define INVALID_FILE_ATTRIBUTES (-1)
+#endif
+
+enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ;
+
+struct SECURITY_CONTEXT {
+ struct MAPPING *mapping[MAPCOUNT];
+} ;
+
+typedef enum { MAPNONE, MAPEXTERN, MAPLOCAL, MAPDUMMY } MAPTYPE;
+
+
+
+struct passwd {
+ uid_t pw_uid;
+} ;
+
+struct group {
+ gid_t gr_gid;
+} ;
+
+typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos);
+
+/*
+ * Data defined in secaudit.c
+ */
+
+extern MAPTYPE mappingtype;
+
+/*
+ * Functions defined in acls.c
+ */
+
+BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz);
+BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc);
+BOOL ntfs_valid_pattern(const SID *sid);
+BOOL ntfs_same_sid(const SID *first, const SID *second);
+
+
+int ntfs_sid_size(const SID * sid);
+unsigned int ntfs_attr_size(const char *attr);
+
+const SID *ntfs_find_usid(const struct MAPPING *usermapping,
+ uid_t uid, SID *pdefsid);
+const SID *ntfs_find_gsid(const struct MAPPING *groupmapping,
+ gid_t gid, SID *pdefsid);
+uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid);
+gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid);
+const SID *ntfs_acl_owner(const char *secattr);
+
+void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc);
+int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode);
+
+
+struct POSIX_SECURITY *ntfs_build_permissions_posix(
+ struct MAPPING* const mapping[],
+ const char *securattr,
+ const SID *usid, const SID *gsid, BOOL isdir);
+int ntfs_build_permissions(const char *securattr,
+ const SID *usid, const SID *gsid, BOOL isdir);
+struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid);
+struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem);
+struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem);
+void ntfs_free_mapping(struct MAPPING *mapping[]);
+
+struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first,
+ const struct POSIX_SECURITY *second);
+char *ntfs_build_descr_posix(struct MAPPING* const mapping[],
+ struct POSIX_SECURITY *pxdesc,
+ int isdir, const SID *usid, const SID *gsid);
+char *ntfs_build_descr(mode_t mode,
+ int isdir, const SID * usid, const SID * gsid);
+
+/*
+ * Functions defined in secaudit.c
+ */
+
+void *chkmalloc(size_t, const char*, int);
+void *chkcalloc(size_t, size_t, const char *, int);
+void chkfree(void*, const char*, int);
+BOOL chkisalloc(void*, const char*, int);
+void dumpalloc(const char*);
+
+#define malloc(sz) chkmalloc(sz, __FILE__, __LINE__)
+#define calloc(cnt,sz) chkcalloc(cnt, sz, __FILE__, __LINE__)
+#define free(ptr) chkfree(ptr, __FILE__, __LINE__)
+#define isalloc(ptr) chkisalloc(ptr, __FILE__, __LINE__)
+#define ntfs_malloc(sz) chkmalloc(sz, __FILE__, __LINE__)
+
+struct passwd *getpwnam(const char *user);
+struct group *getgrnam(const char *group);
+
+const SID *relay_find_usid(const struct MAPPING *usermapping,
+ uid_t uid, SID *pdefsid);
+const SID *relay_find_gsid(const struct MAPPING *groupmapping,
+ gid_t gid, SID *pdefsid);
+uid_t relay_find_user(const struct MAPPING *usermapping, const SID *usid);
+gid_t relay_find_group(const struct MAPPING *groupmapping, const SID * gsid);
+
diff --git a/src/usermap.c b/src/usermap.c
new file mode 100644
index 0000000..3d8ce6d
--- /dev/null
+++ b/src/usermap.c
@@ -0,0 +1,1353 @@
+/*
+ * Windows to Linux user mapping for ntfs-3g
+ *
+ *
+ * Copyright (c) 2007-2008 Jean-Pierre Andre
+ *
+ * A quick'n dirty program scanning owners of files in
+ * "c:\Documents and Settings" (and "c:\Users")
+ * and asking user to map them to Linux accounts
+ *
+ * History
+ *
+ * Sep 2007
+ * - first version, limited to Win32
+ *
+ * Oct 2007
+ * - ported to Linux (rewritten would be more correct)
+ *
+ * Nov 2007 Version 1.0.0
+ * - added more defaults
+ *
+ * Nov 2007 Version 1.0.1
+ * - avoided examining files whose name begin with a '$'
+ *
+ * Jan 2008 Version 1.0.2
+ * - moved user mapping file to directory .NTFS-3G (hidden for Linux)
+ * - fixed an error case in Windows version
+ *
+ * Nov 2008 Version 1.1.0
+ * - fixed recursions for account in Linux version
+ * - searched owner in c:\Users (standard location for Vista)
+ *
+ * May 2009 Version 1.1.1
+ * - reordered mapping records to limit usage of same SID for user and group
+ * - fixed decoding SIDs on 64-bit systems
+ * - fixed a pointer to dynamic data in mapping tables
+ * - fixed default mapping on Windows
+ * - fixed bug for renaming UserMapping on Windows
+ *
+ * May 2009 Version 1.1.2
+ * - avoided selecting DOS names on Linux
+ *
+ * Nov 2009 Version 1.1.3
+ * - shutdown compiler warnings for unused parameters
+ *
+ * Jan 2010 Version 1.1.4
+ * - fixed compilation problems for Mac OSX (Erik Larsson)
+ */
+
+/*
+ * 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 (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * General parameters which may have to be adapted to needs
+ */
+
+#ifdef HAVE_CONFIG_H
+#define USESTUBS 1 /* API stubs generated at link time */
+#else
+#define USESTUBS 0 /* direct calls to API, based on following definitions */
+#define ENVNTFS3G "NTFS3G"
+#define LIBFILE64 "/lib64/libntfs-3g.so.491"
+#define LIBFILE "/lib/libntfs-3g.so.491"
+#endif
+
+#define GET_FILE_SECURITY "ntfs_get_file_security"
+#define SET_FILE_SECURITY "ntfs_set_file_security"
+#define READ_DIRECTORY "ntfs_read_directory"
+#define INIT_FILE_SECURITY "ntfs_initialize_file_security"
+#define LEAVE_FILE_SECURITY "ntfs_leave_file_security"
+
+#define VERSION "1.1.4"
+#define MAPDIR ".NTFS-3G"
+#define MAPFILE "UserMapping"
+#define MAXATTRSZ 2048
+#define MAXSIDSZ 80
+#define MAXNAMESZ 256
+#define OWNERS1 "Documents and Settings"
+#define OWNERS2 "Users"
+
+/*
+ * Define WIN32 for a Windows execution
+ * may have to be adapted to compiler or something else
+ */
+
+#ifndef WIN32
+#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC)
+#define WIN32 1
+#endif
+#endif
+
+#ifdef WIN32
+#define BANNER "Generated by usermap for Windows, v " VERSION
+#else
+#define BANNER "Generated by usermap for Linux, v " VERSION
+#endif
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+/*
+ * Define the security API according to platform
+ */
+
+#ifdef WIN32
+
+#include <fcntl.h>
+#include <windows.h>
+
+#define STATIC
+
+typedef enum { DENIED, AGREED } boolean;
+
+#else
+
+#include <unistd.h>
+#include <dlfcn.h>
+
+typedef enum { DENIED, AGREED } boolean, BOOL;
+typedef unsigned int DWORD; /* must be 32 bits whatever the platform */
+typedef DWORD *LPDWORD;
+
+enum { OWNER_SECURITY_INFORMATION = 1,
+ GROUP_SECURITY_INFORMATION = 2,
+ DACL_SECURITY_INFORMATION = 4,
+ SACL_SECURITY_INFORMATION = 8
+} ;
+
+struct CALLBACK {
+ const char *accname;
+ const char *dir;
+ int levels;
+ int docset;
+} ;
+
+typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname,
+ int length, int type, long long pos, unsigned long long mft_ref,
+ unsigned int dt_type);
+
+#if USESTUBS
+
+#define STATIC static
+
+BOOL ntfs_get_file_security(void *scapi,
+ const char *path, DWORD selection,
+ char *buf, DWORD buflen, LPDWORD psize);
+BOOL ntfs_set_file_security(void *scapi,
+ const char *path, DWORD selection, const char *attr);
+BOOL ntfs_read_directory(void *scapi,
+ const char *path, dircallback callback, void *context);
+void *ntfs_initialize_file_security(const char *device,
+ int flags);
+BOOL ntfs_leave_file_security(void *scapi);
+
+#else
+
+#define STATIC
+
+BOOL (*ntfs_get_file_security)(void *scapi,
+ const char *path, DWORD selection,
+ char *buf, DWORD buflen, LPDWORD psize);
+BOOL (*ntfs_set_file_security)(void *scapi,
+ const char *path, DWORD selection, const char *attr);
+BOOL (*ntfs_read_directory)(void *scapi,
+ const char *path, dircallback callback, void *context);
+void *(*ntfs_initialize_file_security)(const char *device,
+ int flags);
+BOOL (*ntfs_leave_file_security)(void *scapi);
+
+#endif
+
+STATIC boolean open_security_api(void);
+STATIC boolean close_security_api(void);
+STATIC boolean open_volume(const char *volume);
+STATIC boolean close_volume(const char *volume);
+
+#endif
+
+struct MAPPING {
+ struct MAPPING *next;
+ const char *uidstr;
+ const char *gidstr;
+ const char *sidstr;
+ const unsigned char *sid;
+ const char *login;
+ boolean defined;
+};
+
+struct MAPPING *firstmapping;
+struct MAPPING *lastmapping;
+
+#ifdef WIN32
+char *currentwinname;
+char *currentdomain;
+unsigned char *currentsid;
+#endif
+
+#ifndef WIN32
+
+void *ntfs_handle;
+void *ntfs_context = (void*)NULL;
+
+/*
+ * Shut down compiler warnings for unused parameters
+ */
+
+static long unused(const void *p)
+{
+return ((long)p);
+}
+
+/*
+ * Open and close the security API (platform dependent)
+ */
+
+STATIC boolean open_security_api(void)
+{
+#if USESTUBS
+ return (AGREED);
+#else
+ char *error;
+ boolean err;
+ const char *libfile;
+
+ err = AGREED;
+ libfile = getenv(ENVNTFS3G);
+ if (!libfile)
+ libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE);
+ ntfs_handle = dlopen(libfile,RTLD_LAZY);
+ if (ntfs_handle) {
+ ntfs_initialize_file_security =
+ dlsym(ntfs_handle,INIT_FILE_SECURITY);
+ error = dlerror();
+ if (error)
+ fprintf(stderr," %s\n",error);
+ else {
+ ntfs_leave_file_security =
+ dlsym(ntfs_handle,LEAVE_FILE_SECURITY);
+ ntfs_get_file_security =
+ dlsym(ntfs_handle,GET_FILE_SECURITY);
+ ntfs_set_file_security =
+ dlsym(ntfs_handle,SET_FILE_SECURITY);
+ ntfs_read_directory =
+ dlsym(ntfs_handle,READ_DIRECTORY);
+ err = !ntfs_initialize_file_security
+ || !ntfs_leave_file_security
+ || !ntfs_get_file_security
+ || !ntfs_set_file_security
+ || !ntfs_read_directory;
+ if (error)
+ fprintf(stderr,"ntfs-3g API not available\n");
+ }
+ } else {
+ fprintf(stderr,"Could not open ntfs-3g library\n");
+ fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n");
+ fprintf(stderr,"to appropriate path and retry\n");
+ }
+ return (!err);
+#endif
+}
+
+STATIC boolean close_security_api(void)
+{
+#if USESTUBS
+ return (0);
+#else
+ return (!dlclose(ntfs_handle));
+#endif
+}
+
+/*
+ * Open and close a volume (platform dependent)
+ * assuming a single volume needs to be opened at any time
+ */
+
+STATIC boolean open_volume(const char *volume)
+{
+ boolean ok;
+
+ ok = DENIED;
+ if (!ntfs_context) {
+ ntfs_context = ntfs_initialize_file_security(volume,0);
+ if (ntfs_context) {
+ fprintf(stderr,"\"%s\" opened\n",volume);
+ ok = AGREED;
+ } else {
+ fprintf(stderr,"Could not open \"%s\"\n",volume);
+ fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume);
+ }
+ } else
+ fprintf(stderr,"A volume is already open\n");
+ return (ok);
+}
+
+STATIC boolean close_volume(const char *volume)
+{
+ boolean r;
+
+ r = ntfs_leave_file_security(ntfs_context);
+ if (r)
+ fprintf(stderr,"\"%s\" closed\n",volume);
+ else
+ fprintf(stderr,"Could not close \"%s\"\n",volume);
+ ntfs_context = (void*)NULL;
+ return (r);
+}
+
+/*
+ * A poor man's conversion of Unicode to UTF8
+ * We are assuming outputs to terminal expect UTF8
+ */
+
+STATIC void to_utf8(char *dst, const char *src, unsigned int cnt)
+{
+ unsigned int ch;
+ unsigned int i;
+
+ for (i=0; i<cnt; i++) {
+ ch = *src++ & 255;
+ ch += (*src++ & 255) << 8;
+ if (ch < 0x80)
+ *dst++ = ch;
+ else
+ if (ch < 0x1000) {
+ *dst++ = 0xc0 + (ch >> 6);
+ *dst++ = 0x80 + (ch & 63);
+ } else {
+ *dst++ = 0xe0 + (ch >> 12);
+ *dst++ = 0x80 + ((ch >> 6) & 63);
+ *dst++ = 0x80 + (ch & 63);
+ }
+ }
+ *dst = 0;
+}
+
+STATIC int utf8_size(const char *src, unsigned int cnt)
+{
+ unsigned int ch;
+ unsigned int i;
+ int size;
+
+ size = 0;
+ for (i=0; i<cnt; i++) {
+ ch = *src++ & 255;
+ ch += (*src++ & 255) << 8;
+ if (ch < 0x80)
+ size++;
+ else
+ if (ch < 0x1000)
+ size += 2;
+ else
+ size += 3;
+ }
+ return (size);
+}
+
+#endif
+
+
+STATIC void welcome(void)
+{
+ printf("\nThis tool will help you to build a mapping of Windows users\n");
+ printf("to Linux users.\n");
+ printf("Be prepared to give Linux user id (uid) and group id (gid)\n");
+ printf("for owners of files which will be selected.\n");
+}
+
+STATIC unsigned int get2l(const unsigned char *attr, int p)
+{
+ int i;
+ unsigned int v;
+
+ v = 0;
+ for (i = 0; i < 2; i++)
+ v += (attr[p + i] & 255) << (8 * i);
+ return (v);
+}
+
+STATIC unsigned long get4l(const unsigned char *attr, int p)
+{
+ int i;
+ unsigned long v;
+
+ v = 0;
+ for (i = 0; i < 4; i++)
+ v += (attr[p + i] & 255L) << (8 * i);
+ return (v);
+}
+
+STATIC unsigned long long get6h(const unsigned char *attr, int p)
+{
+ int i;
+ unsigned long long v;
+
+ v = 0;
+ for (i = 0; i < 6; i++)
+ v = (v << 8) + (attr[p + i] & 255L);
+ return (v);
+}
+
+STATIC char *decodesid(const unsigned char *sid)
+{
+ char *str;
+ int i;
+ unsigned long long auth;
+ unsigned long subauth;
+
+ str = (char *)malloc(MAXSIDSZ);
+ if (str) {
+ strcpy(str, "S");
+ sprintf(&str[strlen(str)], "-%d", sid[0]); /* revision */
+ auth = get6h(sid, 2);
+#ifdef WIN32
+ sprintf(&str[strlen(str)], "-%I64u", auth); /* main authority */
+#else
+ sprintf(&str[strlen(str)], "-%llu", auth); /* main authority */
+#endif
+ for (i = 0; (i < 8) && (i < sid[1]); i++) {
+ subauth = get4l(sid, 8 + 4 * i);
+ sprintf(&str[strlen(str)], "-%lu", subauth); /* sub-authority */
+ }
+ }
+ return (str);
+}
+
+/*
+ * Test whether a generic group (S-1-5-21- ... -513)
+ */
+
+STATIC boolean isgenericgroup(const char *sid)
+{
+ boolean yes;
+
+ yes = !strncmp(sid,"S-1-5-21-",9)
+ && !strcmp(strrchr(sid,'-'),"-513");
+ return (yes);
+}
+
+STATIC unsigned char *makegroupsid(const unsigned char *sid)
+{
+ static unsigned char groupsid[MAXSIDSZ];
+ int size;
+
+ size = 8 + 4*sid[1];
+ memcpy(groupsid, sid, size);
+ /* replace last level by 513 */
+ groupsid[size - 4] = 1;
+ groupsid[size - 3] = 2;
+ groupsid[size - 2] = 0;
+ groupsid[size - 1] = 0;
+ return (groupsid);
+}
+
+STATIC void domapping(const char *accname, const char *filename,
+ const unsigned char *sid, int type)
+{
+ char buf[81];
+ char *sidstr;
+ char *idstr;
+ int sidsz;
+ boolean reject;
+ struct MAPPING *mapping;
+ char *login;
+ char *p;
+
+ if ((get6h(sid, 2) == 5) && (get4l(sid, 8) == 21)) {
+ sidstr = decodesid(sid);
+ mapping = firstmapping;
+ while (mapping && strcmp(mapping->sidstr, sidstr))
+ mapping = mapping->next;
+ if (mapping
+ && (mapping->defined
+ || !accname
+ || !strcmp(mapping->login, accname)))
+ free(sidstr); /* decision already known */
+ else {
+ do {
+ reject = DENIED;
+ printf("\n");
+ if (accname)
+ printf("Under Windows login \"%s\"\n", accname);
+ printf(" file \"%s\" has no mapped %s\n",
+ filename,(type ? "group" : "owner"));
+ printf("By which Linux login should this file be owned ?\n");
+ printf("Enter %s of login, or just press \"enter\" if this file\n",
+ (type ? "gid" : "uid"));
+ printf("does not belong to a user, or you do not known to whom\n");
+ printf("\n");
+ if (type)
+ printf("Group : ");
+ else
+ printf("User : ");
+ p = fgets(buf, 80, stdin);
+ if (p && p[0] && (p[strlen(p) - 1] == '\n'))
+ p[strlen(p) - 1] = '\0';
+
+ if (p && p[0]
+ && ((p[0] == '0') || !strcmp(p, "root"))) {
+ printf("Please do not map users to root\n");
+ printf("Administrators will be mapped automatically\n");
+ reject = AGREED;
+ }
+ if (reject)
+ printf("Please retry\n");
+ } while (reject);
+ if (!mapping) {
+ mapping =
+ (struct MAPPING *)
+ malloc(sizeof(struct MAPPING));
+ mapping->next = (struct MAPPING *)NULL;
+ mapping->defined = DENIED;
+ if (lastmapping)
+ lastmapping->next = mapping;
+ else
+ firstmapping = mapping;
+ lastmapping = mapping;
+ }
+ if (mapping) {
+ if (p && p[0]) {
+ idstr = (char *)malloc(strlen(p) + 1);
+ if (idstr) {
+ strcpy(idstr, p);
+ if (type) {
+ mapping->uidstr = "";
+ mapping->gidstr = idstr;
+ } else {
+ mapping->uidstr = idstr;
+ mapping->gidstr = idstr;
+ }
+ mapping->defined = AGREED;
+ }
+ }
+ mapping->sidstr = sidstr;
+ if (accname) {
+ login = (char*)malloc(strlen(accname) + 1);
+ if (login)
+ strcpy(login,accname);
+ mapping->login = login;
+ } else
+ mapping->login = (char*)NULL;
+ sidsz = 8 + sid[1]*4;
+ p = (char*)malloc(sidsz);
+ if (p) {
+ memcpy(p, sid, sidsz);
+ }
+ mapping->sid = (unsigned char*)p;
+ }
+ }
+ }
+}
+
+STATIC void listaclusers(const char *accname, const unsigned char *attr, int off)
+{
+ int i;
+ int cnt;
+ int x;
+
+ cnt = get2l(attr, off + 4);
+ x = 8;
+ for (i = 0; i < cnt; i++) {
+ domapping(accname, (char *)NULL, &attr[off + x + 8], 2);
+ x += get2l(attr, off + x + 2);
+ }
+}
+
+#ifdef WIN32
+
+STATIC void account(const char *accname, const char *dir, const char *name, int type)
+{
+ unsigned char attr[MAXATTRSZ];
+ unsigned long attrsz;
+ char *fullname;
+ int attrib;
+
+ fullname = (char *)malloc(strlen(dir) + strlen(name) + 2);
+ if (fullname) {
+ strcpy(fullname, dir);
+ strcat(fullname, "\\");
+ strcat(fullname, name);
+ attrib = GetFileAttributes(fullname);
+ if (attrib & 0x10) { /* only directories processed */
+ if (GetFileSecurity
+ (fullname, OWNER_SECURITY_INFORMATION, attr, MAXATTRSZ,
+ &attrsz)) {
+ domapping(accname, name, &attr[20], 0);
+ attrsz = 0;
+ if (GetFileSecurity
+ (fullname, GROUP_SECURITY_INFORMATION, attr,
+ MAXATTRSZ, &attrsz))
+ domapping(accname, name, &attr[20], 1);
+ else
+ printf(" No group SID\n");
+ attrsz = 0;
+ if (GetFileSecurityA
+ (fullname, DACL_SECURITY_INFORMATION, attr,
+ MAXATTRSZ, &attrsz)) {
+ if (type == 0)
+ listaclusers(accname, attr, 20);
+ } else
+ printf
+ (" No discretionary access control list\n");
+ }
+ }
+ free(fullname);
+ }
+}
+
+#else
+
+STATIC void account(const char *accname, const char *dir, const char *name, int type)
+{
+ unsigned char attr[MAXATTRSZ];
+ DWORD attrsz;
+ char *fullname;
+
+ fullname = (char *)malloc(strlen(dir) + strlen(name) + 2);
+ if (fullname) {
+ strcpy(fullname, dir);
+ strcat(fullname, "/");
+ strcat(fullname, name);
+ if (ntfs_get_file_security(ntfs_context,
+ fullname, OWNER_SECURITY_INFORMATION,
+ (char*)attr, MAXATTRSZ, &attrsz)) {
+ domapping(accname, name, &attr[20], 0);
+ attrsz = 0;
+ if (ntfs_get_file_security(ntfs_context,
+ fullname, GROUP_SECURITY_INFORMATION,
+ (char*)attr, MAXATTRSZ, &attrsz))
+ domapping(accname, name, &attr[20], 1);
+ else
+ printf(" No group SID\n");
+ attrsz = 0;
+ if (ntfs_get_file_security(ntfs_context,
+ fullname, DACL_SECURITY_INFORMATION,
+ (char*)attr, MAXATTRSZ, &attrsz)) {
+ if (type == 0)
+ listaclusers(accname, attr, 20);
+ } else
+ printf(" No discretionary access control list for %s !\n",
+ dir);
+ }
+ free(fullname);
+ }
+}
+
+#endif
+
+
+/*
+ * recursive search of file owners and groups in a directory
+ */
+
+#ifdef WIN32
+
+STATIC boolean recurse(const char *accname, const char *dir, int levels)
+{
+ WIN32_FIND_DATA found;
+ HANDLE search;
+ char *filter;
+ char *fullname;
+ boolean err;
+
+ err = DENIED;
+ filter = (char *)malloc(strlen(dir) + 5);
+ if (filter) {
+ strcpy(filter, dir);
+ strcat(filter, "\\*.*");
+ search = FindFirstFile(filter, &found);
+ if (search != INVALID_HANDLE_VALUE) {
+ do {
+ if (found.cFileName[0] != '.') {
+ account(accname, dir, found.cFileName,1);
+ if (levels > 0) {
+ fullname =
+ (char *)malloc(strlen(dir) +
+ strlen(found.cFileName)
+ + 2);
+ if (fullname) {
+ strcpy(fullname, dir);
+ strcat(fullname, "\\");
+ strcat(fullname,
+ found.cFileName);
+ recurse(accname,
+ fullname,
+ levels - 1);
+ free(fullname);
+ }
+ }
+ }
+ } while (FindNextFile(search, &found));
+ FindClose(search);
+ }
+ free(filter);
+ } else {
+ printf("Directory %s not found\n",dir);
+ err = AGREED;
+ }
+ return (!err);
+}
+
+#else
+
+STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset);
+
+STATIC int callback(struct CALLBACK *context, char *ntfsname,
+ int length, int type, long long pos, unsigned long long mft_ref,
+ unsigned int dt_type)
+{
+ char *fullname;
+ char *accname;
+ char *name;
+
+ unused((void*)&pos);
+ unused((void*)&mft_ref);
+ unused((void*)&dt_type);
+ fullname = (char *)malloc(strlen(context->dir)
+ + utf8_size(ntfsname, length) + 2);
+ if (fullname) {
+ if (strcmp(context->dir,"/")) {
+ strcpy(fullname, context->dir);
+ strcat(fullname, "/");
+ } else
+ strcpy(fullname,"/");
+ /* Unicode to ascii conversion by a lazy man */
+ name = &fullname[strlen(fullname)];
+ to_utf8(name, ntfsname, length);
+ /* ignore special files and DOS names */
+ if ((type != 2)
+ && strcmp(name,".")
+ && strcmp(name,"..")
+ && (name[0] != '$')) {
+ switch (context->docset) {
+ case 2 :
+ /*
+ * only "Documents and Settings"
+ * or "Users"
+ */
+ if (!strcmp(name,OWNERS1)
+ || !strcmp(name,OWNERS2)) {
+ recurse((char*)NULL, fullname, 2, 1);
+ }
+ break;
+ /*
+ * within "Documents and Settings"
+ * or "Users"
+ */
+ case 1 :
+ accname = (char*)malloc(strlen(name) + 1);
+ if (accname) {
+ strcpy(accname, name);
+ if (context->levels > 0)
+ recurse(name, fullname,
+ context->levels - 1, 0);
+ }
+ break;
+ /*
+ * not related to "Documents and Settings"
+ * or "Users"
+ */
+ case 0 :
+ account(context->accname, context->dir,
+ name, 1);
+ if (context->levels > 0)
+ recurse(context->accname, fullname,
+ context->levels - 1, 0);
+ break;
+ }
+ }
+ free(fullname);
+ }
+/* check expected return value */
+ return (0);
+}
+
+STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset)
+{
+ struct CALLBACK context;
+ boolean err;
+
+ err = DENIED;
+ context.dir = dir;
+ context.accname = accname;
+ context.levels = levels;
+ context.docset = docset;
+ ntfs_read_directory(ntfs_context,dir,callback,&context);
+ return (!err);
+}
+#endif
+
+/*
+ * Search directory "Documents and Settings" for user accounts
+ */
+
+#ifdef WIN32
+
+STATIC boolean getusers(const char *dir, int levels)
+{
+ WIN32_FIND_DATA found;
+ HANDLE search;
+ char *filter;
+ char *fullname;
+ char *accname;
+ boolean err;
+ const char *docset;
+
+ /* first get files from "Documents and Settings" */
+ err = DENIED;
+ if (sizeof(OWNERS1) > sizeof(OWNERS2))
+ filter = (char *)malloc(strlen(dir) + strlen(OWNERS1) + 6);
+ else
+ filter = (char *)malloc(strlen(dir) + strlen(OWNERS2) + 6);
+ if (filter) {
+ docset = OWNERS1;
+ strcpy(filter, dir);
+ strcat(filter, "\\");
+ strcat(filter, docset);
+ strcat(filter, "\\*.*");
+ search = FindFirstFile(filter, &found);
+ /* if failed, retry with "Users" */
+ if (search == INVALID_HANDLE_VALUE) {
+ docset = OWNERS2;
+ strcpy(filter, dir);
+ strcat(filter, "\\");
+ strcat(filter, docset);
+ strcat(filter, "\\*.*");
+ search = FindFirstFile(filter, &found);
+ }
+ if (search != INVALID_HANDLE_VALUE) {
+ do {
+ if (found.cFileName[0] != '.') {
+ fullname =
+ (char *)malloc(strlen(dir)
+ + strlen(docset)
+ + strlen(found.cFileName) + 3);
+ accname = (char *)
+ malloc(strlen(found.cFileName) + 1);
+ if (fullname && accname) {
+ strcpy(accname,
+ found.cFileName);
+
+ strcpy(fullname, dir);
+ strcat(fullname, "\\");
+ strcat(fullname, docset);
+ strcat(fullname, "\\");
+ strcat(fullname,
+ found.cFileName);
+ recurse(accname, fullname, 2);
+
+ free(fullname);
+ }
+ }
+ } while (FindNextFile(search, &found));
+ FindClose(search);
+ } else {
+ printf("No subdirectory found in %s\\%s\n",dir,docset);
+ }
+ /* now search in other directories */
+ strcpy(filter, dir);
+ strcat(filter, "\\*.*");
+ search = FindFirstFile(filter, &found);
+ if (search != INVALID_HANDLE_VALUE) {
+ do {
+ if ((found.cFileName[0] != '.')
+ && strcmp(found.cFileName,OWNERS1)
+ && strcmp(found.cFileName,OWNERS2)) {
+ fullname =
+ (char *)malloc(strlen(dir)
+ + strlen(found.cFileName) + 2);
+ if (fullname) {
+ strcpy(fullname, dir);
+ strcat(fullname, "\\");
+ strcat(fullname,
+ found.cFileName);
+ recurse((char*)NULL, fullname, 2);
+ free(fullname);
+ }
+ }
+ } while (FindNextFile(search, &found));
+ FindClose(search);
+ } else {
+ printf("No directory found in %s\n",dir);
+ err = AGREED;
+ }
+ }
+ return (!err);
+}
+
+#else
+
+STATIC boolean getusers(const char *dir, int levels)
+{
+ boolean err;
+ struct CALLBACK context;
+
+ printf("* Search for \"" OWNERS1 "\" and \"" OWNERS2 "\"\n");
+ err = DENIED;
+ context.dir = dir;
+ context.accname = (const char*)NULL;
+ context.levels = levels;
+ context.docset = 2;
+ ntfs_read_directory(ntfs_context,dir,callback,&context);
+ printf("* Search for other directories %s\n",dir);
+ context.docset = 0;
+ ntfs_read_directory(ntfs_context,dir,callback,&context);
+
+ return (!err);
+}
+
+#endif
+
+#ifdef WIN32
+/*
+ * Get the current login name (Win32 only)
+ */
+
+STATIC void loginname(boolean silent)
+{
+ char *winname;
+ char *domain;
+ unsigned char *sid;
+ unsigned long namesz;
+ unsigned long sidsz;
+ unsigned long domainsz;
+ int nametype;
+ boolean ok;
+ int r;
+
+ ok = FALSE;
+ winname = (char*)malloc(MAXNAMESZ);
+ domain = (char*)malloc(MAXNAMESZ);
+ sid = (char*)malloc(MAXSIDSZ);
+
+ namesz = MAXNAMESZ;
+ domainsz = MAXNAMESZ;
+ sidsz = MAXSIDSZ;
+ if (winname
+ && domain
+ && sid
+ && GetUserName(winname,&namesz)) {
+ winname[namesz] = '\0';
+ if (!silent)
+ printf("Your current user name is %s\n",winname);
+ nametype = 1;
+ r = LookupAccountName((char*)NULL,winname,sid,&sidsz,
+ domain,&domainsz,&nametype);
+ if (r) {
+ domain[domainsz] = '\0';
+ if (!silent)
+ printf("Your account domain is %s\n",domain);
+ ok = AGREED;
+ }
+ }
+ if (ok) {
+ currentwinname = winname;
+ currentdomain = domain;
+ currentsid = sid;
+ } else {
+ currentwinname = (char*)NULL;
+ currentdomain = (char*)NULL;
+ currentsid = (unsigned char*)NULL;
+ }
+}
+
+/*
+ * Minimal output on stdout
+ */
+
+boolean minimal(unsigned char *sid)
+{
+ const unsigned char *groupsid;
+ boolean ok;
+
+ ok = DENIED;
+ if (sid) {
+ groupsid = makegroupsid(sid);
+ printf("# %s\n",BANNER);
+ printf("# For Windows account \"%s\" in domain \"%s\"\n",
+ currentwinname, currentdomain);
+ printf("# Replace \"user\" and \"group\" hereafter by matching Linux login\n");
+ printf("user::%s\n",decodesid(sid));
+ printf(":group:%s\n",decodesid(groupsid));
+ ok = AGREED;
+ }
+ return (ok);
+}
+
+#endif
+
+STATIC boolean outputmap(const char *volume, const char *dir)
+{
+ char buf[256];
+ int fn;
+ char *fullname;
+ char *backup;
+ struct MAPPING *mapping;
+ boolean done;
+ boolean err;
+ boolean undecided;
+#ifdef WIN32
+#else
+ struct stat st;
+ int s;
+#endif
+
+ done = DENIED;
+ fullname = (char *)malloc(strlen(MAPFILE) + 1
+ + strlen(volume) + 1
+ + (dir ? strlen(dir) + 1 : 0));
+ if (fullname) {
+#ifdef WIN32
+ strcpy(fullname, volume);
+ if (dir && dir[0]) {
+ strcat(fullname, "\\");
+ strcat(fullname,dir);
+ }
+
+ /* build directory, if not present */
+ if (GetFileAttributes(fullname) & 0x80000000) {
+ printf("* Creating directory %s\n", fullname);
+ mkdir(fullname);
+ }
+
+ strcat(fullname, "\\");
+ strcat(fullname, MAPFILE);
+ printf("\n");
+
+ if (!(GetFileAttributes(fullname) & 0x80000000)) {
+ backup = (char*)malloc(strlen(fullname) + 5);
+ strcpy(backup,fullname);
+ strcat(backup,".bak");
+ unlink(backup);
+ if (!rename(fullname,backup))
+ printf("* Old mapping file moved to %s\n",backup);
+ }
+#else
+ strcpy(fullname, MAPFILE);
+ printf("\n");
+
+ s = stat(fullname,&st);
+ if (!s) {
+ backup = (char*)malloc(strlen(fullname + 5));
+ strcpy(backup,fullname);
+ strcat(backup,".bak");
+ if (rename(fullname,backup))
+ printf("* Old mapping file moved to %s\n",backup);
+ }
+#endif
+
+ printf("* Creating file %s\n", fullname);
+ err = DENIED;
+#ifdef WIN32
+ fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY + O_BINARY,
+ S_IREAD + S_IWRITE);
+#else
+ fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY,
+ S_IREAD + S_IWRITE);
+#endif
+ if (fn > 0) {
+ sprintf(buf,"# %s\n",BANNER);
+ if (!write(fn,buf,strlen(buf)))
+ err = AGREED;
+ printf("%s",buf);
+ undecided = DENIED;
+ /* records for owner only or group only */
+ for (mapping = firstmapping; mapping && !err;
+ mapping = mapping->next)
+ if (mapping->defined
+ && (!mapping->uidstr[0] || !mapping->gidstr[0])) {
+ sprintf(buf,"%s:%s:%s\n",
+ mapping->uidstr,
+ mapping->gidstr,
+ mapping->sidstr);
+ if (!write(fn,buf,strlen(buf)))
+ err = AGREED;
+ printf("%s",buf);
+ } else
+ undecided = AGREED;
+ /* records for both owner and group */
+ for (mapping = firstmapping; mapping && !err;
+ mapping = mapping->next)
+ if (mapping->defined
+ && mapping->uidstr[0] && mapping->gidstr[0]) {
+ sprintf(buf,"%s:%s:%s\n",
+ mapping->uidstr,
+ mapping->gidstr,
+ mapping->sidstr);
+ if (!write(fn,buf,strlen(buf)))
+ err = AGREED;
+ printf("%s",buf);
+ } else
+ undecided = AGREED;
+ done = !err;
+ close(fn);
+ if (undecided) {
+ printf("Undecided :\n");
+ for (mapping = firstmapping; mapping;
+ mapping = mapping->next)
+ if (!mapping->defined) {
+ printf(" %s\n", mapping->sidstr);
+ }
+ }
+#ifndef WIN32
+ printf("\n* You will have to move the file \"" MAPFILE "\"\n");
+ printf(" to directory \"" MAPDIR "\" after mounting\n");
+#endif
+ }
+ }
+ if (!done)
+ fprintf(stderr, "* Could not create mapping file \"%s\"\n", fullname);
+ return (done);
+}
+
+STATIC boolean sanitize(void)
+{
+ char buf[81];
+ boolean ok;
+ int ownercnt;
+ int groupcnt;
+ struct MAPPING *mapping;
+ struct MAPPING *firstowner;
+ struct MAPPING *genericgroup;
+ struct MAPPING *group;
+ char *sidstr;
+
+ /* count owners and groups */
+ /* and find first user, and a generic group */
+ ownercnt = 0;
+ groupcnt = 0;
+ firstowner = (struct MAPPING*)NULL;
+ genericgroup = (struct MAPPING*)NULL;
+ for (mapping=firstmapping; mapping; mapping=mapping->next) {
+ if (mapping->defined && mapping->uidstr[0]) {
+ if (!ownercnt)
+ firstowner = mapping;
+ ownercnt++;
+ }
+ if (mapping->defined && mapping->gidstr[0] && !mapping->uidstr[0]) {
+ groupcnt++;
+ }
+ if (!mapping->defined && isgenericgroup(mapping->sidstr)) {
+ genericgroup = mapping;
+ }
+ }
+#ifdef WIN32
+ /* no user defined, on Windows, suggest a mapping */
+ /* based on account currently used */
+ if (!ownercnt && currentwinname && currentsid) {
+ char *owner;
+ char *p;
+
+ printf("\nYou have defined no file owner,\n");
+ printf(" please enter the Linux login which should be mapped\n");
+ printf(" to account you are currently using\n");
+ printf(" Linux user ? ");
+ p = fgets(buf, 80, stdin);
+ if (p && p[0] && (p[strlen(p) - 1] == '\n'))
+ p[strlen(p) - 1] = '\0';
+ if (p && p[0]) {
+ firstowner = (struct MAPPING*)malloc(sizeof(struct MAPPING));
+ owner = (char*)malloc(strlen(p) + 1);
+ if (firstowner && owner) {
+ strcpy(owner, p);
+ firstowner->next = firstmapping;
+ firstowner->uidstr = owner;
+ firstowner->gidstr = "";
+ firstowner->sidstr = decodesid(currentsid);
+ firstowner->sid = currentsid;
+ firstmapping = firstowner;
+ ownercnt++;
+ /* prefer a generic group with the same authorities */
+ for (mapping=firstmapping; mapping;
+ mapping=mapping->next)
+ if (!mapping->defined
+ && isgenericgroup(mapping->sidstr)
+ && !memcmp(firstowner->sidstr,
+ mapping->sidstr,
+ strlen(mapping->sidstr)-3))
+ genericgroup = mapping;
+ }
+ }
+ }
+#endif
+ if (ownercnt) {
+ /*
+ * No group was selected, but there were a generic group
+ * insist in using it, associated to the first user
+ */
+ if (!groupcnt) {
+ printf("\nYou have defined no group, this can cause problems\n");
+ printf("Do you accept defining a standard group ?\n");
+ if (!fgets(buf,80,stdin)
+ || ((buf[0] != 'n')
+ && (buf[0] != 'N'))) {
+ if (genericgroup) {
+ genericgroup->uidstr = "";
+ genericgroup->gidstr = firstowner->uidstr;
+ genericgroup->defined = AGREED;
+ } else {
+ group = (struct MAPPING*)
+ malloc(sizeof(struct MAPPING));
+ sidstr = decodesid(
+ makegroupsid(firstowner->sid));
+ if (group && sidstr) {
+ group->uidstr = "";
+ group->gidstr = firstowner->
+ uidstr;
+ group->sidstr = sidstr;
+ group->defined = AGREED;
+ group->next = firstmapping;
+ firstmapping = group;
+ }
+ }
+ }
+ }
+ ok = AGREED;
+ } else {
+ printf("\nYou have defined no user, no mapping can be built\n");
+ ok = DENIED;
+ }
+
+ return (ok);
+}
+
+STATIC boolean checkoptions(int argc, char *argv[], boolean silent)
+{
+ boolean err;
+#ifdef WIN32
+ int xarg;
+ const char *pvol;
+
+ if (silent)
+ err = (argc != 1);
+ else {
+ err = (argc < 2);
+ for (xarg=1; (xarg<argc) && !err; xarg++) {
+ pvol = argv[xarg];
+ if (pvol[0] && (pvol[1] == ':') && !pvol[2]) {
+ err = !(((pvol[0] >= 'A') && (pvol[0] <= 'Z'))
+ || ((pvol[0] >= 'a') && (pvol[0] <= 'z')));
+ }
+ }
+ }
+ if (err) {
+ fprintf(stderr, "Usage : usermap [vol1: [vol2: ...]]\n");
+ fprintf(stderr, " \"voln\" are the letters of the partition to share with Linux\n");
+ fprintf(stderr, " eg C:\n");
+ fprintf(stderr, " the Windows system partition should be named first\n");
+ }
+#else
+ unused((void*)argv);
+ unused((void*)&silent);
+ err = (argc < 2);
+ if (err) {
+ fprintf(stderr, "Usage : usermap dev1 [dev2 ...]\n");
+ fprintf(stderr, " \"dev.\" are the devices to share with Windows\n");
+ fprintf(stderr, " eg /dev/sdb1\n");
+ fprintf(stderr, " the devices should not be mounted\n");
+ fprintf(stderr, " the Windows system partition should be named first\n");
+ } else
+ if (getuid()) {
+ fprintf(stderr, "\nSorry, only root can start usermap\n");
+ err = AGREED;
+ }
+#endif
+ return (!err);
+}
+
+STATIC boolean process(int argc, char *argv[])
+{
+ boolean ok;
+ int xarg;
+ int targ;
+
+ firstmapping = (struct MAPPING *)NULL;
+ lastmapping = (struct MAPPING *)NULL;
+ ok = AGREED;
+#ifdef WIN32
+ for (xarg=1; (xarg<argc) && ok; xarg++) {
+ printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]);
+ ok = getusers(argv[xarg],2);
+ }
+#else
+ for (xarg=1; (xarg<argc) && ok; xarg++)
+ if (open_volume(argv[xarg])) {
+ printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]);
+ ok = getusers("/",2);
+ close_volume(argv[xarg]);
+ } else
+ ok = DENIED;
+#endif
+ if (ok && sanitize()) {
+ targ = (argc > 2 ? 2 : 1);
+ if (!outputmap(argv[targ],MAPDIR)) {
+ printf("Trying to write file on root directory\n");
+ if (outputmap(argv[targ],(const char*)NULL)) {
+ printf("\nNote : you will have to move the file to directory \"%s\" on Linux\n",
+ MAPDIR);
+ } else
+ ok = DENIED;
+ } else
+ ok = DENIED;
+ } else
+ ok = DENIED;
+ return (ok);
+}
+
+int main(int argc, char *argv[])
+{
+ boolean ok;
+ boolean silent;
+
+ silent = !isatty(1);
+ if (!silent)
+ welcome();
+ if (checkoptions(argc, argv, silent)) {
+#ifdef WIN32
+ loginname(silent);
+ if (silent)
+ ok = minimal(currentsid);
+ else
+ ok = process(argc, argv);
+#else
+ if (open_security_api()) {
+ ok = process(argc,argv);
+ if (!close_security_api()) ok = DENIED;
+ }
+#endif
+ } else
+ ok = DENIED;
+ if (!ok)
+ exit(1);
+ return (0);
+}