Used plugins to process reparse points

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".
pull/2/head
Jean-Pierre André 2016-04-06 09:10:35 +02:00
parent 8e550e712d
commit da33b0328f
11 changed files with 988 additions and 115 deletions

7
README
View File

@ -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)

View File

@ -121,6 +121,14 @@ AC_ARG_ENABLE(
[enable_xattr_mappings="no"]
)
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

View 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 \

View File

@ -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 */

View 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);

View 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);
}

View 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

View 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.
@ -105,6 +105,7 @@
#include "xattrs.h"
#include "misc.h"
#include "ioctl.h"
#include "plugin.h"
#include "ntfs-3g_common.h"
@ -157,6 +158,22 @@
#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;
#ifdef HAVE_SETXATTR /* extended attributes interface required */
/* mark a future need to fixup encrypted inode */
if (ctx->efs_raw
&& !(na->data_flags & ATTR_IS_ENCRYPTED)
&& (ni->flags & FILE_ATTR_ENCRYPTED))
state |= CLOSE_ENCRYPTED;
#endif /* HAVE_SETXATTR */
/* mark a future need to update the mtime */
if (ctx->dmtime)
state |= CLOSE_DMTIME;
/* deny opening metadata files for writing */
if (ino < FILE_first_user)
res = -EPERM;
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;
}
ntfs_attr_close(na);
} else
res = -errno;
#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;
#endif /* HAVE_SETXATTR */
/* mark a future need to update the mtime */
if (ctx->dmtime)
state |= CLOSE_DMTIME;
/* deny opening metadata files for writing */
if (ino < FILE_first_user)
res = -EPERM;
}
ntfs_attr_close(na);
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);

View 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"
@ -129,6 +130,22 @@
#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 !KERNELPERMS | (POSIXACLS & !KERNELACLS)
if (ntfs_fuse_fill_security_context(&security)) {
if (fi->flags & O_WRONLY)
accesstype = S_IWRITE;
else
if (fi->flags & O_RDWR)
accesstype = S_IWRITE | S_IREAD;
else
accesstype = S_IREAD;
/*
* directory must be searchable
* and requested access allowed
*/
if (!ntfs_allowed_dir_access(&security,
path,(ntfs_inode*)NULL,ni,S_IEXEC)
|| !ntfs_allowed_access(&security,
ni,accesstype))
res = -EACCES;
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;
else
if (fi->flags & O_RDWR)
accesstype = S_IWRITE | S_IREAD;
else
accesstype = S_IREAD;
/*
* directory must be searchable
* and requested access allowed
*/
if (!ntfs_allowed_dir_access(&security,
path,(ntfs_inode*)NULL,ni,S_IEXEC)
|| !ntfs_allowed_access(&security,
ni,accesstype))
res = -EACCES;
}
#endif
if ((res >= 0)
&& (fi->flags & (O_WRONLY | O_RDWR))) {
/* mark a future need to compress the last chunk */
if (na->data_flags & ATTR_COMPRESSION_MASK)
fi->fh |= CLOSE_COMPRESSED;
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);

View 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
@ -28,6 +28,10 @@
#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 */

View 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 */