OSDN Git Service

Used plugins to process reparse points
authorJean-Pierre André <jpandre@users.sourceforge.net>
Wed, 6 Apr 2016 07:10:35 +0000 (09:10 +0200)
committerJean-Pierre André <jpandre@users.sourceforge.net>
Wed, 6 Apr 2016 07:10:35 +0000 (09:10 +0200)
The new "system compression" files used by Windows 10 make use of reparse
points to record the compression parameters, and a specific named data
stream is used to store the compressed data. With this patch, processing
of reparse points can be done by an external plugin only loaded as needed.
Junctions and symlinks, which are also based on reparse points, are now
processed by "internal plugins".

README
configure.ac
include/ntfs-3g/Makefile.am
include/ntfs-3g/plugin.h [new file with mode: 0644]
include/ntfs-3g/reparse.h
libntfs-3g/reparse.c
src/Makefile.am
src/lowntfs-3g.c
src/ntfs-3g.c
src/ntfs-3g_common.c
src/ntfs-3g_common.h

diff --git a/README b/README
index 0f6a7db..daf5ccf 100644 (file)
--- a/README
+++ b/README
@@ -16,8 +16,10 @@ reliability and feature richness per invested effort wise.
 
 Besides the common file system features, NTFS-3G has support for file 
 ownership and permissions, POSIX ACLs, junction points, extended attributes 
-and creating compressed files. Parameter files in the directory .NTFS-3G may 
-be required to enable them, please get the instructions from
+and creating internally compressed files (parameter files in the directory
+.NTFS-3G may be required to enable them). The new compressed file formats
+available in Windows 10 can also be read through a plugin. For using
+advanced features, please get the instructions from
 
        http://www.tuxera.com/community/ntfs-3g-advanced/
 
@@ -59,6 +61,7 @@ typing :
 
 Below are a few specific options to ./configure :
        --disable-ntfsprogs : do not build the ntfsprogs tools,
+       --disable-plugins : disable support for plugins
        --enable-posix-acls : enable support for Posix ACLs
        --enable-xattr-mappings : enable system extended attributes mappings
        --with-fuse=external : use external fuse (overriding Linux default)
index d31a9cd..b14b00a 100644 (file)
@@ -122,6 +122,14 @@ AC_ARG_ENABLE(
 )
 
 AC_ARG_ENABLE(
+       [plugins],
+       [AS_HELP_STRING([--disable-plugins], [Disable external reparse point
+        plugins for the ntfs-3g FUSE driver])],
+       ,
+       [disable_plugins="no"]
+)
+
+AC_ARG_ENABLE(
        [device-default-io-ops],
        [AS_HELP_STRING([--disable-device-default-io-ops],[install default IO ops])],
        ,
@@ -566,6 +574,7 @@ test "${enable_device_default_io_ops}" = "no" && AC_DEFINE(
 test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab])
 test "${enable_posix_acls}" != "no" && AC_DEFINE([POSIXACLS], [1], [POSIX ACL support])
 test "${enable_xattr_mappings}" != "no" && AC_DEFINE([XATTR_MAPPINGS], [1], [system extended attributes mappings])
+test "${disable_plugins}" != "no" && AC_DEFINE([DISABLE_PLUGINS], [1], [Define to 1 for disabling reparse plugins])
 
 test "${enable_really_static}" = "yes" && enable_library="no"
 test "${enable_library}" = "no" && enable_ldconfig="no"
@@ -617,6 +626,7 @@ AM_CONDITIONAL([ENABLE_NTFS_3G], [test "${enable_ntfs_3g}" = "yes"])
 AM_CONDITIONAL([ENABLE_NTFSPROGS], [test "${enable_ntfsprogs}" = "yes"])
 AM_CONDITIONAL([ENABLE_EXTRAS], [test "${enable_extras}" = "yes"])
 AM_CONDITIONAL([ENABLE_QUARANTINED], [test "${enable_quarantined}" = "yes"])
+AM_CONDITIONAL([DISABLE_PLUGINS], [test "${disable_plugins}" != "no"])
 
 # workaround for <autoconf-2.60
 if test -z "${docdir}"; then
index 11761bc..7217c58 100644 (file)
@@ -30,7 +30,8 @@ headers = \
        mst.h           \
        ntfstime.h      \
        object_id.h     \
-       param.h \
+       param.h         \
+       plugin.h        \
        realpath.h      \
        reparse.h       \
        runlist.h       \
diff --git a/include/ntfs-3g/plugin.h b/include/ntfs-3g/plugin.h
new file mode 100644 (file)
index 0000000..bd90213
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ *             plugin.h : define interface for plugin development
+ *
+ * Copyright (c) 2015 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
+ */
+
+/*
+ *     This file defines the interface to ntfs-3g plugins which
+ *     add support for processing some type of reparse points.
+ */
+
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+struct fuse_file_info;
+
+       /*
+        *      The plugin operations currently defined.
+        * These functions should return a non-negative value when they
+        * succeed, or a negative errno value when they fail.
+        * They must not close or free their arguments.
+        * The file system must be left in a consistent state after
+        * each individual call.
+        * If an operation is not defined, an EOPNOTSUPP error is
+        * returned to caller.
+        */
+typedef struct plugin_operations {
+       /*
+        *      Set the attributes st_size, st_blocks and st_mode
+        * into a struct stat. The returned st_mode must at least
+        * define the file type. Depending on the permissions options
+        * used for mounting, the umask will be applied to the returned
+        * permissions, or the permissions will be changed according
+        * to the ACL set on the file.
+        */
+       int (*getattr)(ntfs_inode *ni, const REPARSE_POINT *reparse,
+                       struct stat *stbuf);
+
+       /*
+        *      Open a file for reading or writing
+        * The field fi->flags indicates the kind of opening.
+        * The field fi->fh may be used to store some information which
+        * will be available to subsequent reads and writes. When used
+        * this field must be non-null.
+        * The returned value is zero for success and a negative errno
+        * value for failure.
+        */
+       int (*open)(ntfs_inode *ni, const REPARSE_POINT *reparse,
+                       struct fuse_file_info *fi);
+
+       /*
+        *      Release an open file
+        * This is only called if fi->fh has been set to a non-null
+        * value while opening. It may be used to free some context
+        * specific to the open file.
+        * The returned value is zero for success or a negative errno
+        * value for failure.
+        */
+       int (*release)(ntfs_inode *ni, const REPARSE_POINT *reparse,
+                       struct fuse_file_info *fi);
+
+       /*
+        *      Read from an open file
+        * The returned value is the count of bytes which were read
+        * or a negative errno value for failure.
+        * If the returned value is positive, the access time stamp
+        * will be updated after the call.
+        */
+       int (*read)(ntfs_inode *ni, const REPARSE_POINT *reparse,
+                       char *buf, size_t size,
+                       off_t offset, struct fuse_file_info *fi);
+
+       /*
+        *      Write to an open file
+        * The file system must be left consistent after each write call,
+        * the file itself must be at least deletable if the application
+        * writing to it is killed for some reason.
+        * The returned value is the count of bytes which were written
+        * or a negative errno value for failure.
+        * If the returned value is positive, the modified time stamp
+        * will be updated after the call.
+        */
+       int (*write)(ntfs_inode *ni, const REPARSE_POINT *reparse,
+                       const char *buf, size_t size,
+                       off_t offset, struct fuse_file_info *fi);
+
+       /*
+        *      Get a symbolic link
+        * The symbolic link must be returned in an allocated buffer,
+        * encoded in a zero terminated multibyte string compatible
+        * which the locale mount option.
+        * The returned value is zero for success or a negative errno
+        * value for failure.
+        */
+       int (*readlink)(ntfs_inode *ni, const REPARSE_POINT *reparse,
+                       char **pbuf);
+
+       /*
+        *      Truncate a file (shorten or append zeroes)
+        * The returned value is zero for success or a negative errno
+        * value for failure.
+        * If the returned value is zero, the modified time stamp
+        * will be updated after the call.
+        */
+       int (*truncate)(ntfs_inode *ni, const REPARSE_POINT *reparse,
+                       off_t size);
+} plugin_operations_t;
+
+
+/*
+ *             Plugin initialization routine
+ *     Returns the entry table if successful, otherwise returns NULL
+ *     and sets errno (e.g. to EINVAL if the tag is not supported by
+ *     the plugin.)
+ */
+typedef const struct plugin_operations *(*plugin_init_t)(le32 tag);
+const struct plugin_operations *init(le32 tag);
+
+#endif /* PLUGIN_H */
index 35f4aa4..27e9050 100644 (file)
@@ -30,6 +30,8 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni);
 
 int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size);
 
+REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni);
+
 int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value,
                        size_t size, int flags);
 int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni);
index fe9d361..5e2f403 100644 (file)
@@ -3,7 +3,7 @@
  *
  *     This module is part of ntfs-3g library
  *
- * Copyright (c) 2008-2014 Jean-Pierre Andre
+ * Copyright (c) 2008-2016 Jean-Pierre Andre
  *
  * 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
@@ -1256,3 +1256,31 @@ int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni)
 }
 
 #endif /* HAVE_SETXATTR */
+
+/*
+ *             Get the reparse data into a buffer
+ *
+ *     Returns the buffer if the reparse data exists and is valid
+ *             NULL otherwise (with errno set according to the cause).
+ *     When a buffer is returned, it has to be freed by caller.
+ */
+
+REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni)
+{
+       s64 attr_size = 0;
+       REPARSE_POINT *reparse_attr;
+
+       reparse_attr = (REPARSE_POINT*)NULL;
+       if (ni) {
+               reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
+                       AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
+               if (reparse_attr
+                   && !valid_reparse_data(ni, reparse_attr, attr_size)) {
+                       free(reparse_attr);
+                       reparse_attr = (REPARSE_POINT*)NULL;
+                       errno = ENOENT;
+               }
+       } else
+               errno = EINVAL;
+       return (reparse_attr);
+}
index 7fd4af4..4edbcbc 100644 (file)
@@ -11,6 +11,11 @@ FUSE_CFLAGS = $(FUSE_MODULE_CFLAGS)
 FUSE_LIBS   = $(FUSE_MODULE_LIBS)
 endif
 
+if !DISABLE_PLUGINS
+plugindir        = $(libdir)/ntfs-3g
+PLUGIN_CFLAGS    = -DPLUGIN_DIR=\"$(plugindir)\"
+endif
+
 if ENABLE_NTFS_3G
 
 bin_PROGRAMS    = ntfs-3g.probe \
@@ -22,7 +27,7 @@ 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
+ntfs_3g_LDADD    = -ldl $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la
 if REALLYSTATIC
 ntfs_3g_LDFLAGS  = $(AM_LDFLAGS) -all-static
 endif
@@ -30,10 +35,11 @@ ntfs_3g_CFLAGS   =                  \
        $(AM_CFLAGS)                    \
        -DFUSE_USE_VERSION=26           \
        $(FUSE_CFLAGS)                  \
-       -I$(top_srcdir)/include/ntfs-3g
+       -I$(top_srcdir)/include/ntfs-3g \
+       $(PLUGIN_CFLAGS)
 ntfs_3g_SOURCES  = ntfs-3g.c ntfs-3g_common.c
 
-lowntfs_3g_LDADD    = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la
+lowntfs_3g_LDADD    = -ldl $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la
 if REALLYSTATIC
 lowntfs_3g_LDFLAGS  = $(AM_LDFLAGS) -all-static
 endif
@@ -41,7 +47,8 @@ lowntfs_3g_CFLAGS   =                 \
        $(AM_CFLAGS)                    \
        -DFUSE_USE_VERSION=26           \
        $(FUSE_CFLAGS)                  \
-       -I$(top_srcdir)/include/ntfs-3g
+       -I$(top_srcdir)/include/ntfs-3g \
+       $(PLUGIN_CFLAGS)
 lowntfs_3g_SOURCES  = lowntfs-3g.c ntfs-3g_common.c
 
 ntfs_3g_probe_LDADD    = $(top_builddir)/libntfs-3g/libntfs-3g.la
@@ -61,10 +68,13 @@ ntfs_3g_secaudit_SOURCES    = secaudit.c
 
 drivers : $(FUSE_LIBS) ntfs-3g lowntfs-3g
 
-if RUN_LDCONFIG
 install-exec-hook:
+if RUN_LDCONFIG
        $(LDCONFIG)
 endif
+if !DISABLE_PLUGINS
+       $(MKDIR_P) $(DESTDIR)/$(plugindir)
+endif
 
 if ENABLE_MOUNT_HELPER
 install-exec-local:    install-rootbinPROGRAMS
index 0bb38f9..899586c 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (c) 2005-2007 Yura Pakhuchiy
  * Copyright (c) 2005 Yuval Fledel
  * Copyright (c) 2006-2009 Szabolcs Szakacsits
- * Copyright (c) 2007-2015 Jean-Pierre Andre
+ * Copyright (c) 2007-2016 Jean-Pierre Andre
  * Copyright (c) 2009 Erik Larsson
  *
  * This file is originated from the Linux-NTFS project.
 #include "xattrs.h"
 #include "misc.h"
 #include "ioctl.h"
+#include "plugin.h"
 
 #include "ntfs-3g_common.h"
 
 #define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE
 #define INODE(ino) ((ino) == 1 ? (MFT_REF)FILE_root : (MFT_REF)(ino))
 
+/*
+ *             Call a function from a reparse plugin (variable arguments)
+ *     Requires "reparse" and "ops" to have been defined
+ *
+ *     Returns a non-negative value if successful,
+ *             and a negative error code if something fails.
+ */
+#define CALL_REPARSE_PLUGIN(ni, op_name, ...)                  \
+        (reparse = (REPARSE_POINT*)NULL,                        \
+        ops = select_reparse_plugin(ctx, ni, &reparse),         \
+        (!ops ? -errno                                          \
+                : (ops->op_name ?                               \
+                        ops->op_name(ni, reparse, __VA_ARGS__)  \
+                        : -EOPNOTSUPP))),                       \
+                free(reparse)
+
 typedef enum {
        FSTYPE_NONE,
        FSTYPE_UNKNOWN,
@@ -187,13 +204,17 @@ struct open_file {
        fuse_ino_t ino;
        fuse_ino_t parent;
        int state;
+#ifndef PLUGINS_DISABLED
+       struct fuse_file_info fi;
+#endif /* PLUGINS_DISABLED */
 } ;
 
 enum {
        CLOSE_GHOST = 1,
        CLOSE_COMPRESSED = 2,
        CLOSE_ENCRYPTED = 4,
-       CLOSE_DMTIME = 8
+       CLOSE_DMTIME = 8,
+       CLOSE_REPARSE = 16
 };
 
 enum RM_TYPES {
@@ -598,6 +619,67 @@ static void ntfs_init(void *userdata __attribute__((unused)),
 #endif /* defined(FUSE_CAP_IOCTL_DIR) */
 }
 
+#ifndef PLUGINS_DISABLED
+
+/*
+ *             Define attributes for a junction or symlink
+ *             (internal plugin)
+ */
+
+static int junction_getstat(ntfs_inode *ni,
+                       const REPARSE_POINT *reparse __attribute__((unused)),
+                       struct stat *stbuf)
+{
+       char *target;
+       int attr_size;
+       int res;
+
+       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_mode = S_IFLNK;
+               free(target);
+               res = 0;
+       } else {
+               res = -errno;
+       }
+       return (res);
+}
+
+/*
+ *             Apply permission masks to st_mode returned by reparse handler
+ */
+
+static void apply_umask(struct stat *stbuf)
+{
+       switch (stbuf->st_mode & S_IFMT) {
+       case S_IFREG :
+               stbuf->st_mode &= ~ctx->fmask;
+               break;
+       case S_IFDIR :
+               stbuf->st_mode &= ~ctx->dmask;
+               break;
+       case S_IFLNK :
+               stbuf->st_mode = (stbuf->st_mode & S_IFMT) | 0777;
+               break;
+       default :
+               break;
+       }
+}
+
+#endif /* PLUGINS_DISABLED */
+
 static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
                                ntfs_inode *ni, struct stat *stbuf)
 {
@@ -607,9 +689,27 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
 
        memset(stbuf, 0, sizeof(struct stat));
        withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL);
+       stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
        if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
            || (ni->flags & FILE_ATTR_REPARSE_POINT)) {
                if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+                       const plugin_operations_t *ops;
+                       REPARSE_POINT *reparse;
+
+                       res = CALL_REPARSE_PLUGIN(ni, getattr, stbuf);
+                       if (!res) {
+                               apply_umask(stbuf);
+                       } else {
+                               stbuf->st_size =
+                                       sizeof(ntfs_bad_reparse);
+                               stbuf->st_blocks =
+                                       (ni->allocated_size + 511) >> 9;
+                               stbuf->st_mode = S_IFLNK;
+                               res = 0;
+                       }
+                       goto ok;
+#else /* PLUGINS_DISABLED */
                        char *target;
                        int attr_size;
 
@@ -638,6 +738,7 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
                                res = -errno;
                                goto exit;
                        }
+#endif /* PLUGINS_DISABLED */
                } else {
                        /* Directory. */
                        stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
@@ -676,7 +777,6 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
                 * 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) {
@@ -742,6 +842,9 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
                }
                stbuf->st_mode |= (0777 & ~ctx->fmask);
        }
+#ifndef PLUGINS_DISABLED
+ok:
+#endif /* PLUGINS_DISABLED */
        if (withusermapping) {
                if (ntfs_get_owner_mode(scx,ni,stbuf) < 0)
                        set_fuse_error(&res);
@@ -884,6 +987,36 @@ static void ntfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent,
                fuse_reply_entry(req, &entry);
 }
 
+#ifndef PLUGINS_DISABLED
+
+/*
+ *             Get the link defined by a junction or symlink
+ *             (internal plugin)
+ */
+
+static int junction_readlink(ntfs_inode *ni,
+                       const REPARSE_POINT *reparse __attribute__((unused)),
+                       char **pbuf)
+{
+       int attr_size;
+       int res;
+
+       errno = 0;
+       res = 0;
+       *pbuf = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
+       if (!*pbuf) {
+               if (errno == EOPNOTSUPP) {
+                       *pbuf = strdup(ntfs_bad_reparse);
+                       if (!*pbuf)
+                               res = -errno;
+               } else
+                       res = -errno;
+       }
+       return (res);
+}
+
+#endif /* PLUGINS_DISABLED */
+
 static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino)
 {
        ntfs_inode *ni = NULL;
@@ -902,6 +1035,17 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino)
                 * Reparse point : analyze as a junction point
                 */
        if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               res = CALL_REPARSE_PLUGIN(ni, readlink, &buf);
+               if (res) {
+                       buf = strdup(ntfs_bad_reparse);
+                       if (!buf)
+                               res = -errno;
+               }
+#else /* PLUGINS_DISABLED */
                int attr_size;
 
                errno = 0;
@@ -913,7 +1057,8 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino)
                        if (!buf)
                                res = -errno;
                }
-               goto exit;
+#endif /* PLUGINS_DISABLED */
+               goto exit;
        }
        /* Sanity checks. */
        if (!(ni->flags & FILE_ATTR_SYSTEM)) {
@@ -1266,10 +1411,9 @@ static void ntfs_fuse_open(fuse_req_t req, fuse_ino_t ino,
                      struct fuse_file_info *fi)
 {
        ntfs_inode *ni;
-       ntfs_attr *na;
+       ntfs_attr *na = NULL;
        struct open_file *of;
        int state = 0;
-       char *path = NULL;
        int res = 0;
 #if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
        int accesstype;
@@ -1278,56 +1422,77 @@ static void ntfs_fuse_open(fuse_req_t req, fuse_ino_t ino,
 
        ni = ntfs_inode_open(ctx->vol, INODE(ino));
        if (ni) {
-               na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
-               if (na) {
+               if (!(ni->flags & FILE_ATTR_REPARSE_POINT)) {
+                       na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+                       if (!na) {
+                               res = -errno;
+                               goto close;
+                       }
+               }
 #if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
-                       if (ntfs_fuse_fill_security_context(req, &security)) {
-                               if (fi->flags & O_WRONLY)
-                                       accesstype = S_IWRITE;
+               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
-                                       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;
-                       }
+                                       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;
+               if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+                       const plugin_operations_t *ops;
+                       REPARSE_POINT *reparse;
+
+                       fi->fh = 0;
+                       res = CALL_REPARSE_PLUGIN(ni, open, fi);
+                       if (!res && fi->fh) {
+                               state = CLOSE_REPARSE;
+                       }
+#else /* PLUGINS_DISABLED */
+                       res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+                       goto close;
+               }
+               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;
+               /* 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;
+               /* 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 (ino < FILE_first_user)
+                               res = -EPERM;
+               }
+               ntfs_attr_close(na);
+close:
                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;
+#ifdef PLUGIN_ENABLED
+                       memcpy(&of->fi, fi, sizeof(struct fuse_file_info));
+#endif /* PLUGIN_ENABLED */
                        of->next = ctx->open_files;
                        of->previous = (struct open_file*)NULL;
                        if (ctx->open_files)
@@ -1368,6 +1533,22 @@ static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size,
                res = -errno;
                goto exit;
        }
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+               struct open_file *of;
+
+               of = (struct open_file*)fi;
+               res = CALL_REPARSE_PLUGIN(ni, read, buf, size, offset, &of->fi);
+               if (res >= 0) {
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+               res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+               goto exit;
+       }
        na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
        if (!na) {
                res = -errno;
@@ -1403,8 +1584,11 @@ static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size,
                total += ret;
        }
 ok:
-       ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME);
        res = total;
+#ifndef PLUGINS_DISABLED
+stamps :
+#endif /* PLUGINS_DISABLED */
+       ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME);
 exit:
        if (na)
                ntfs_attr_close(na);
@@ -1430,6 +1614,23 @@ static void ntfs_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
                res = -errno;
                goto exit;
        }
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+               struct open_file *of;
+
+               of = (struct open_file*)fi;
+               res = CALL_REPARSE_PLUGIN(ni, write, buf, size, offset,
+                                                               &of->fi);
+               if (res >= 0) {
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+               res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+               goto exit;
+       }
        na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
        if (!na) {
                res = -errno;
@@ -1446,15 +1647,18 @@ static void ntfs_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
                total  += ret;
        }
        res = total;
+#ifndef PLUGINS_DISABLED 
+stamps :
+#endif /* PLUGINS_DISABLED */
        if ((res > 0)
            && (!ctx->dmtime
                || (sle64_to_cpu(ntfs_current_time())
                     - sle64_to_cpu(ni->last_data_change_time)) > ctx->dmtime))
-               ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+               ntfs_fuse_update_times(ni, NTFS_UPDATE_MCTIME);
 exit:
        if (na)
                ntfs_attr_close(na);
-       if (total)
+       if (res > 0)
                set_archive(ni);
        if (ntfs_inode_close(ni))
                set_fuse_error(&res);
@@ -1607,9 +1811,11 @@ static int ntfs_fuse_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
                errno = EPERM;
                goto exit;
        }
-       na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
-       if (!na)
-               goto exit;
+       if (!(ni->flags & FILE_ATTR_REPARSE_POINT)) {
+               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
@@ -1622,6 +1828,21 @@ static int ntfs_fuse_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
                goto exit;
        }
 #endif
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               res = CALL_REPARSE_PLUGIN(ni, truncate, size);
+               if (!res) {
+                       set_archive(ni);
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+               res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+               goto exit;
+       }
                /*
                 * for compressed files, upsizing is done by inserting a final
                 * zero, which is optimized as creating a hole when possible. 
@@ -1637,8 +1858,11 @@ static int ntfs_fuse_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
                        goto exit;
        if (oldsize != size)
                set_archive(ni);
-        
-       ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+
+#ifndef PLUGINS_DISABLED
+stamps :
+#endif /* PLUGINS_DISABLED */
+       ntfs_fuse_update_times(ni, NTFS_UPDATE_MCTIME);
        res = ntfs_fuse_getstat(scx, ni, stbuf);
        errno = (res ? -res : 0);
 exit:
@@ -2550,8 +2774,8 @@ static void ntfs_fuse_release(fuse_req_t req, fuse_ino_t ino,
        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))) {
+           || !(of->state & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED
+                               | CLOSE_DMTIME | CLOSE_REPARSE))) {
                res = 0;
                goto out;
        }
@@ -2560,20 +2784,38 @@ static void ntfs_fuse_release(fuse_req_t req, fuse_ino_t ino,
                res = -errno;
                goto exit;
        }
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               res = CALL_REPARSE_PLUGIN(ni, release, &of->fi);
+               if (!res) {
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+                       /* Assume release() was not needed */
+               res = 0;
+#endif /* PLUGINS_DISABLED */
+               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 */
+#ifndef PLUGINS_DISABLED
+stamps :
+#endif /* PLUGINS_DISABLED */
+       if (of->state & CLOSE_DMTIME)
+               ntfs_inode_update_times(ni,NTFS_UPDATE_MCTIME);
 exit:
        if (na)
                ntfs_attr_close(na);
@@ -3568,6 +3810,20 @@ out :
 #endif
 #endif /* HAVE_SETXATTR */
 
+#ifndef PLUGINS_DISABLED
+static void register_internal_reparse_plugins(void)
+{
+       static const plugin_operations_t ops = {
+               .getattr = junction_getstat,
+               .readlink = junction_readlink,
+       } ;
+       register_reparse_plugin(ctx, IO_REPARSE_TAG_MOUNT_POINT,
+                                       &ops, (void*)NULL);
+       register_reparse_plugin(ctx, IO_REPARSE_TAG_SYMLINK,
+                                       &ops, (void*)NULL);
+}
+#endif /* PLUGINS_DISABLED */
+
 static void ntfs_close(void)
 {
        struct SECURITY_CONTEXT security;
@@ -4179,6 +4435,10 @@ int main(int argc, char *argv[])
                free(ctx->xattrmap_path);
 #endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
 
+#ifndef PLUGINS_DISABLED
+       register_internal_reparse_plugins();
+#endif /* PLUGINS_DISABLED */
+
        se = mount_fuse(parsed_options);
        if (!se) {
                err = NTFS_VOLUME_FUSE_ERROR;
@@ -4214,6 +4474,9 @@ err_out:
 #endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
 err2:
        ntfs_close();
+#ifndef PLUGINS_DISABLED
+       close_reparse_plugins(ctx);
+#endif /* PLUGINS_DISABLED */
        free(ctx);
        free(parsed_options);
        free(opts.options);
index 268b056..e3a23e5 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (c) 2005-2007 Yura Pakhuchiy
  * Copyright (c) 2005 Yuval Fledel
  * Copyright (c) 2006-2009 Szabolcs Szakacsits
- * Copyright (c) 2007-2015 Jean-Pierre Andre
+ * Copyright (c) 2007-2016 Jean-Pierre Andre
  * Copyright (c) 2009 Erik Larsson
  *
  * This file is originated from the Linux-NTFS project.
@@ -99,6 +99,7 @@
 #include "xattrs.h"
 #include "misc.h"
 #include "ioctl.h"
+#include "plugin.h"
 
 #include "ntfs-3g_common.h"
 
 
 #define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE
 
+/*
+ *             Call a function from a reparse plugin (variable arguments)
+ *     Requires "reparse" and "ops" to have been defined
+ *
+ *     Returns a non-negative value if successful,
+ *             and a negative error code if something fails.
+ */
+#define CALL_REPARSE_PLUGIN(ni, op_name, ...)                  \
+        (reparse = (REPARSE_POINT*)NULL,                        \
+        ops = select_reparse_plugin(ctx, ni, &reparse),         \
+        (!ops ? -errno                                          \
+                : (ops->op_name ?                               \
+                        ops->op_name(ni, reparse, __VA_ARGS__)  \
+                        : -EOPNOTSUPP))),                       \
+                free(reparse)
+
 typedef enum {
        FSTYPE_NONE,
        FSTYPE_UNKNOWN,
@@ -144,7 +161,8 @@ typedef struct {
 enum {
        CLOSE_COMPRESSED = 1,
        CLOSE_ENCRYPTED = 2,
-       CLOSE_DMTIME = 4
+       CLOSE_DMTIME = 4,
+       CLOSE_REPARSE = 8
 };
 
 static struct ntfs_options opts;
@@ -663,6 +681,67 @@ static void *ntfs_init(struct fuse_conn_info *conn)
        return NULL;
 }
 
+#ifndef PLUGINS_DISABLED
+
+/*
+ *             Define attributes for a junction or symlink
+ *             (internal plugin)
+ */
+
+static int junction_getattr(ntfs_inode *ni,
+               const REPARSE_POINT *reparse __attribute__((unused)),
+               struct stat *stbuf)
+{
+       char *target;
+       int attr_size;
+       int res;
+
+       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_mode = S_IFLNK;
+               free(target);
+               res = 0;
+       } else {
+               res = -errno;
+       }
+       return (res);
+}
+
+/*
+ *             Apply permission masks to st_mode returned by a reparse handler
+ */
+
+static void apply_umask(struct stat *stbuf)
+{
+       switch (stbuf->st_mode & S_IFMT) {
+       case S_IFREG :
+               stbuf->st_mode &= ~ctx->fmask;
+               break;
+       case S_IFDIR :
+               stbuf->st_mode &= ~ctx->dmask;
+               break;
+       case S_IFLNK :
+               stbuf->st_mode = (stbuf->st_mode & S_IFMT) | 0777;
+               break;
+       default :
+               break;
+       }
+}
+
+#endif /* PLUGINS_DISABLED */
+
 static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
 {
        int res = 0;
@@ -696,10 +775,31 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
                        goto exit;
        }
 #endif
+       stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
+
        if (((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
                || (ni->flags & FILE_ATTR_REPARSE_POINT))
            && !stream_name_len) {
                if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+                       const plugin_operations_t *ops;
+                       REPARSE_POINT *reparse;
+
+                       res = CALL_REPARSE_PLUGIN(ni, getattr, stbuf);
+                       if (!res) {
+                               apply_umask(stbuf);
+                               goto ok;
+                       } else {
+                               stbuf->st_size =
+                                       sizeof(ntfs_bad_reparse);
+                               stbuf->st_blocks =
+                                       (ni->allocated_size + 511) >> 9;
+                               stbuf->st_mode = S_IFLNK;
+                               res = 0;
+                               goto ok;
+                       }
+                       goto exit;
+#else /* PLUGINS_DISABLED */
                        char *target;
                        int attr_size;
 
@@ -724,6 +824,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
                                res = -errno;
                                goto exit;
                        }
+#endif /* PLUGINS_DISABLED */
                } else {
                        /* Directory. */
                        stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
@@ -761,7 +862,6 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
                 * 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);
@@ -845,6 +945,9 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
                }
                stbuf->st_mode |= (0777 & ~ctx->fmask);
        }
+#ifndef PLUGINS_DISABLED
+ok:
+#endif /* PLUGINS_DISABLED */
        if (withusermapping) {
                if (ntfs_get_owner_mode(&security,ni,stbuf) < 0)
                        set_fuse_error(&res);
@@ -900,6 +1003,36 @@ exit:
        return res;
 }
 
+#ifndef PLUGINS_DISABLED
+
+/*
+ *             Get the link defined by a junction or symlink
+ *             (internal plugin)
+ */
+
+static int junction_readlink(ntfs_inode *ni,
+                       const REPARSE_POINT *reparse __attribute__((unused)),
+                       char **pbuf)
+{
+       int attr_size;
+       int res;
+
+       errno = 0;
+       res = 0;
+       *pbuf = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
+       if (!*pbuf) {
+               if (errno == EOPNOTSUPP) {
+                       *pbuf = strdup(ntfs_bad_reparse);
+                       if (!*pbuf)
+                               res = -errno;
+               } else
+                       res = -errno;
+       }
+       return (res);
+}
+
+#endif /* PLUGINS_DISABLED */
+
 static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
 {
        char *path = NULL;
@@ -926,6 +1059,21 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
                 * Reparse point : analyze as a junction point
                 */
        if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               char *gotlink;
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               gotlink = (char*)NULL;
+               res = CALL_REPARSE_PLUGIN(ni, readlink, &gotlink);
+               if (gotlink) {
+                       strncpy(buf, gotlink, buf_size);
+                       free(gotlink);
+               } else {
+                       strncpy(buf, ntfs_bad_reparse, buf_size);
+                       res = 0;
+               }
+#else /* PLUGINS_DISABLED */
                char *target;
                int attr_size;
 
@@ -940,6 +1088,7 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
                                strcpy(buf,ntfs_bad_reparse);
                        else
                                res = -errno;
+#endif /* PLUGINS_DISABLED */
                goto exit;
        }
        /* Sanity checks. */
@@ -1163,7 +1312,7 @@ static int ntfs_fuse_open(const char *org_path,
 #endif
 {
        ntfs_inode *ni;
-       ntfs_attr *na;
+       ntfs_attr *na = NULL;
        int res = 0;
        char *path = NULL;
        ntfschar *stream_name;
@@ -1178,50 +1327,66 @@ static int ntfs_fuse_open(const char *org_path,
                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 (!(ni->flags & FILE_ATTR_REPARSE_POINT)) {
+                       na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
+                       if (!na) {
+                               res = -errno;
+                               goto close;
+                       }
+               }
 #if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
-                       if (ntfs_fuse_fill_security_context(&security)) {
-                               if (fi->flags & O_WRONLY)
-                                       accesstype = S_IWRITE;
+               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
-                                       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;
-                       }
+                                       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;
+               if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+                       const plugin_operations_t *ops;
+                       REPARSE_POINT *reparse;
+
+                       fi->fh = 0;
+                       res = CALL_REPARSE_PLUGIN(ni, open, fi);
+#else /* PLUGINS_DISABLED */
+                       res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+                       goto close;
+               }
+               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;
+                       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;
+               /* 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);
+close:
                if (ntfs_inode_close(ni))
                        set_fuse_error(&res);
        } else
@@ -1254,6 +1419,24 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size,
                res = -errno;
                goto exit;
        }
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               if (stream_name_len || !fi) {
+                       res = -EINVAL;
+                       goto exit;
+               }
+               res = CALL_REPARSE_PLUGIN(ni, read, buf, size, offset, fi);
+               if (res >= 0) {
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+               res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+               goto exit;
+       }
        na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
        if (!na) {
                res = -errno;
@@ -1289,8 +1472,11 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size,
                total += ret;
        }
 ok:
-       ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME);
        res = total;
+#ifndef PLUGINS_DISABLED
+stamps:
+#endif /* PLUGINS_DISABLED */
+       ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME);
 exit:
        if (na)
                ntfs_attr_close(na);
@@ -1321,6 +1507,24 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size,
                res = -errno;
                goto exit;
        }
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               if (stream_name_len || !fi) {
+                       res = -EINVAL;
+                       goto exit;
+               }
+               res = CALL_REPARSE_PLUGIN(ni, write, buf, size, offset, fi);
+               if (res >= 0) {
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+               res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+               goto exit;
+       }
        na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
        if (!na) {
                res = -errno;
@@ -1337,15 +1541,18 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size,
                total  += ret;
        }
        res = total;
+#ifndef PLUGINS_DISABLED
+stamps: 
+#endif /* PLUGINS_DISABLED */
        if ((res > 0)
            && (!ctx->dmtime
                || (sle64_to_cpu(ntfs_current_time())
                     - sle64_to_cpu(ni->last_data_change_time)) > ctx->dmtime))
-               ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+               ntfs_fuse_update_times(ni, NTFS_UPDATE_MCTIME);
 exit:
        if (na)
                ntfs_attr_close(na);
-       if (total)
+       if (res > 0)
                set_archive(ni);
        if (ntfs_inode_close(ni))
                set_fuse_error(&res);
@@ -1365,8 +1572,14 @@ static int ntfs_fuse_release(const char *org_path,
        ntfschar *stream_name;
        int stream_name_len, res;
 
+       if (!fi) {
+               res = -EINVAL;
+               goto out;
+       }
+
        /* Only for marked descriptors there is something to do */
-       if (!(fi->fh & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED | CLOSE_DMTIME))) {
+       
+       if (!fi->fh) {
                res = 0;
                goto out;
        }
@@ -1380,20 +1593,42 @@ static int ntfs_fuse_release(const char *org_path,
                res = -errno;
                goto exit;
        }
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               if (stream_name_len) {
+                       res = -EINVAL;
+                       goto exit;
+               }
+               res = CALL_REPARSE_PLUGIN(ni, release, fi);
+               if (!res) {
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+                       /* Assume release() was not needed */
+               res = 0;
+#endif /* PLUGINS_DISABLED */
+               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 */
+#ifndef PLUGINS_DISABLED
+stamps:
+#endif /* PLUGINS_DISABLED */
+       if (fi->fh & CLOSE_DMTIME)
+               ntfs_inode_update_times(ni,NTFS_UPDATE_MCTIME);
 exit:
        if (na)
                ntfs_attr_close(na);
@@ -1440,6 +1675,25 @@ static int ntfs_fuse_trunc(const char *org_path, off_t size,
                goto exit;
        }
 
+       if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+#ifndef PLUGINS_DISABLED
+               const plugin_operations_t *ops;
+               REPARSE_POINT *reparse;
+
+               if (stream_name_len) {
+                       res = -EINVAL;
+                       goto exit;
+               }
+               res = CALL_REPARSE_PLUGIN(ni, truncate, size);
+               if (!res) {
+                       set_archive(ni);
+                       goto stamps;
+               }
+#else /* PLUGINS_DISABLED */
+               res = -EOPNOTSUPP;
+#endif /* PLUGINS_DISABLED */
+               goto exit;
+       }
        na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
        if (!na)
                goto exit;
@@ -1472,8 +1726,11 @@ static int ntfs_fuse_trunc(const char *org_path, off_t size,
                        goto exit;
        if (oldsize != size)
                set_archive(ni);
-       
-       ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
+
+#ifndef PLUGINS_DISABLED       
+stamps:
+#endif /* PLUGINS_DISABLED */
+       ntfs_fuse_update_times(ni, NTFS_UPDATE_MCTIME);
        errno = 0;
 exit:
        res = -errno;
@@ -3352,6 +3609,20 @@ exit:
 #endif
 #endif /* HAVE_SETXATTR */
 
+#ifndef PLUGINS_DISABLED
+static void register_internal_reparse_plugins(void)
+{
+       static const plugin_operations_t ops = {
+               .getattr = junction_getattr,
+               .readlink = junction_readlink,
+       } ;
+       register_reparse_plugin(ctx, IO_REPARSE_TAG_MOUNT_POINT,
+                                       &ops, (void*)NULL);
+       register_reparse_plugin(ctx, IO_REPARSE_TAG_SYMLINK,
+                                       &ops, (void*)NULL);
+}
+#endif /* PLUGINS_DISABLED */
+
 static void ntfs_close(void)
 {
        struct SECURITY_CONTEXT security;
@@ -3968,6 +4239,10 @@ int main(int argc, char *argv[])
                free(ctx->xattrmap_path);
 #endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
 
+#ifndef PLUGINS_DISABLED
+       register_internal_reparse_plugins();
+#endif /* PLUGINS_DISABLED */
+
        fh = mount_fuse(parsed_options);
        if (!fh) {
                err = NTFS_VOLUME_FUSE_ERROR;
@@ -4005,6 +4280,9 @@ err_out:
 #endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
 err2:
        ntfs_close();
+#ifndef PLUGINS_DISABLED
+       close_reparse_plugins(ctx);
+#endif /* PLUGINS_DISABLED */
        free(ctx);
        free(parsed_options);
        free(opts.options);
index 38ccd74..425e810 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g.
  *
- * Copyright (c) 2010-2015 Jean-Pierre Andre
+ * Copyright (c) 2010-2016 Jean-Pierre Andre
  * Copyright (c) 2010      Erik Larsson
  *
  * This program/include file is free software; you can redistribute it and/or
 #include <stdlib.h>
 #endif
 
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
@@ -46,6 +50,8 @@
 #include "inode.h"
 #include "security.h"
 #include "xattrs.h"
+#include "reparse.h"
+#include "plugin.h"
 #include "ntfs-3g_common.h"
 #include "realpath.h"
 #include "misc.h"
@@ -750,3 +756,109 @@ exit :
 }
 
 #endif /* HAVE_SETXATTR */
+
+#ifndef PLUGINS_DISABLED
+
+int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag,
+                               const plugin_operations_t *ops, void *handle)
+{
+       plugin_list_t *plugin;
+       int res;
+
+       res = -1;
+       plugin = (plugin_list_t*)ntfs_malloc(sizeof(plugin_list_t));
+       if (plugin) {
+               plugin->tag = tag;
+               plugin->ops = ops;
+               plugin->handle = handle;
+               plugin->next = ctx->plugins;
+               ctx->plugins = plugin;
+               res = 0;
+       }
+       return (res);
+}
+
+/*
+ *             Get the reparse operations associated to an inode
+ *
+ *     The plugin able to process the reparse point is dynamically loaded
+ *
+ *     When successful, returns the operations vector and the reparse
+ *             data if requested,
+ *     Otherwise returns NULL, with errno set.
+ */
+
+const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
+                               ntfs_inode *ni, REPARSE_POINT **reparse_wanted)
+{
+       const struct plugin_operations *ops;
+       void *handle;
+       REPARSE_POINT *reparse;
+       le32 tag;
+       plugin_list_t *plugin;
+       plugin_init_t pinit;
+
+       ops = (struct plugin_operations*)NULL;
+       reparse = ntfs_get_reparse_point(ni);
+       if (reparse) {
+               tag = reparse->reparse_tag;
+               for (plugin=ctx->plugins; plugin && (plugin->tag != tag);
+                                               plugin = plugin->next) { }
+               if (plugin) {
+                       ops = plugin->ops;
+               } else {
+#ifdef PLUGIN_DIR
+                       char name[sizeof(PLUGIN_DIR) + 64];
+
+                       snprintf(name,sizeof(name), PLUGIN_DIR
+                                       "/ntfs-plugin-%08lx.so",
+                                       (long)le32_to_cpu(tag));
+#else
+                       char name[64];
+
+                       snprintf(name,sizeof(name), "ntfs-plugin-%08lx.so",
+                                       (long)le32_to_cpu(tag));
+#endif
+                       handle = dlopen(name, RTLD_LAZY);
+                       if (handle) {
+                               pinit = (plugin_init_t)dlsym(handle, "init");
+                               if (pinit) {
+                               /* pinit() should set errno if it fails */
+                                       ops = (*pinit)(tag);
+                                       if (ops && register_reparse_plugin(ctx,
+                                                       tag, ops, handle))
+                                               ops = (struct plugin_operations*)NULL;
+                               } else
+                                       errno = ELIBBAD;
+                               if (!ops)
+                                       dlclose(handle);
+                       } else {
+                               if (!(ctx->errors_logged & ERR_PLUGIN))
+                                       ntfs_log_perror(
+                                               "Could not load plugin %s",
+                                               name);
+                               ctx->errors_logged |= ERR_PLUGIN;
+                       }
+               }
+               if (ops && reparse_wanted)
+                       *reparse_wanted = reparse;
+               else
+                       free(reparse);
+       }
+       return (ops);
+}
+
+void close_reparse_plugins(ntfs_fuse_context_t *ctx)
+{
+       while (ctx->plugins) {
+               plugin_list_t *next;
+
+               next = ctx->plugins->next;
+               if (ctx->plugins->handle)
+                       dlclose(ctx->plugins->handle);
+               free(ctx->plugins);
+               ctx->plugins = next;
+       }
+}
+
+#endif /* PLUGINS_DISABLED */
index 6e573a6..2245232 100644 (file)
@@ -110,6 +110,21 @@ typedef enum {
        ATIME_RELATIVE
 } ntfs_atime_t;
 
+typedef enum {
+       ERR_PLUGIN = 1
+} single_log_t;
+
+#ifndef PLUGINS_DISABLED
+
+typedef struct plugin_list {
+       struct plugin_list *next;
+       void *handle;
+       const plugin_operations_t *ops;
+       le32 tag;
+} plugin_list_t;
+
+#endif /* PLUGINS_DISABLED */
+
 typedef struct {
        ntfs_volume *vol;
        unsigned int uid;
@@ -145,8 +160,12 @@ typedef struct {
        struct fuse_chan *fc;
        BOOL inherit;
        unsigned int secure_flags;
+       single_log_t errors_logged;
        char *usermap_path;
        char *abs_mnt_point;
+#ifndef PLUGINS_DISABLED
+       plugin_list_t *plugins;
+#endif /* PLUGINS_DISABLED */
        struct PERMISSIONS_CACHE *seccache;
        struct SECURITY_CONTEXT security;
        struct open_file *open_files; /* only defined in lowntfs-3g */
@@ -182,4 +201,14 @@ int ntfs_parse_options(struct ntfs_options *popts, void (*usage)(void),
 int ntfs_fuse_listxattr_common(ntfs_inode *ni, ntfs_attr_search_ctx *actx,
                        char *list, size_t size, BOOL prefixing);
 
+#ifndef PLUGINS_DISABLED
+
+void close_reparse_plugins(ntfs_fuse_context_t *ctx);
+const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
+                               ntfs_inode *ni, REPARSE_POINT **reparse);
+int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag,
+                                const plugin_operations_t *ops, void *handle);
+
+#endif /* PLUGINS_DISABLED */
+
 #endif /* _NTFS_3G_COMMON_H */