diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index a99c1ae0..e6a2c3b1 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -109,6 +109,7 @@ extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, void *dirent, ntfs_filldir_t filldir); ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); +u32 ntfs_interix_types(ntfs_inode *ni); int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, char *value, size_t size); diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index b6447ff4..e4d48ed0 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -875,7 +875,7 @@ typedef enum { * and most metadata files have such similar patters. */ -static u32 ntfs_interix_types(ntfs_inode *ni) +u32 ntfs_interix_types(ntfs_inode *ni) { ntfs_attr *na; u32 dt_type; @@ -884,8 +884,14 @@ static u32 ntfs_interix_types(ntfs_inode *ni) dt_type = NTFS_DT_UNKNOWN; na = ntfs_attr_open(ni, AT_DATA, NULL, 0); if (na) { - /* Unrecognized patterns (eg HID + SYST) are plain files */ - dt_type = NTFS_DT_REG; + /* + * Unrecognized patterns (eg HID + SYST for metadata) + * are plain files or directories + */ + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + dt_type = NTFS_DT_DIR; + else + dt_type = NTFS_DT_REG; if (na->data_size <= 1) { if (!(ni->flags & FILE_ATTR_HIDDEN)) dt_type = (na->data_size ? diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 683ac0c6..80c224f8 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -3165,7 +3165,7 @@ static void ntfs_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) goto out; } /* Return with no result for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) + if (!user_xattrs_allowed(ctx, ni)) goto exit; /* otherwise file must be readable */ #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) @@ -3314,7 +3314,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, goto out; } /* Return with no result for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + if (!user_xattrs_allowed(ctx, ni)) { res = -ENODATA; goto exit; } @@ -3511,7 +3511,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, break; default : /* User xattr not allowed for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + if (!user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } @@ -3524,7 +3524,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, #else /* User xattr not allowed for symlinks, fifo, etc. */ if ((namespace == XATTRNS_USER) - && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + && !user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } @@ -3756,7 +3756,7 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na break; default : /* User xattr not allowed for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + if (!user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } @@ -3769,7 +3769,7 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na #else /* User xattr not allowed for symlinks, fifo, etc. */ if ((namespace == XATTRNS_USER) - && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + && !user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 5b19b1dc..4b4ec363 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2946,7 +2946,7 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size) if (!ni) return -errno; /* Return with no result for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) + if (!user_xattrs_allowed(ctx, ni)) goto exit; /* otherwise file must be readable */ #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) @@ -3148,7 +3148,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, if (!ni) return -errno; /* Return with no result for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + if (!user_xattrs_allowed(ctx, ni)) { res = -ENODATA; goto exit; } @@ -3324,7 +3324,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, break; default : /* User xattr not allowed for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + if (!user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } @@ -3337,7 +3337,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, #else /* User xattr not allowed for symlinks, fifo, etc. */ if ((namespace == XATTRNS_USER) - && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + && !user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } @@ -3561,7 +3561,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) break; default : /* User xattr not allowed for symlinks, fifo, etc. */ - if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + if (!user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } @@ -3574,7 +3574,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) #else /* User xattr not allowed for symlinks, fifo, etc. */ if ((namespace == XATTRNS_USER) - && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + && !user_xattrs_allowed(ctx, ni)) { res = -EPERM; goto exit; } diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 425e8102..29fa42c0 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -48,6 +48,7 @@ #include #include "inode.h" +#include "dir.h" #include "security.h" #include "xattrs.h" #include "reparse.h" @@ -862,3 +863,60 @@ void close_reparse_plugins(ntfs_fuse_context_t *ctx) } #endif /* PLUGINS_DISABLED */ + +#ifdef HAVE_SETXATTR + +/* + * Check whether a user xattr is allowed + * + * The inode must be a plain file or a directory. The only allowed + * metadata file is the root directory (useful for MacOSX and hopefully + * does not harm Windows). + */ + +BOOL user_xattrs_allowed(ntfs_fuse_context_t *ctx, ntfs_inode *ni) +{ + u32 dt_type; + BOOL res; + + /* Quick return for common cases and root */ + if (!(ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) + || (ni->mft_no == FILE_root)) + res = TRUE; + else { + /* Reparse point depends on kind, see plugin */ + if (ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef PLUGINS_DISABLED + struct stat stbuf; + REPARSE_POINT *reparse; + const plugin_operations_t *ops; + + res = FALSE; /* default for error cases */ + ops = select_reparse_plugin(ctx, ni, &reparse); + if (ops) { + if (ops->getattr + && !ops->getattr(ni,reparse,&stbuf)) { + res = S_ISREG(stbuf.st_mode) + || S_ISDIR(stbuf.st_mode); + } + free(reparse); +#else /* PLUGINS_DISABLED */ + res = FALSE; /* mountpoints, symlinks, ... */ +#endif /* PLUGINS_DISABLED */ + } + } else { + /* Metadata */ + if (ni->mft_no < FILE_first_user) + res = FALSE; + else { + /* Interix types */ + dt_type = ntfs_interix_types(ni); + res = (dt_type == NTFS_DT_REG) + || (dt_type == NTFS_DT_DIR); + } + } + } + return (res); +} + +#endif /* HAVE_SETXATTR */ diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index 22452321..e9026576 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -208,6 +208,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); +BOOL user_xattrs_allowed(ntfs_fuse_context_t *ctx, ntfs_inode *ni); #endif /* PLUGINS_DISABLED */