From 4f450a35f55c7985122ad5f1505afda4aacae5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 1 Jun 2018 16:08:33 +0200 Subject: [PATCH] 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. --- include/ntfs-3g/layout.h | 13 ++-- ntfsprogs/ntfsinfo.c | 4 +- src/lowntfs-3g.c | 1 + src/ntfs-3g.c | 1 + src/ntfs-3g_common.c | 145 ++++++++++++++++++++++++++++++++++++--- src/ntfs-3g_common.h | 2 +- 6 files changed, 146 insertions(+), 20 deletions(-) diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index fd6ad2c6..754d66e9 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -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), diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 0eb7bae9..50fbf431 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -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)"; diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 71eceeb4..993867fa 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -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); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index b224e188..6ce89fef 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -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); diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 7f3247df..64b0f52b 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -36,6 +36,10 @@ #include #endif +#ifdef HAVE_FCNTL_H +#include +#endif + #ifdef HAVE_LIMITS_H #include #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 diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index bcffe4f7..33cced36 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -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 */