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/
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)
)
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])],
,
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"
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
mst.h \
ntfstime.h \
object_id.h \
- param.h \
+ param.h \
+ plugin.h \
realpath.h \
reparse.h \
runlist.h \
--- /dev/null
+/*
+ * 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 */
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);
*
* 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
}
#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);
+}
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 \
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
$(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
$(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
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
* 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,
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 {
#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)
{
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;
res = -errno;
goto exit;
}
+#endif /* PLUGINS_DISABLED */
} else {
/* Directory. */
stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
* 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_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);
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;
* 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;
if (!buf)
res = -errno;
}
- goto exit;
+#endif /* PLUGINS_DISABLED */
+ goto exit;
}
/* Sanity checks. */
if (!(ni->flags & FILE_ATTR_SYSTEM)) {
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;
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)
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;
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);
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;
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);
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
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.
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:
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;
}
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);
#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;
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;
#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);
* 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
+/*
+ * 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,
enum {
CLOSE_COMPRESSED = 1,
CLOSE_ENCRYPTED = 2,
- CLOSE_DMTIME = 4
+ CLOSE_DMTIME = 4,
+ CLOSE_REPARSE = 8
};
static struct ntfs_options opts;
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;
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;
res = -errno;
goto exit;
}
+#endif /* PLUGINS_DISABLED */
} else {
/* Directory. */
stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
* 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);
}
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);
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;
* 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;
strcpy(buf,ntfs_bad_reparse);
else
res = -errno;
+#endif /* PLUGINS_DISABLED */
goto exit;
}
/* Sanity checks. */
#endif
{
ntfs_inode *ni;
- ntfs_attr *na;
+ ntfs_attr *na = NULL;
int res = 0;
char *path = NULL;
ntfschar *stream_name;
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
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;
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);
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;
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);
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;
}
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);
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;
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;
#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;
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;
#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);
/**
* 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
#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"
}
#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 */
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;
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 */
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 */