From ca6466c6bbc38861c843bd3a2559d7a24e1796b0 Mon Sep 17 00:00:00 2001 From: jpandre Date: Mon, 5 Jan 2009 13:56:05 +0000 Subject: [PATCH] Mapped a few NTFS internal data to extended attributes --- include/ntfs-3g/attrib.h | 6 + include/ntfs-3g/reparse.h | 6 + include/ntfs-3g/security.h | 8 +- libntfs-3g/attrib.c | 61 +++++++++ libntfs-3g/reparse.c | 163 ++++++++++++++++++++++++ libntfs-3g/security.c | 98 ++++++++------- src/ntfs-3g.c | 247 ++++++++++++++++++++++++++++++------- 7 files changed, 495 insertions(+), 94 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index 7425d12c..57de046e 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -345,5 +345,11 @@ extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); +int ntfs_get_ntfs_attrib(const char *path, + char *value, size_t size, ntfs_inode *ni); +int ntfs_set_ntfs_attrib(const char *path, + const char *value, size_t size, int flags, + ntfs_inode *ni); + #endif /* defined _NTFS_ATTRIB_H */ diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 21de4077..6aa45c26 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -28,4 +28,10 @@ char *ntfs_make_symlink(const char *org_path, ntfs_inode *ni, int *pattr_size); BOOL ntfs_possible_symlink(ntfs_inode *ni); +int ntfs_get_ntfs_reparse_data(const char *path, + char *value, size_t size, ntfs_inode *ni); +int ntfs_set_ntfs_reparse_data(const char *path, const char *value, + size_t size, int flags, ntfs_inode *ni); +int ntfs_remove_ntfs_reparse_data(const char *path, ntfs_inode *ni); + #endif /* REPARSE_H */ diff --git a/include/ntfs-3g/security.h b/include/ntfs-3g/security.h index fe31d57a..1d2e449a 100644 --- a/include/ntfs-3g/security.h +++ b/include/ntfs-3g/security.h @@ -30,6 +30,10 @@ #include "inode.h" #include "dir.h" +#ifndef POSIXACLS +#define POSIXACLS 0 +#endif + #if __BYTE_ORDER == __LITTLE_ENDIAN #define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) #define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ @@ -277,7 +281,7 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, ntfs_inode *ni); int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, const char *name, const char *value, size_t size, - ntfs_inode *ni); + int flags, ntfs_inode *ni); int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, const char *name, ntfs_inode *ni); #endif @@ -287,7 +291,7 @@ int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, ntfs_inode *ni); int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, const char *name, const char *value, size_t size, - ntfs_inode *ni); + int flags, ntfs_inode *ni); /* * Security API for direct access to security descriptors diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 7a2074dd..585e10b8 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -39,6 +39,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_SETXATTR +#include +#endif #include "compat.h" #include "attrib.h" @@ -5137,3 +5140,61 @@ s64 ntfs_attr_get_free_bits(ntfs_attr *na) return nr_free; } +/* + * Get the ntfs attribute into an extended attribute + * The attribute is returned according to cpu endianness + */ + +int ntfs_get_ntfs_attrib(const char *path __attribute__((unused)), + char *value, size_t size, ntfs_inode *ni) +{ + u32 attrib; + size_t outsize; + + outsize = 0; /* default to no data and no error */ + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + outsize = sizeof(FILE_ATTR_FLAGS); + if (size >= outsize) { + if (value) + memcpy(value,&attrib,outsize); + else + errno = EINVAL; + } + } + return (outsize ? (int)outsize : -errno); +} + +/* + * Return the ntfs attribute into an extended attribute + * The attribute is expected according to cpu endianness + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)), + const char *value, size_t size, int flags, + ntfs_inode *ni) +{ + u32 attrib; + int res; + + res = -1; + if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) { + if (!(flags & XATTR_CREATE)) { + /* copy to avoid alignment problems */ + memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS)); + ni->flags = (ni->flags & ~const_cpu_to_le32(0x31a7)) + | cpu_to_le32(attrib & 0x31a7); + NInoSetDirty(ni); + res = 0; + } else + errno = EEXIST; + } else + errno = EINVAL; + return (res ? -1 : 0); +} diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index cc4fdc60..75ffbf6a 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -39,6 +39,10 @@ #include #endif +#ifdef HAVE_SETXATTR +#include +#endif + #ifdef HAVE_SYS_SYSMACROS_H #include #endif @@ -112,6 +116,23 @@ static const ntfschar vol_junction_head[] = { const_cpu_to_le16('{'), } ; +static ntfschar reparse_point_name[] = { + const_cpu_to_le16('$'), + const_cpu_to_le16('R'), + const_cpu_to_le16('E'), + const_cpu_to_le16('P'), + const_cpu_to_le16('A'), + const_cpu_to_le16('R'), + const_cpu_to_le16('S'), + const_cpu_to_le16('E'), + const_cpu_to_le16('_'), + const_cpu_to_le16('P'), + const_cpu_to_le16('O'), + const_cpu_to_le16('I'), + const_cpu_to_le16('N'), + const_cpu_to_le16('T'), +} ; + static const char mappingdir[] = ".NTFS-3G/"; /* @@ -891,3 +912,145 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni) } return (possible); } + +/* + * Get the ntfs reparse data into an extended attribute + * + * Returns the reparse data size + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_reparse_data(const char *path __attribute__((unused)), + char *value, size_t size, ntfs_inode *ni) +{ + REPARSE_POINT *reparse_attr; + s64 attr_size; + + attr_size = 0; /* default to no data and no error */ + if (ni) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,reparse_attr, + attr_size); + else + errno = EINVAL; + } + free(reparse_attr); + } + } else + errno = ENODATA; + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Set the reparse data from an extended attribute + * + * Warning : the new data is not checked + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), + const char *value, size_t size, int flags, + ntfs_inode *ni) +{ + int res; + int written; + ntfs_attr *na; + + res = 0; + if (ni && (value || !size)) { + if (!ntfs_attr_exist(ni,AT_REPARSE_POINT,(ntfschar*)NULL,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no reparse data attribute : add one, + * apparently, this does not feed the new value in + */ + res = ntfs_attr_add(ni,AT_REPARSE_POINT, + reparse_point_name,14,(u8*)NULL,(s64)size); + if (!res) + ni->flags |= FILE_ATTR_REPARSE_POINT; + NInoSetDirty(ni); + } else { + errno = ENODATA; + res = -1; + } + } else { + if (flags & XATTR_CREATE) { + errno = EEXIST; + res = -1; + } + } + if (!res) { + /* + * open and update the existing reparse data + */ + na = ntfs_attr_open(ni, AT_REPARSE_POINT, + reparse_point_name, 14); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to update " + "reparse data\n"); + errno = EIO; + res = -1; + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the reparse data + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), + ntfs_inode *ni) +{ + int res; + ntfs_attr *na; + + res = 0; + if (ni) { + /* + * open and delete the reparse data + */ + na = ntfs_attr_open(ni, AT_REPARSE_POINT, + reparse_point_name, 14); + if (na) { + /* remove attribute */ + res = ntfs_attr_rm(na); + if (!res) + ni->flags &= ~FILE_ATTR_REPARSE_POINT; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index 70801a01..f9f8cbb6 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -41,6 +41,10 @@ #ifdef HAVE_FCNTL_H #include #endif +#ifdef HAVE_SETXATTR +#include +#endif + #include #include #include @@ -2687,8 +2691,6 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, return (res); } -#if POSIXACLS - /* * Check whether user has ownership rights on a file * @@ -2741,6 +2743,8 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, return (allowed); } +#if POSIXACLS + /* * Set a new access or default Posix ACL to a file * (or remove ACL if no input data) @@ -2751,7 +2755,7 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, const char *name, const char *value, size_t size, - ntfs_inode *ni) + int flags, ntfs_inode *ni) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -2765,6 +2769,7 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, mode_t mode; BOOL isdir; BOOL deflt; + BOOL exist; int count; struct POSIX_SECURITY *oldpxdesc; struct POSIX_SECURITY *newpxdesc; @@ -2804,9 +2809,18 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, oldpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, usid, gsid, ni); if (oldpxdesc) { - mode = oldpxdesc->mode; - newpxdesc = ntfs_replace_acl(oldpxdesc, + if (deflt) + exist = oldpxdesc->defcnt > 0; + else + exist = oldpxdesc->acccnt > 3; + if ((exist && (flags & XATTR_CREATE)) + || (!exist && (flags & XATTR_REPLACE))) { + errno = (exist ? EEXIST : ENODATA); + } else { + mode = oldpxdesc->mode; + newpxdesc = ntfs_replace_acl(oldpxdesc, (const struct POSIX_ACL*)value,count,deflt); + } free(oldpxdesc); } free(oldattr); @@ -2846,7 +2860,7 @@ int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, const char *name, ntfs_inode *ni) { return (ntfs_set_posix_acl(scx, path, name, - (const char*)NULL, 0, ni)); + (const char*)NULL, 0, 0, ni)); } #endif @@ -2860,55 +2874,51 @@ int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path __attribute__((unused)), const char *name __attribute__((unused)), - const char *value, size_t size, ntfs_inode *ni) + const char *value, size_t size, int flags, + ntfs_inode *ni) { char *attr; int res; res = -1; if ((size > 0) + && !(flags & XATTR_CREATE) && ntfs_valid_descr(value,size) && (ntfs_attr_size(value) == size)) { -#if POSIXACLS - if (ntfs_allowed_as_owner(scx,path,ni)) { -#else - { /* relying on fuse for access control */ -#endif - /* need copying in order to write */ - attr = (char*)ntfs_malloc(size); - if (attr) { - memcpy(attr,value,size); - res = update_secur_descr(scx->vol, attr, ni); - /* - * No need to invalidate standard caches : - * the relation between a securid and - * the associated protection is unchanged, - * only the relation between a file and - * its securid and protection is changed. - */ + /* need copying in order to write */ + attr = (char*)ntfs_malloc(size); + if (attr) { + memcpy(attr,value,size); + res = update_secur_descr(scx->vol, attr, ni); + /* + * No need to invalidate standard caches : + * the relation between a securid and + * the associated protection is unchanged, + * only the relation between a file and + * its securid and protection is changed. + */ #if CACHE_LEGACY_SIZE - /* - * we must however invalidate the legacy - * cache, which is based on inode numbers. - * For safety, invalidate even if updating - * failed. - */ - if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - && !ni->security_id) { - struct CACHED_PERMISSIONS_LEGACY legacy; + /* + * we must however invalidate the legacy + * cache, which is based on inode numbers. + * For safety, invalidate even if updating + * failed. + */ + if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; - legacy.mft_no = ni->mft_no; - legacy.variable = (char*)NULL; - legacy.varsize = 0; - ntfs_invalidate_cache(scx->vol->legacy_cache, - GENERIC(&legacy), - (cache_compare)leg_compare); - } + legacy.mft_no = ni->mft_no; + legacy.variable = (char*)NULL; + legacy.varsize = 0; + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare); + } #endif - free(attr); - } else - errno = ENOMEM; - } + free(attr); + } else + errno = ENOMEM; } else errno = EINVAL; return (res ? -1 : 0); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 35738158..dd51d3e4 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1754,9 +1754,17 @@ close_inode: #ifdef HAVE_SETXATTR +enum { XATTR_UNMAPPED, + XATTR_NTFS_ACL, + XATTR_NTFS_ATTRIB, + XATTR_NTFS_REPARSE_DATA, + XATTR_POSIX_ACC, XATTR_POSIX_DEF } ; + static const char nf_ns_xattr_preffix[] = "user."; static const int nf_ns_xattr_preffix_len = 5; static const char nf_ns_xattr_ntfs[] = "system.ntfs_acl"; +static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; +static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; #if POSIXACLS @@ -1771,8 +1779,8 @@ static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; * NULL and errno set if not allowed */ -static ntfs_inode *ntfs_check_access_xattr(const char *path, - struct SECURITY_CONTEXT *security) +static ntfs_inode *ntfs_check_access_xattr(struct SECURITY_CONTEXT *security, + const char *path) { ntfs_inode *ni; @@ -1801,6 +1809,32 @@ static ntfs_inode *ntfs_check_access_xattr(const char *path, #endif +static int mapped_xattr(const char *name) +{ + int num; + + + if (!strcmp(name,nf_ns_xattr_ntfs)) + num = XATTR_NTFS_ACL; + else + if (!strcmp(name,nf_ns_xattr_attrib)) + num = XATTR_NTFS_ATTRIB; + else + if (!strcmp(name,nf_ns_xattr_reparse)) + num = XATTR_NTFS_REPARSE_DATA; + else +#if POSIXACLS + if (!strcmp(name,nf_ns_xattr_posix_access)) + num = XATTR_POSIX_ACC; + else + if (!strcmp(name,nf_ns_xattr_posix_default)) + num = XATTR_POSIX_DEF; + else +#endif + num = XATTR_UNMAPPED; + return (num); +} + static int ntfs_fuse_listxattr(const char *path, char *list, size_t size) { ntfs_attr_search_ctx *actx = NULL; @@ -1950,19 +1984,18 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, ntfs_attr *na = NULL; ntfschar *lename = NULL; int res, lename_len; + int attr; struct SECURITY_CONTEXT security; + attr = mapped_xattr(name); + if (attr != XATTR_UNMAPPED) { #if POSIXACLS /* * hijack Posix/NTFS ACL retrieval, whatever mode * was selected for xattr (from the user's point * of view, ACLs are not xattr) */ - if ((!strcmp(name,nf_ns_xattr_posix_access) - || !strcmp(name,nf_ns_xattr_posix_default) - || !strcmp(name,nf_ns_xattr_ntfs))) { - - ni = ntfs_check_access_xattr(path,&security); + ni = ntfs_check_access_xattr(&security,path); if (ni) { /* * the returned value is the needed @@ -1970,25 +2003,37 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, * is done, and the caller has to * issue a new call with correct size. */ - if (!strcmp(name,nf_ns_xattr_ntfs)) + switch (attr) { + case XATTR_NTFS_ACL : res = ntfs_get_ntfs_acl(&security,path, name,value,size,ni); - else + break; + case XATTR_POSIX_ACC : + case XATTR_POSIX_DEF : res = ntfs_get_posix_acl(&security,path, name,value,size,ni); + break; + case XATTR_NTFS_ATTRIB : + res = ntfs_get_ntfs_attrib(path, + value,size,ni); + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_get_ntfs_reparse_data(path, + value,size,ni); + break; + default : /* not possible */ + break; + } if (ntfs_inode_close(ni)) set_fuse_error(&res); } else res = -errno; - return (res); - } #else /* * Only hijack NTFS ACL retrieval if POSIX ACLS * option is not selected * Access control is done by fuse */ - if (!strcmp(name,nf_ns_xattr_ntfs)) { if (ntfs_fuse_is_named_data_stream(path)) res = -EINVAL; /* n/a for named data streams. */ else { @@ -2002,16 +2047,30 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, * is done, and the caller has to * issue a new call with correct size. */ - res = ntfs_get_ntfs_acl(&security,path, + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_get_ntfs_acl(&security,path, name,value,size,ni); + break; + case XATTR_NTFS_ATTRIB : + res = ntfs_get_ntfs_attrib(path, + value,size,ni); + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_get_ntfs_reparse_data(path, + value,size,ni); + break; + default : /* not possible */ + break; + } if (ntfs_inode_close(ni)) set_fuse_error(&res); } else res = -errno; } +#endif return (res); } -#endif if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) return ntfs_fuse_getxattr_windows(path, name, value, size); if (ctx->streams != NF_STREAMS_INTERFACE_XATTR) @@ -2057,8 +2116,11 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, ntfs_attr *na = NULL; ntfschar *lename = NULL; int res, lename_len; + int attr; struct SECURITY_CONTEXT security; + attr = mapped_xattr(name); + if (attr != XATTR_UNMAPPED) { #if POSIXACLS /* * hijack Posix/NTFS ACL retrieval, whatever mode @@ -2066,50 +2128,83 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, * of view, ACLs are not xattr) * Note : updating an ACL does not set ctime */ - if (!strcmp(name,nf_ns_xattr_posix_access) - || !strcmp(name,nf_ns_xattr_posix_default) - || !strcmp(name,nf_ns_xattr_ntfs)) { - - ni = ntfs_check_access_xattr(path,&security); + ni = ntfs_check_access_xattr(&security,path); if (ni) { - if (!strcmp(name,nf_ns_xattr_ntfs)) - res = ntfs_set_ntfs_acl(&security,path, - name,value,size,ni); - else - res = ntfs_set_posix_acl(&security,path, - name,value,size,ni); - if (res) + if (ntfs_allowed_as_owner(&security,path,ni)) { + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_set_ntfs_acl(&security,path, + name,value,size,flags,ni); + break; + case XATTR_POSIX_ACC : + case XATTR_POSIX_DEF : + res = ntfs_set_posix_acl(&security,path, + name,value,size,flags,ni); + break; + case XATTR_NTFS_ATTRIB : + res = ntfs_set_ntfs_attrib(path, + value,size,flags,ni); + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_set_ntfs_reparse_data(path, + value,size,flags,ni); + break; + default : /* not possible */ + break; + } + if (res) + res = -errno; + } else res = -errno; if (ntfs_inode_close(ni)) set_fuse_error(&res); } else res = -errno; - return (res); - } #else /* * Only hijack NTFS ACL setting if POSIX ACLS * option is not selected - * Access control is done by fuse + * Access control is partially done by fuse */ - if (!strcmp(name,nf_ns_xattr_ntfs)) { if (ntfs_fuse_is_named_data_stream(path)) res = -EINVAL; /* n/a for named data streams. */ else { ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (ni) { - /* user mapping not mandatory */ - ntfs_fuse_fill_security_context(&security); - res = ntfs_set_ntfs_acl(&security,path, - name,value,size,ni); + /* + * user mapping is not mandatory + * if defined, only owner is allowed + */ + if (!ntfs_fuse_fill_security_context(&security) + || ntfs_allowed_as_owner(&security,path,ni)) { + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_set_ntfs_acl(&security,path, + name,value,size,flags,ni); + break; + case XATTR_NTFS_ATTRIB : + res = ntfs_set_ntfs_attrib(path, + value,size,flags,ni); + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_set_ntfs_reparse_data(path, + value,size,flags,ni); + break; + default : /* not possible */ + break; + } + if (res) + res = -errno; + } else + res = -errno; if (ntfs_inode_close(ni)) set_fuse_error(&res); } else res = -errno; } +#endif return (res); } -#endif if (ctx->streams != NF_STREAMS_INTERFACE_XATTR) return -EOPNOTSUPP; if (strncmp(name, nf_ns_xattr_preffix, nf_ns_xattr_preffix_len) || @@ -2176,39 +2271,95 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) ntfs_inode *ni; ntfschar *lename = NULL; int res = 0, lename_len; - - -#if POSIXACLS + int attr; struct SECURITY_CONTEXT security; + attr = mapped_xattr(name); + if (attr != XATTR_UNMAPPED) { +#if POSIXACLS + /* * hijack Posix/NTFS ACL retrieval, whatever mode * was selected for xattr (from the user's point * of view, ACLs are not xattr) * Note : updating an ACL does not set ctime */ - if (!strcmp(name,nf_ns_xattr_posix_access) - || !strcmp(name,nf_ns_xattr_posix_default) - || !strcmp(name,nf_ns_xattr_ntfs)) { + res = 0; + switch (attr) { /* - * Removal of NTFS ACL is never allowed + * Removal of NTFS ACL or ATTRIB is never allowed */ - if (!strcmp(name,nf_ns_xattr_ntfs)) + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : res = -EPERM; - else { - ni = ntfs_check_access_xattr(path,&security); + break; + case XATTR_POSIX_ACC : + case XATTR_POSIX_DEF : + ni = ntfs_check_access_xattr(&security,path); if (ni) { - if (ntfs_remove_posix_acl(&security,path, + if (!ntfs_allowed_as_owner(&security,path,ni) + || ntfs_remove_posix_acl(&security,path, name,ni)) res = -errno; if (ntfs_inode_close(ni)) set_fuse_error(&res); } else res = -errno; + break; + case XATTR_NTFS_REPARSE_DATA : + ni = ntfs_check_access_xattr(&security,path); + if (ni) { + if (!ntfs_allowed_as_owner(&security,path,ni) + || ntfs_remove_ntfs_reparse_data(path,ni)) + res = -errno; + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; + break; + default : /* not possible */ + break; + } +#else + /* + * Only hijack NTFS ACL and ATTRIB removal if POSIX ACLS + * option is not selected + * Access control is partially done by fuse + */ + if (ntfs_fuse_is_named_data_stream(path)) + res = -EINVAL; /* n/a for named data streams. */ + else { + switch (attr) { + /* + * Removal of NTFS ACL or ATTRIB is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (ni) { + /* + * user mapping is not mandatory + * if defined, only owner is allowed + */ + if ((ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_as_owner(&security,path,ni)) + || ntfs_remove_ntfs_reparse_data(path,ni)) + res = -errno; + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; + break; + default : /* not possible */ + break; + } } - return (res); - } #endif + return (res); + } if (ctx->streams != NF_STREAMS_INTERFACE_XATTR) return -EOPNOTSUPP; if (strncmp(name, nf_ns_xattr_preffix, nf_ns_xattr_preffix_len) ||