From c79b3777ca02eeae664848a824a76a475580fe8a Mon Sep 17 00:00:00 2001 From: jpandre Date: Wed, 20 May 2009 14:17:31 +0000 Subject: [PATCH] Implemented backup/restoring of encrypted files (by Martin Bene) --- AUTHORS | 1 + include/ntfs-3g/Makefile.am | 1 + include/ntfs-3g/attrib.h | 3 +- include/ntfs-3g/efs.h | 31 ++++ include/ntfs-3g/volume.h | 2 + libntfs-3g/Makefile.am | 3 +- libntfs-3g/attrib.c | 86 +++++++-- libntfs-3g/efs.c | 339 ++++++++++++++++++++++++++++++++++++ src/ntfs-3g.c | 266 +++++++++++++++++++--------- 9 files changed, 630 insertions(+), 102 deletions(-) create mode 100644 include/ntfs-3g/efs.h create mode 100644 libntfs-3g/efs.c diff --git a/AUTHORS b/AUTHORS index a56a1ef4..f9299b41 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,6 +3,7 @@ Present authors of ntfs-3g in alphabetical order: Jean-Pierre Andre Alon Bar-Lev +Martin Bene Dominique L Bouix Csaba Henk Bernhard Kaindl diff --git a/include/ntfs-3g/Makefile.am b/include/ntfs-3g/Makefile.am index 11923e1f..c801a6e7 100644 --- a/include/ntfs-3g/Makefile.am +++ b/include/ntfs-3g/Makefile.am @@ -14,6 +14,7 @@ headers = \ device.h \ device_io.h \ dir.h \ + efs.h \ endians.h \ index.h \ inode.h \ diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index 073286ea..a11f7b6b 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -285,7 +285,8 @@ extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type); extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type); - +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx); extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, diff --git a/include/ntfs-3g/efs.h b/include/ntfs-3g/efs.h new file mode 100644 index 00000000..bb28e3c7 --- /dev/null +++ b/include/ntfs-3g/efs.h @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2009 Martin Bene + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EFS_H +#define EFS_H + +int ntfs_get_efs_info(const char *path, + char *value, size_t size, ntfs_inode *ni); +int ntfs_set_efs_info(const char *path, + const char *value, size_t size, int flags, + ntfs_inode *ni); +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na); + +#endif /* EFS_H */ diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 069932f2..2aaaf7f6 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -228,6 +228,8 @@ struct _ntfs_volume { s64 free_clusters; /* Track the number of free clusters which greatly improves statfs() performance */ s64 free_mft_records; /* Same for free mft records (see above) */ + BOOL efs_raw; /* volume is mounted for raw access to + efs-encrypted files */ #if CACHE_INODE_SIZE struct CACHE_HEADER *xinode_cache; diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index e618e760..da8ff5db 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -29,6 +29,7 @@ libntfs_3g_la_SOURCES = \ debug.c \ device.c \ dir.c \ + efs.c \ index.c \ inode.c \ lcnalloc.c \ @@ -37,7 +38,7 @@ libntfs_3g_la_SOURCES = \ mft.c \ misc.c \ mst.c \ - reparse.c \ + reparse.c \ runlist.c \ security.c \ unistr.c \ diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 325a11bf..774796ce 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -58,6 +58,7 @@ #include "bitmap.h" #include "logging.h" #include "misc.h" +#include "efs.h" #define STANDARD_COMPRESSION_UNIT 4 @@ -446,7 +447,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, * directory (for unnamed data streams) or on current * inode (for named data streams). The compression mark * may change any time, the compression state can only - * change when stream is void. + * change when stream is wiped out. */ a->flags &= ~ATTR_COMPRESSION_MASK; if (na->ni->flags & FILE_ATTR_COMPRESSED) @@ -806,9 +807,10 @@ map_rl: */ static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) { - s64 br, to_read, ofs, total, total2; + s64 br, to_read, ofs, total, total2, max_read, max_init; ntfs_volume *vol; runlist_element *rl; + u16 efs_padding_length; /* Sanity checking arguments is done in ntfs_attr_pread(). */ @@ -825,20 +827,37 @@ static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) /* * Encrypted non-resident attributes are not supported. We return * access denied, which is what Windows NT4 does, too. + * However, allow if mounted with efs_raw option */ - if (NAttrEncrypted(na) && NAttrNonResident(na)) { + vol = na->ni->vol; + if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) { errno = EACCES; return -1; } - vol = na->ni->vol; if (!count) return 0; - /* Truncate reads beyond end of attribute. */ - if (pos + count > na->data_size) { - if (pos >= na->data_size) + /* + * Truncate reads beyond end of attribute, + * but round to next 512 byte boundary for encrypted + * attributes with efs_raw mount option + */ + max_read = na->data_size; + max_init = na->initialized_size; + if (na->ni->vol->efs_raw + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + if (na->data_size != na->initialized_size) { + ntfs_log_error("uninitialized encrypted file not supported\n"); + errno = EINVAL; + return -1; + } + max_init = max_read = ((na->data_size + 511) & ~511) + 2; + } + if (pos + count > max_read) { + if (pos >= max_read) return 0; - count = na->data_size - pos; + count = max_read - pos; } /* If it is a resident attribute, get the value from the mft record. */ if (!NAttrNonResident(na)) { @@ -868,15 +887,42 @@ res_err_out: } total = total2 = 0; /* Zero out reads beyond initialized size. */ - if (pos + count > na->initialized_size) { - if (pos >= na->initialized_size) { + if (pos + count > max_init) { + if (pos >= max_init) { memset(b, 0, count); return count; } - total2 = pos + count - na->initialized_size; + total2 = pos + count - max_init; count -= total2; memset((u8*)b + count, 0, total2); } + /* + * for encrypted non-resident attributes with efs_raw set + * the last two bytes aren't read from disk but contain + * the number of padding bytes so original size can be + * restored + */ + if (na->ni->vol->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + ((pos + count) > max_init-2)) { + efs_padding_length = 511 - ((na->data_size - 1) & 511); + if (pos+count == max_init) { + if (count == 1) { + *((u8*)b+count-1) = (u8)(efs_padding_length >> 8); + count--; + total2++; + } else { + *(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); + count -= 2; + total2 +=2; + } + } else { + *((u8*)b+count-1) = (u8)(efs_padding_length & 0xff); + count--; + total2++; + } + } + /* Find the runlist element containing the vcn. */ rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); if (!rl) { @@ -1255,10 +1301,12 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) compressed = (na->data_flags & ATTR_COMPRESSION_MASK) != const_cpu_to_le16(0); /* - * Encrypted non-resident attributes are not supported. We return + * Encrypted attributes are only supported in raw mode. We return * access denied, which is what Windows NT4 does, too. + * Moreover a file cannot be both encrypted and compressed. */ - if (NAttrEncrypted(na) && NAttrNonResident(na)) { + if ((na->data_flags & ATTR_IS_ENCRYPTED) + && (compressed || !vol->efs_raw)) { errno = EACCES; goto errno_set; } @@ -3074,7 +3122,7 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, ntfs_inode *base_ni; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", - (long long) ni->mft_no, (unsigned) type, (unsigned) flags); + (long long) ni->mft_no, (unsigned) type, (unsigned) data_flags); if (!ni || (!name && name_len)) { errno = EINVAL; @@ -4051,7 +4099,7 @@ int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) * We expect the caller to do this as this is a fairly low level * function and it is likely there will be further changes made. */ -static int ntfs_attr_make_non_resident(ntfs_attr *na, +int ntfs_attr_make_non_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) { s64 new_allocated_size, bw; @@ -4504,8 +4552,8 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) if (ntfs_attr_can_be_resident(vol, na->type)) return -1; - if (NAttrEncrypted(na)) { - ntfs_log_trace("Making encrypted files resident is not " + if (na->data_flags & ATTR_IS_ENCRYPTED) { + ntfs_log_trace("Making encrypted streams resident is not " "implemented yet.\n"); errno = EOPNOTSUPP; return -1; @@ -5476,9 +5524,9 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) * Encrypted attributes are not supported. We return access denied, * which is what Windows NT4 does, too. */ - if (NAttrEncrypted(na)) { + if (na->data_flags & ATTR_IS_ENCRYPTED) { errno = EACCES; - ntfs_log_perror("Failed to truncate encrypted attribute"); + ntfs_log_info("Failed to truncate encrypted attribute"); goto out; } /* diff --git a/libntfs-3g/efs.c b/libntfs-3g/efs.c new file mode 100644 index 00000000..9bc01351 --- /dev/null +++ b/libntfs-3g/efs.c @@ -0,0 +1,339 @@ +/** + * efs.c - Limited processing of encrypted files + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Martin Bene + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "efs.h" +#include "index.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" + +static ntfschar logged_utility_stream_name[] = { + const_cpu_to_le16('$'), + const_cpu_to_le16('E'), + const_cpu_to_le16('F'), + const_cpu_to_le16('S'), + const_cpu_to_le16(0) +} ; + + +/* + * Get the ntfs EFS info into an extended attribute + */ + +int ntfs_get_efs_info(const char *path, + char *value, size_t size, ntfs_inode *ni) +{ + EFS_ATTR_HEADER *efs_info; + s64 attr_size = 0; + + if (ni) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0, + &attr_size); + if (efs_info + && (le32_to_cpu(efs_info->length) == attr_size)) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,efs_info,attr_size); + else { + errno = EFAULT; + attr_size = 0; + } + } else + if (size) { + errno = ERANGE; + attr_size = 0; + } + free (efs_info); + } else { + if (efs_info) { + free(efs_info); + ntfs_log_info("Bad efs_info for file %s\n",path); + } else { + ntfs_log_info("Could not get efsinfo" + " for file %s\n", path); + } + errno = EIO; + attr_size = 0; + } + } else { + errno = ENODATA; + ntfs_log_info("File %s is not encrypted",path); + } + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Set the efs data from an extended attribute + * Warning : the new data is not checked + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_efs_info(const char *path __attribute__((unused)), + const char *value, size_t size, int flags, + ntfs_inode *ni) +{ + int res; + int written; + ntfs_attr *na; + const EFS_ATTR_HEADER *info_header; + ntfs_attr_search_ctx *ctx; + + res = 0; + if (ni && value && size) { + if (ni->flags && (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { + if (ni->flags && FILE_ATTR_ENCRYPTED) { + ntfs_log_info("File %s already encrypted",path); + errno = EEXIST; + } else { + /* + * Possible problem : if encrypted file was + * restored in a compressed directory, it was + * restored as compressed. + * TODO : decompress first. + */ + ntfs_log_error("File %s cannot be encrypted and compressed\n", + path); + errno = EIO; + } + return -1; + } + info_header = (const EFS_ATTR_HEADER*)value; + /* make sure we get a likely efsinfo */ + if (le32_to_cpu(info_header->length) != size) { + errno = EINVAL; + return (-1); + } + if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM, + (ntfschar*)NULL,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no logged_utility_stream attribute : add one, + * apparently, this does not feed the new value in + */ + res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name,4, + (u8*)NULL,(s64)size); + } else { + errno = ENODATA; + res = -1; + } + } else { + errno = EEXIST; + res = -1; + } + if (!res) { + /* + * open and update the existing efs data + */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name, 4); + 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 efs data\n"); + errno = EIO; + res = -1; + } + } + ntfs_attr_close(na); + } else + res = -1; + } + if (!res) { + /* Don't handle AT_DATA Attribute(s) if inode is a directory */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* iterate over AT_DATA attributes */ + /* set encrypted flag, truncate attribute to match padding bytes */ + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + return (-1); + } + while (!ntfs_attr_lookup(AT_DATA, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_efs_fixup_attribute(ctx, NULL)) { + ntfs_log_error("Error in efs fixup of AT_DATA Attribute"); + ntfs_attr_put_search_ctx(ctx); + return(-1); + } + } + ntfs_attr_put_search_ctx(ctx); + } + ni->flags |= FILE_ATTR_ENCRYPTED; + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Fixup raw encrypted AT_DATA Attribute + * read padding length from last two bytes + * truncate attribute, make non-resident, + * set data size to match padding length + * set ATTR_IS_ENCRYPTED flag on attribute + * + * Return 0 if successful + * -1 if failed (errno tells why) + */ + +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) +{ + u64 newsize; + le16 appended_bytes; + u16 padding_length; + ATTR_RECORD *a; + ntfs_inode *ni; + BOOL close_na = FALSE; + BOOL close_ctx = FALSE; + + if (!ctx && !na) { + ntfs_log_error("neither ctx nor na specified for efs_fixup_attribute\n"); + goto err_out; + } + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + goto err_out; + } + close_ctx=TRUE; + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } + + a = ctx->attr; + if (!na) { + na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, + (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!na) { + ntfs_log_error("can't open DATA Attribute\n"); + return (-1); + } + close_na = TRUE; + } + /* make sure size is valid for a raw encrypted stream */ + if ((na->data_size & 511) != 2) { + ntfs_log_error("Bad raw encrypted stream"); + goto err_out; + } + /* read padding length from last two bytes of attribute */ + if (ntfs_attr_pread(na, na->data_size-2, 2, &appended_bytes) != 2) { + ntfs_log_error("Error reading padding length\n"); + goto err_out; + } + padding_length = le16_to_cpu(appended_bytes); + if (padding_length > 511 || padding_length > na->data_size-2) { + errno = EINVAL; + ntfs_log_error("invalid padding length %d for data_size %lld\n", + padding_length, (long long)na->data_size); + goto err_out; + } + newsize = na->data_size - padding_length - 2; + /* truncate attribute to possibly free clusters allocated + for the last two bytes */ + if (ntfs_attr_truncate(na, na->data_size-2)) { + ntfs_log_error("Error truncating attribute\n"); + goto err_out; + } + + /* Encrypted AT_DATA Attributes MUST be non-resident */ + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + ntfs_log_error("Error making DATA attribute non-resident\n"); + goto err_out; + } + ni = na->ni; + if (!na->name_len) { + ni->data_size = newsize; + ni->allocated_size = na->allocated_size; + } + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + if (close_na) + ntfs_attr_close(na); + + ctx->attr->data_size = cpu_to_le64(newsize); + if (le64_to_cpu(ctx->attr->initialized_size) > newsize) + ctx->attr->initialized_size = ctx->attr->data_size; + ctx->attr->flags |= ATTR_IS_ENCRYPTED; + if (close_ctx) + ntfs_attr_put_search_ctx(ctx); + + return (0); +err_out: + if (close_na && na) + ntfs_attr_close(na); + if (close_ctx && ctx) + ntfs_attr_put_search_ctx(ctx); + return (-1); +} diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 77897732..6e1428eb 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -94,6 +94,7 @@ #include "ntfstime.h" #include "security.h" #include "reparse.h" +#include "efs.h" #include "logging.h" #include "misc.h" @@ -122,6 +123,11 @@ typedef enum { NF_STREAMS_INTERFACE_WINDOWS, /* "file:stream" interface. */ } ntfs_fuse_streams_interface; +enum { + CLOSE_COMPRESSED = 1, + CLOSE_ENCRYPTED = 2 +}; + typedef struct { ntfs_volume *vol; unsigned int uid; @@ -139,6 +145,7 @@ typedef struct { BOOL no_detach; BOOL blkdev; BOOL mounted; + BOOL efs_raw; struct fuse_chan *fc; BOOL inherit; unsigned int secure_flags; @@ -532,6 +539,14 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) /* Regular or Interix (INTX) file. */ stbuf->st_mode = S_IFREG; stbuf->st_size = ni->data_size; + /* + * return data size rounded to next 512 byte boundary for + * encrypted files to include padding required for decryption + * also include 2 bytes for padding info + */ + if (ctx->efs_raw && ni->flags & FILE_ATTR_ENCRYPTED) + stbuf->st_size = ((ni->data_size + 511) & ~511) + 2; + /* * Temporary fix to make ActiveSync work via Samba 3.0. * See more on the ntfs-3g-devel list. @@ -560,6 +575,12 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) if (na->data_size == 1) stbuf->st_mode = S_IFSOCK; } + /* encrypted named stream */ + /* round size up to next 512 byte boundary */ + if (ctx->efs_raw && stream_name_len && + (na->data_flags & ATTR_IS_ENCRYPTED) && + NAttrNonResident(na)) + stbuf->st_size = ((na->data_size+511) & ~511)+2; /* * Check whether it's Interix symbolic link, block or * character device. @@ -793,9 +814,9 @@ static int ntfs_fuse_opendir(const char *path, accesstype = S_IWRITE | S_IREAD; else accesstype = S_IREAD; - /* JPA directory must be searchable */ + /* directory must be searchable */ if (!ntfs_allowed_dir_access(&security,path,S_IEXEC) - /* JPA check whether requested access is allowed */ + /* check whether requested access is allowed */ || !ntfs_allowed_access(&security,path,ni,accesstype)) res = -EACCES; } @@ -865,25 +886,26 @@ static int ntfs_fuse_open(const char *org_path, accesstype = S_IWRITE | S_IREAD; else accesstype = S_IREAD; - if (NAttrEncrypted(na) /* JPA directory must be searchable */ - || !ntfs_allowed_dir_access(&security,path,S_IEXEC) + if (!ntfs_allowed_dir_access(&security, + path,S_IEXEC) /* JPA check whether requested access is allowed */ - || !ntfs_allowed_access(&security,path,ni,accesstype)) - res = -EACCES; - } else { - if (NAttrEncrypted(na)) + || !ntfs_allowed_access(&security, + path,ni,accesstype)) res = -EACCES; } -#else - if (NAttrEncrypted(na)) - res = -EACCES; #endif - /* mark a future need to compress the last chunk */ if ((res >= 0) - && (na->data_flags & ATTR_COMPRESSION_MASK) - && (fi->flags & (O_WRONLY | O_RDWR))) - fi->fh |= 1; + && (fi->flags & (O_WRONLY | O_RDWR))) { + /* mark a future need to compress the last chunk */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + fi->fh |= CLOSE_COMPRESSED; + /* mark a future need to fixup encrypted inode */ + if (ctx->efs_raw + && !(na->data_flags & ATTR_IS_ENCRYPTED) + && (ni->flags & FILE_ATTR_ENCRYPTED)) + fi->fh |= CLOSE_ENCRYPTED; + } ntfs_attr_close(na); } else res = -errno; @@ -906,6 +928,7 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, ntfschar *stream_name; int stream_name_len, res; s64 total = 0; + s64 max_read; if (!size) return 0; @@ -923,10 +946,16 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, res = -errno; goto exit; } - if (offset + (off_t)size > na->data_size) { - if (na->data_size < offset) + /* limit reads at next 512 byte boundary for encrypted attributes */ + max_read = na->data_size; + if (ctx->efs_raw && (na->data_flags & ATTR_IS_ENCRYPTED) && + NAttrNonResident(na)) { + max_read = ((na->data_size+511) & ~511) + 2; + } + if (offset + (off_t)size > max_read) { + if (max_read < offset) goto ok; - size = na->data_size - offset; + size = max_read - offset; } while (size > 0) { s64 ret = ntfs_attr_pread(na, offset, size, buf); @@ -1015,7 +1044,7 @@ static int ntfs_fuse_release(const char *org_path, int stream_name_len, res; /* Only for marked descriptors there is something to do */ - if (!(fi->fh & 1)) { + if (!(fi->fh & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED))) { res = 0; goto out; } @@ -1034,7 +1063,11 @@ static int ntfs_fuse_release(const char *org_path, res = -errno; goto exit; } - res = ntfs_attr_pclose(na); + res = 0; + if (fi->fh & CLOSE_COMPRESSED) + res = ntfs_attr_pclose(na); + if (fi->fh & CLOSE_ENCRYPTED) + res = ntfs_efs_fixup_attribute(NULL, na); exit: if (na) ntfs_attr_close(na); @@ -1372,10 +1405,15 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev, set_fuse_error(&res); #endif } - /* mark a need to compress the end of file */ + /* mark a need to compress the end of file */ if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) { - fi->fh |= 1; + fi->fh |= CLOSE_COMPRESSED; } + /* mark a future need to fixup encrypted inode */ + if (fi + && ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) + fi->fh |= CLOSE_ENCRYPTED; NInoSetDirty(ni); if (ntfs_inode_close(ni)) set_fuse_error(&res); @@ -1426,9 +1464,18 @@ static int ntfs_fuse_create_stream(const char *path, } if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0)) res = -errno; - if ((res >= 0) && (ni->flags & FILE_ATTR_COMPRESSED) - && (fi->flags & (O_WRONLY | O_RDWR))) - fi->fh |= 1; + + if ((res >= 0) + && (fi->flags & (O_WRONLY | O_RDWR))) { + /* mark a future need to compress the last block */ + if (ni->flags & FILE_ATTR_COMPRESSED) + fi->fh |= CLOSE_COMPRESSED; + /* mark a future need to fixup encrypted inode */ + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) + fi->fh |= CLOSE_ENCRYPTED; + } + if (ntfs_inode_close(ni)) set_fuse_error(&res); return res; @@ -1925,11 +1972,14 @@ static const char xattr_ntfs_3g[] = "ntfs-3g."; enum { XATTR_UNMAPPED, XATTR_NTFS_ACL, XATTR_NTFS_ATTRIB, + XATTR_NTFS_EFSINFO, XATTR_NTFS_REPARSE_DATA, - XATTR_POSIX_ACC, XATTR_POSIX_DEF } ; + XATTR_POSIX_ACC, + XATTR_POSIX_DEF } ; 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_efsinfo[] = "user.ntfs.efsinfo"; static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; @@ -2003,7 +2053,12 @@ static int mapped_xattr_system(const char *name) if (!strcmp(name,nf_ns_xattr_posix_default)) num = XATTR_POSIX_DEF; else - num = XATTR_UNMAPPED; + if (ctx->efs_raw + && !strcmp(name, + nf_ns_xattr_efsinfo)) + num = XATTR_NTFS_EFSINFO; + else + num = XATTR_UNMAPPED; return (num); } @@ -2093,10 +2148,6 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size) #if POSIXACLS struct SECURITY_CONTEXT security; #endif - - if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR) - && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) - return -EOPNOTSUPP; #if POSIXACLS /* parent directory must be executable */ if (ntfs_fuse_fill_security_context(&security) @@ -2121,53 +2172,71 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size) ntfs_inode_close(ni); goto exit; } - while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, - 0, NULL, 0, actx)) { - char *tmp_name = NULL; - int tmp_name_len; - if (!actx->attr->name_length) - continue; - tmp_name_len = ntfs_ucstombs((ntfschar *)((u8*)actx->attr + - le16_to_cpu(actx->attr->name_offset)), + if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR) + || (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) { + while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) { + char *tmp_name = NULL; + int tmp_name_len; + + if (!actx->attr->name_length) + continue; + tmp_name_len = ntfs_ucstombs( + (ntfschar *)((u8*)actx->attr + + le16_to_cpu(actx->attr->name_offset)), actx->attr->name_length, &tmp_name, 0); - if (tmp_name_len < 0) { - ret = -errno; - goto exit; - } - /* - * When using name spaces, do not return - * security, trusted nor system attributes - * (filtered elsewhere anyway) - * otherwise insert "user." prefix - */ - if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { - if ((strlen(tmp_name) > sizeof(xattr_ntfs_3g)) - && !strncmp(tmp_name,xattr_ntfs_3g, - sizeof(xattr_ntfs_3g)-1)) - tmp_name_len = 0; - else - ret += tmp_name_len + nf_ns_user_prefix_len + 1; - } else - ret += tmp_name_len + 1; - if (size && tmp_name_len) { - if ((size_t)ret <= size) { - if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { - strcpy(to, nf_ns_user_prefix); - to += nf_ns_user_prefix_len; - } - strncpy(to, tmp_name, tmp_name_len); - to += tmp_name_len; - *to = 0; - to++; - } else { - free(tmp_name); - ret = -ERANGE; + if (tmp_name_len < 0) { + ret = -errno; goto exit; } + /* + * When using name spaces, do not return + * security, trusted nor system attributes + * (filtered elsewhere anyway) + * otherwise insert "user." prefix + */ + if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { + if ((strlen(tmp_name) > sizeof(xattr_ntfs_3g)) + && !strncmp(tmp_name,xattr_ntfs_3g, + sizeof(xattr_ntfs_3g)-1)) + tmp_name_len = 0; + else + ret += tmp_name_len + + nf_ns_user_prefix_len + 1; + } else + ret += tmp_name_len + 1; + if (size && tmp_name_len) { + if ((size_t)ret <= size) { + if (ctx->streams + == NF_STREAMS_INTERFACE_XATTR) { + strcpy(to, nf_ns_user_prefix); + to += nf_ns_user_prefix_len; + } + strncpy(to, tmp_name, tmp_name_len); + to += tmp_name_len; + *to = 0; + to++; + } else { + free(tmp_name); + ret = -ERANGE; + goto exit; + } + } + free(tmp_name); } - free(tmp_name); } + + /* List efs info xattr for encrypted files */ + if (ctx->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED)) { + ret += sizeof(nf_ns_xattr_efsinfo); + if ((size_t)ret <= size) { + memcpy(to, nf_ns_xattr_efsinfo, + sizeof(nf_ns_xattr_efsinfo)); + to += sizeof(nf_ns_xattr_efsinfo); + } + } + if (errno != ENOENT) ret = -errno; exit: @@ -2263,6 +2332,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, ntfs_attr *na = NULL; ntfschar *lename = NULL; int res, lename_len; + s64 rsize; int attr; int namespace; struct SECURITY_CONTEXT security; @@ -2298,6 +2368,10 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, res = ntfs_get_ntfs_attrib(path, value,size,ni); break; + case XATTR_NTFS_EFSINFO : + res = ntfs_get_efs_info(path, + value,size,ni); + break; case XATTR_NTFS_REPARSE_DATA : res = ntfs_get_ntfs_reparse_data(path, value,size,ni); @@ -2305,8 +2379,9 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, default : /* not possible */ break; } - } else + } else { res = -errno; + } if (ntfs_inode_close(ni)) set_fuse_error(&res); } else @@ -2339,6 +2414,10 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, res = ntfs_get_ntfs_attrib(path, value,size,ni); break; + case XATTR_NTFS_EFSINFO : + res = ntfs_get_efs_info(path, + value,size,ni); + break; case XATTR_NTFS_REPARSE_DATA : res = ntfs_get_ntfs_reparse_data(path, value,size,ni); @@ -2398,15 +2477,20 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, res = -ENODATA; goto exit; } + rsize = na->data_size; + if (ctx->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + NAttrNonResident(na)) + rsize = ((na->data_size + 511) & ~511)+2; if (size) { - if (size >= (size_t)na->data_size) { - res = ntfs_attr_pread(na, 0, na->data_size, value); - if (res != na->data_size) + if (size >= (size_t)rsize) { + res = ntfs_attr_pread(na, 0, rsize, value); + if (res != rsize) res = -errno; } else res = -ERANGE; } else - res = na->data_size; + res = rsize; exit: if (na) ntfs_attr_close(na); @@ -2454,6 +2538,10 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = ntfs_set_ntfs_attrib(path, value,size,flags,ni); break; + case XATTR_NTFS_EFSINFO : + res = ntfs_set_efs_info(path, + value,size,flags,ni); + break; case XATTR_NTFS_REPARSE_DATA : res = ntfs_set_ntfs_reparse_data(path, value,size,flags,ni); @@ -2495,6 +2583,10 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = ntfs_set_ntfs_attrib(path, value,size,flags,ni); break; + case XATTR_NTFS_EFSINFO : + res = ntfs_set_efs_info(path, + value,size,flags,ni); + break; case XATTR_NTFS_REPARSE_DATA : res = ntfs_set_ntfs_reparse_data(path, value,size,flags,ni); @@ -2603,10 +2695,14 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, if (part > 0) total += part; } while ((part > 0) && (total < size)); - if (total != size) - res = -errno; - else - res = ntfs_attr_pclose(na); + if (total != size) + res = -errno; + else + if (!(res = ntfs_attr_pclose(na))) + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) + res = ntfs_efs_fixup_attribute(NULL, + na); } else res = 0; exit: @@ -2644,6 +2740,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) */ case XATTR_NTFS_ACL : case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_EFSINFO : res = -EPERM; break; case XATTR_POSIX_ACC : @@ -2688,6 +2785,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) */ case XATTR_NTFS_ACL : case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_EFSINFO : res = -EPERM; break; case XATTR_NTFS_REPARSE_DATA : @@ -2994,6 +3092,7 @@ static char *parse_mount_options(const char *orig_opts) int default_permissions = 0; ctx->secure_flags = 0; + ctx->efs_raw = FALSE; options = strdup(orig_opts ? orig_opts : ""); if (!options) { ntfs_log_perror("%s: strdup failed", EXEC_NAME); @@ -3164,6 +3263,10 @@ static char *parse_mount_options(const char *orig_opts) "'usermapping' option.\n"); goto err_exit; } + } else if (!strcmp(opt, "efs_raw")) { + if (bogus_option_value(val, "efs_raw")) + goto err_exit; + ctx->efs_raw = TRUE; } else { /* Probably FUSE option. */ if (strappend(&ret, opt)) goto err_exit; @@ -3615,6 +3718,7 @@ int main(int argc, char *argv[]) ctx->security.vol = ctx->vol; ctx->vol->secure_flags = ctx->secure_flags; + ctx->vol->efs_raw = ctx->efs_raw; if (ctx->secure_flags & (1 << SECURITY_RAW)) { ctx->security.uid = ctx->uid; ctx->security.gid = ctx->gid;