Made accessing reparse directories through internal plugins

When the bit 28 of a reparse tag is set on a directory, the reparse
information should be ignored and the directory should be accessed
the usual way (this setting is new to Windows 10). In such a situation
access to the directory through an internal plugin rather than through
an external one.
The same policy applies to REPARSE_TAG_WCI which had been defined
earlier without the bit 28 being set.
This commit is contained in:
Jean-Pierre André 2018-06-01 16:08:33 +02:00
parent ad79372024
commit 4f450a35f5
6 changed files with 146 additions and 20 deletions

View File

@ -2403,22 +2403,23 @@ typedef struct {
*
* 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of
* the reparse point.
* 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use.
* 3. The most significant three bits are flags describing the reparse point.
* 2. The 12 bits after this (i.e. bits 16 to 27) are reserved for future use.
* 3. The most significant four bits are flags describing the reparse point.
* They are defined as follows:
* bit 28: Directory bit. If set, the directory is not a surrogate
* and can be used the usual way.
* bit 29: Name surrogate bit. If set, the filename is an alias for
* another object in the system.
* bit 30: High-latency bit. If set, accessing the first byte of data will
* be slow. (E.g. the data is stored on a tape drive.)
* bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User
* defined tags have to use zero here.
* 4. However, on Windows 10 :
* 4. Moreover, on Windows 10 :
* Some flags may be used in bits 12 to 15 to further describe the
* reparse point, and bit 28 may be set, probably to signal the
* presence of these flags.
* reparse point.
*/
typedef enum {
IO_REPARSE_TAG_WITH_FLAGS = const_cpu_to_le32(0x10000000),
IO_REPARSE_TAG_DIRECTORY = const_cpu_to_le32(0x10000000),
IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000),
IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000),
IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000),

View File

@ -413,9 +413,7 @@ static const char *reparse_type_name(le32 tag)
const char *name;
le32 seltag;
seltag = tag;
if (tag & IO_REPARSE_TAG_WITH_FLAGS)
seltag &= IO_REPARSE_PLUGIN_SELECT;
seltag = tag & IO_REPARSE_PLUGIN_SELECT;
switch (seltag) {
case IO_REPARSE_TAG_MOUNT_POINT :
name = " (mount point)";

View File

@ -4568,6 +4568,7 @@ int main(int argc, char *argv[])
#ifndef DISABLE_PLUGINS
register_internal_reparse_plugins();
register_directory_plugins(ctx);
#endif /* DISABLE_PLUGINS */
se = mount_fuse(parsed_options);

View File

@ -4304,6 +4304,7 @@ int main(int argc, char *argv[])
#ifndef DISABLE_PLUGINS
register_internal_reparse_plugins();
register_directory_plugins(ctx);
#endif /* DISABLE_PLUGINS */
fh = mount_fuse(parsed_options);

View File

@ -36,6 +36,10 @@
#include <string.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
@ -766,6 +770,108 @@ exit :
#ifndef DISABLE_PLUGINS
/*
* Get attribute information for reparse directories
*
* Reparse directories have a reparse tag which should be ignored.
*/
static int directory_getattr(ntfs_inode *ni, const REPARSE_POINT *reparse,
struct stat *stbuf)
{
static ntfschar I30[] =
{ const_cpu_to_le16('$'), const_cpu_to_le16('I'),
const_cpu_to_le16('3'), const_cpu_to_le16('0') };
ntfs_attr *na;
int res;
res = -EOPNOTSUPP;
if (ni && reparse && stbuf
&& ((reparse->reparse_tag == IO_REPARSE_TAG_WCI)
|| ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY)
&& !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS)))
&& (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
/* Directory */
stbuf->st_mode = S_IFDIR | 0555;
/* get index size, if not known */
if (!test_nino_flag(ni, KnownSize)) {
na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, I30, 4);
if (na) {
ni->data_size = na->data_size;
ni->allocated_size = na->allocated_size;
set_nino_flag(ni, KnownSize);
ntfs_attr_close(na);
}
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
stbuf->st_nlink = 1; /* Make find(1) work */
res = 0;
}
/* Not a directory, or another error occurred */
return (res);
}
/*
* Open a reparse directory for reading
*
* Currently no reading context is created.
*/
static int directory_opendir(ntfs_inode *ni, const REPARSE_POINT *reparse,
struct fuse_file_info *fi)
{
int res;
res = -EOPNOTSUPP;
if (ni && reparse && fi
&& ((reparse->reparse_tag == IO_REPARSE_TAG_WCI)
|| ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY)
&& !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS)))
&& (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
&& ((fi->flags & O_ACCMODE) == O_RDONLY))
res = 0;
return (res);
}
/*
* Release a reparse directory
*
* Should never be called, as no reading context was defined.
*/
static int directory_release(ntfs_inode *ni __attribute__((unused)),
const REPARSE_POINT *reparse __attribute__((unused)),
struct fuse_file_info *fi __attribute__((unused)))
{
return 0;
}
/*
* Read an open reparse directory
*
* Returns 0 or a negative error code
*/
static int directory_readdir(ntfs_inode *ni, const REPARSE_POINT *reparse,
s64 *pos, void *fillctx, ntfs_filldir_t filldir,
struct fuse_file_info *fi __attribute__((unused)))
{
int res;
res = -EOPNOTSUPP;
if (ni && reparse && pos && fillctx && filldir
&& ((reparse->reparse_tag == IO_REPARSE_TAG_WCI)
|| ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY)
&& !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS)))
&& (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
res = 0;
if (ntfs_readdir(ni, pos, fillctx, filldir))
res = -errno;
}
return (res);
}
int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag,
const plugin_operations_t *ops, void *handle)
{
@ -773,14 +879,16 @@ int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag,
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;
if (ctx) {
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);
}
@ -810,8 +918,8 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx,
if (reparse) {
tag = reparse->reparse_tag;
seltag = tag;
if (tag & IO_REPARSE_TAG_WITH_FLAGS)
seltag &= IO_REPARSE_PLUGIN_SELECT;
if (tag & IO_REPARSE_TAG_DIRECTORY)
seltag &= IO_REPARSE_TAG_DIRECTORY;
for (plugin=ctx->plugins; plugin && (plugin->tag != seltag);
plugin = plugin->next) { }
if (plugin) {
@ -873,6 +981,23 @@ void close_reparse_plugins(ntfs_fuse_context_t *ctx)
}
}
void register_directory_plugins(ntfs_fuse_context_t *ctx)
{
static const struct plugin_operations ops = {
.getattr = directory_getattr,
.release = directory_release,
.opendir = directory_opendir,
.readdir = directory_readdir,
} ;
if (ctx) {
register_reparse_plugin(ctx, IO_REPARSE_TAG_WCI,
&ops, (void*)NULL);
register_reparse_plugin(ctx, IO_REPARSE_TAG_DIRECTORY,
&ops, (void*)NULL);
}
}
#endif /* DISABLE_PLUGINS */
#ifdef HAVE_SETXATTR

View File

@ -211,7 +211,7 @@ 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);
void register_directory_plugins(ntfs_fuse_context_t *ctx);
#endif /* DISABLE_PLUGINS */
#endif /* _NTFS_3G_COMMON_H */