From 1611b2190866dd6ec736209c86c129f5943bb333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 11 Aug 2017 09:42:37 +0200 Subject: [PATCH] Enabled directory operations in plugins Windows 10 brought a new type of reparse point for directories (0x80000018), so add opendir() and readdir() to the plugin interface to take directories into account. The interface for releasedir() is merged with release() as the plugins can discriminate them if needed. --- include/ntfs-3g/plugin.h | 31 ++++++++++++++++-- src/lowntfs-3g.c | 70 +++++++++++++++++++++++++++++++++++++--- src/ntfs-3g.c | 31 ++++++++++++++++-- 3 files changed, 122 insertions(+), 10 deletions(-) diff --git a/include/ntfs-3g/plugin.h b/include/ntfs-3g/plugin.h index 7e7b2a7c..a9d56a5f 100644 --- a/include/ntfs-3g/plugin.h +++ b/include/ntfs-3g/plugin.h @@ -30,8 +30,9 @@ #ifndef _NTFS_PLUGIN_H #define _NTFS_PLUGIN_H -#include "inode.h" #include "layout.h" +#include "inode.h" +#include "dir.h" struct fuse_file_info; struct stat; @@ -71,10 +72,10 @@ typedef struct plugin_operations { struct fuse_file_info *fi); /* - * Release an open file + * Release an open file or directory * 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. + * specific to the open file or directory * The returned value is zero for success or a negative errno * value for failure. */ @@ -126,6 +127,30 @@ typedef struct plugin_operations { */ int (*truncate)(ntfs_inode *ni, const REPARSE_POINT *reparse, off_t size); + /* + * Open a directory + * 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 readdir(). When used + * this field must be non-null and freed in release(). + * The returned value is zero for success and a negative errno + * value for failure. + */ + int (*opendir)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct fuse_file_info *fi); + + /* + * Get entries from a directory + * + * Use the filldir() function with fillctx argument to return + * the directory entries. + * Names "." and ".." are expected to be returned. + * The returned value is zero for success and a negative errno + * value for failure. + */ + int (*readdir)(ntfs_inode *ni, const REPARSE_POINT *reparse, + s64 *pos, void *fillctx, ntfs_filldir_t filldir, + struct fuse_file_info *fi); } plugin_operations_t; diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 748ba518..4bd211f6 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -203,6 +203,9 @@ typedef struct fill_item { typedef struct fill_context { struct fill_item *first; struct fill_item *last; +#ifndef DISABLE_PLUGINS + u64 fh; +#endif /* DISABLE_PLUGINS */ off_t off; fuse_req_t req; fuse_ino_t ino; @@ -1292,6 +1295,17 @@ static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino, if (!ntfs_allowed_access(&security,ni,accesstype)) res = -EACCES; } + if (ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + fi->fh = 0; + res = CALL_REPARSE_PLUGIN(ni, opendir, fi); +#else /* DISABLE_PLUGINS */ + res = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } if (ntfs_inode_close(ni)) set_fuse_error(&res); if (!res) { @@ -1305,6 +1319,9 @@ static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino, fill->filled = FALSE; fill->ino = ino; fill->off = 0; +#ifndef DISABLE_PLUGINS + fill->fh = fi->fh; +#endif /* DISABLE_PLUGINS */ } fi->fh = (long)fill; } @@ -1321,9 +1338,15 @@ static void ntfs_fuse_releasedir(fuse_req_t req, fuse_ino_t ino __attribute__((unused)), struct fuse_file_info *fi) { +#ifndef DISABLE_PLUGINS + struct fuse_file_info ufi; + ntfs_inode *ni; +#endif /* DISABLE_PLUGINS */ ntfs_fuse_fill_context_t *fill; ntfs_fuse_fill_item_t *current; + int res; + res = 0; fill = (ntfs_fuse_fill_context_t*)(long)fi->fh; if (fill && (fill->ino == ino)) { /* make sure to clear results */ @@ -1333,16 +1356,38 @@ static void ntfs_fuse_releasedir(fuse_req_t req, free(fill->first); fill->first = current; } +#ifndef DISABLE_PLUGINS + if (fill->fh) { + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (ni) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + memcpy(&ufi, fi, sizeof(ufi)); + ufi.fh = fill->fh; + res = CALL_REPARSE_PLUGIN(ni, release, + &ufi); + } + if (ntfs_inode_close(ni) && !res) + res = -errno; + } else + res = -errno; + } +#endif /* DISABLE_PLUGINS */ fill->ino = 0; free(fill); } - fuse_reply_err(req, 0); + fuse_reply_err(req, -res); } static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off __attribute__((unused)), struct fuse_file_info *fi __attribute__((unused))) { +#ifndef DISABLE_PLUGINS + struct fuse_file_info ufi; +#endif /* DISABLE_PLUGINS */ ntfs_fuse_fill_item_t *first; ntfs_fuse_fill_item_t *current; ntfs_fuse_fill_context_t *fill; @@ -1379,10 +1424,27 @@ static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, if (!ni) err = -errno; else { - if (ntfs_readdir(ni, &pos, fill, - (ntfs_filldir_t) + if (ni->flags + & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + memcpy(&ufi, fi, sizeof(ufi)); + ufi.fh = fill->fh; + err = CALL_REPARSE_PLUGIN(ni, + readdir, &pos, fill, + (ntfs_filldir_t) + ntfs_fuse_filler, &ufi); +#else /* DISABLE_PLUGINS */ + err = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else { + if (ntfs_readdir(ni, &pos, fill, + (ntfs_filldir_t) ntfs_fuse_filler)) - err = -errno; + err = -errno; + } fill->filled = TRUE; ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index e73eee33..9558d50b 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1300,6 +1300,17 @@ static int ntfs_fuse_opendir(const char *path, ni,accesstype)) res = -EACCES; } + if (ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + fi->fh = 0; + res = CALL_REPARSE_PLUGIN(ni, opendir, fi); +#else /* DISABLE_PLUGINS */ + res = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } if (ntfs_inode_close(ni)) set_fuse_error(&res); } else @@ -1323,9 +1334,22 @@ static int ntfs_fuse_readdir(const char *path, void *buf, ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; - if (ntfs_readdir(ni, &pos, &fill_ctx, - (ntfs_filldir_t)ntfs_fuse_filler)) - err = -errno; + + if (ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + err = CALL_REPARSE_PLUGIN(ni, readdir, &pos, &fill_ctx, + (ntfs_filldir_t)ntfs_fuse_filler, fi); +#else /* DISABLE_PLUGINS */ + err = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else { + if (ntfs_readdir(ni, &pos, &fill_ctx, + (ntfs_filldir_t)ntfs_fuse_filler)) + err = -errno; + } ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); if (ntfs_inode_close(ni)) set_fuse_error(&err); @@ -3732,6 +3756,7 @@ static struct fuse_operations ntfs_3g_ops = { #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) .access = ntfs_fuse_access, .opendir = ntfs_fuse_opendir, + .releasedir = ntfs_fuse_release, #endif #ifdef HAVE_SETXATTR .getxattr = ntfs_fuse_getxattr,