Implemented backup/restoring of encrypted files (by Martin Bene)

This commit is contained in:
jpandre 2009-05-20 14:17:31 +00:00
parent 1cd46c2241
commit c79b3777ca
9 changed files with 630 additions and 102 deletions

View File

@ -3,6 +3,7 @@ Present authors of ntfs-3g in alphabetical order:
Jean-Pierre Andre Jean-Pierre Andre
Alon Bar-Lev Alon Bar-Lev
Martin Bene
Dominique L Bouix Dominique L Bouix
Csaba Henk Csaba Henk
Bernhard Kaindl Bernhard Kaindl

View File

@ -14,6 +14,7 @@ headers = \
device.h \ device.h \
device_io.h \ device_io.h \
dir.h \ dir.h \
efs.h \
endians.h \ endians.h \
index.h \ index.h \
inode.h \ inode.h \

View File

@ -285,7 +285,8 @@ extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol,
const ATTR_TYPES type); const ATTR_TYPES type);
extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
const ATTR_TYPES type); 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_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,

31
include/ntfs-3g/efs.h Normal file
View File

@ -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 */

View File

@ -228,6 +228,8 @@ struct _ntfs_volume {
s64 free_clusters; /* Track the number of free clusters which s64 free_clusters; /* Track the number of free clusters which
greatly improves statfs() performance */ greatly improves statfs() performance */
s64 free_mft_records; /* Same for free mft records (see above) */ 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 #if CACHE_INODE_SIZE
struct CACHE_HEADER *xinode_cache; struct CACHE_HEADER *xinode_cache;

View File

@ -29,6 +29,7 @@ libntfs_3g_la_SOURCES = \
debug.c \ debug.c \
device.c \ device.c \
dir.c \ dir.c \
efs.c \
index.c \ index.c \
inode.c \ inode.c \
lcnalloc.c \ lcnalloc.c \

View File

@ -58,6 +58,7 @@
#include "bitmap.h" #include "bitmap.h"
#include "logging.h" #include "logging.h"
#include "misc.h" #include "misc.h"
#include "efs.h"
#define STANDARD_COMPRESSION_UNIT 4 #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 * directory (for unnamed data streams) or on current
* inode (for named data streams). The compression mark * inode (for named data streams). The compression mark
* may change any time, the compression state can only * 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; a->flags &= ~ATTR_COMPRESSION_MASK;
if (na->ni->flags & FILE_ATTR_COMPRESSED) 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) 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; ntfs_volume *vol;
runlist_element *rl; runlist_element *rl;
u16 efs_padding_length;
/* Sanity checking arguments is done in ntfs_attr_pread(). */ /* 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 * Encrypted non-resident attributes are not supported. We return
* access denied, which is what Windows NT4 does, too. * 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; errno = EACCES;
return -1; return -1;
} }
vol = na->ni->vol;
if (!count) if (!count)
return 0; return 0;
/* Truncate reads beyond end of attribute. */ /*
if (pos + count > na->data_size) { * Truncate reads beyond end of attribute,
if (pos >= na->data_size) * 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; 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 it is a resident attribute, get the value from the mft record. */
if (!NAttrNonResident(na)) { if (!NAttrNonResident(na)) {
@ -868,15 +887,42 @@ res_err_out:
} }
total = total2 = 0; total = total2 = 0;
/* Zero out reads beyond initialized size. */ /* Zero out reads beyond initialized size. */
if (pos + count > na->initialized_size) { if (pos + count > max_init) {
if (pos >= na->initialized_size) { if (pos >= max_init) {
memset(b, 0, count); memset(b, 0, count);
return count; return count;
} }
total2 = pos + count - na->initialized_size; total2 = pos + count - max_init;
count -= total2; count -= total2;
memset((u8*)b + count, 0, 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. */ /* Find the runlist element containing the vcn. */
rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
if (!rl) { 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) compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0); != 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. * 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; errno = EACCES;
goto errno_set; goto errno_set;
} }
@ -3074,7 +3122,7 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
ntfs_inode *base_ni; ntfs_inode *base_ni;
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", 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)) { if (!ni || (!name && name_len)) {
errno = EINVAL; 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 * 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. * 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) ntfs_attr_search_ctx *ctx)
{ {
s64 new_allocated_size, bw; 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)) if (ntfs_attr_can_be_resident(vol, na->type))
return -1; return -1;
if (NAttrEncrypted(na)) { if (na->data_flags & ATTR_IS_ENCRYPTED) {
ntfs_log_trace("Making encrypted files resident is not " ntfs_log_trace("Making encrypted streams resident is not "
"implemented yet.\n"); "implemented yet.\n");
errno = EOPNOTSUPP; errno = EOPNOTSUPP;
return -1; 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, * Encrypted attributes are not supported. We return access denied,
* which is what Windows NT4 does, too. * which is what Windows NT4 does, too.
*/ */
if (NAttrEncrypted(na)) { if (na->data_flags & ATTR_IS_ENCRYPTED) {
errno = EACCES; errno = EACCES;
ntfs_log_perror("Failed to truncate encrypted attribute"); ntfs_log_info("Failed to truncate encrypted attribute");
goto out; goto out;
} }
/* /*

339
libntfs-3g/efs.c Normal file
View File

@ -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 <stdlib.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#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);
}

View File

@ -94,6 +94,7 @@
#include "ntfstime.h" #include "ntfstime.h"
#include "security.h" #include "security.h"
#include "reparse.h" #include "reparse.h"
#include "efs.h"
#include "logging.h" #include "logging.h"
#include "misc.h" #include "misc.h"
@ -122,6 +123,11 @@ typedef enum {
NF_STREAMS_INTERFACE_WINDOWS, /* "file:stream" interface. */ NF_STREAMS_INTERFACE_WINDOWS, /* "file:stream" interface. */
} ntfs_fuse_streams_interface; } ntfs_fuse_streams_interface;
enum {
CLOSE_COMPRESSED = 1,
CLOSE_ENCRYPTED = 2
};
typedef struct { typedef struct {
ntfs_volume *vol; ntfs_volume *vol;
unsigned int uid; unsigned int uid;
@ -139,6 +145,7 @@ typedef struct {
BOOL no_detach; BOOL no_detach;
BOOL blkdev; BOOL blkdev;
BOOL mounted; BOOL mounted;
BOOL efs_raw;
struct fuse_chan *fc; struct fuse_chan *fc;
BOOL inherit; BOOL inherit;
unsigned int secure_flags; 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. */ /* Regular or Interix (INTX) file. */
stbuf->st_mode = S_IFREG; stbuf->st_mode = S_IFREG;
stbuf->st_size = ni->data_size; 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. * Temporary fix to make ActiveSync work via Samba 3.0.
* See more on the ntfs-3g-devel list. * 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) if (na->data_size == 1)
stbuf->st_mode = S_IFSOCK; 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 * Check whether it's Interix symbolic link, block or
* character device. * character device.
@ -793,9 +814,9 @@ static int ntfs_fuse_opendir(const char *path,
accesstype = S_IWRITE | S_IREAD; accesstype = S_IWRITE | S_IREAD;
else else
accesstype = S_IREAD; accesstype = S_IREAD;
/* JPA directory must be searchable */ /* directory must be searchable */
if (!ntfs_allowed_dir_access(&security,path,S_IEXEC) 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)) || !ntfs_allowed_access(&security,path,ni,accesstype))
res = -EACCES; res = -EACCES;
} }
@ -865,25 +886,26 @@ static int ntfs_fuse_open(const char *org_path,
accesstype = S_IWRITE | S_IREAD; accesstype = S_IWRITE | S_IREAD;
else else
accesstype = S_IREAD; accesstype = S_IREAD;
if (NAttrEncrypted(na)
/* JPA directory must be searchable */ /* 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 */ /* JPA check whether requested access is allowed */
|| !ntfs_allowed_access(&security,path,ni,accesstype)) || !ntfs_allowed_access(&security,
res = -EACCES; path,ni,accesstype))
} else {
if (NAttrEncrypted(na))
res = -EACCES; res = -EACCES;
} }
#else
if (NAttrEncrypted(na))
res = -EACCES;
#endif #endif
/* mark a future need to compress the last chunk */
if ((res >= 0) if ((res >= 0)
&& (na->data_flags & ATTR_COMPRESSION_MASK) && (fi->flags & (O_WRONLY | O_RDWR))) {
&& (fi->flags & (O_WRONLY | O_RDWR))) /* mark a future need to compress the last chunk */
fi->fh |= 1; 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); ntfs_attr_close(na);
} else } else
res = -errno; res = -errno;
@ -906,6 +928,7 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size,
ntfschar *stream_name; ntfschar *stream_name;
int stream_name_len, res; int stream_name_len, res;
s64 total = 0; s64 total = 0;
s64 max_read;
if (!size) if (!size)
return 0; return 0;
@ -923,10 +946,16 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size,
res = -errno; res = -errno;
goto exit; goto exit;
} }
if (offset + (off_t)size > na->data_size) { /* limit reads at next 512 byte boundary for encrypted attributes */
if (na->data_size < offset) 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; goto ok;
size = na->data_size - offset; size = max_read - offset;
} }
while (size > 0) { while (size > 0) {
s64 ret = ntfs_attr_pread(na, offset, size, buf); 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; int stream_name_len, res;
/* Only for marked descriptors there is something to do */ /* Only for marked descriptors there is something to do */
if (!(fi->fh & 1)) { if (!(fi->fh & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED))) {
res = 0; res = 0;
goto out; goto out;
} }
@ -1034,7 +1063,11 @@ static int ntfs_fuse_release(const char *org_path,
res = -errno; res = -errno;
goto exit; goto exit;
} }
res = 0;
if (fi->fh & CLOSE_COMPRESSED)
res = ntfs_attr_pclose(na); res = ntfs_attr_pclose(na);
if (fi->fh & CLOSE_ENCRYPTED)
res = ntfs_efs_fixup_attribute(NULL, na);
exit: exit:
if (na) if (na)
ntfs_attr_close(na); ntfs_attr_close(na);
@ -1374,8 +1407,13 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
} }
/* 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)) { 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); NInoSetDirty(ni);
if (ntfs_inode_close(ni)) if (ntfs_inode_close(ni))
set_fuse_error(&res); 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)) if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0))
res = -errno; res = -errno;
if ((res >= 0) && (ni->flags & FILE_ATTR_COMPRESSED)
&& (fi->flags & (O_WRONLY | O_RDWR))) if ((res >= 0)
fi->fh |= 1; && (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)) if (ntfs_inode_close(ni))
set_fuse_error(&res); set_fuse_error(&res);
return res; return res;
@ -1925,11 +1972,14 @@ static const char xattr_ntfs_3g[] = "ntfs-3g.";
enum { XATTR_UNMAPPED, enum { XATTR_UNMAPPED,
XATTR_NTFS_ACL, XATTR_NTFS_ACL,
XATTR_NTFS_ATTRIB, XATTR_NTFS_ATTRIB,
XATTR_NTFS_EFSINFO,
XATTR_NTFS_REPARSE_DATA, 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_ntfs[] = "system.ntfs_acl";
static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; 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_reparse[] = "system.ntfs_reparse_data";
static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access";
static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default";
@ -2002,6 +2052,11 @@ static int mapped_xattr_system(const char *name)
else else
if (!strcmp(name,nf_ns_xattr_posix_default)) if (!strcmp(name,nf_ns_xattr_posix_default))
num = XATTR_POSIX_DEF; num = XATTR_POSIX_DEF;
else
if (ctx->efs_raw
&& !strcmp(name,
nf_ns_xattr_efsinfo))
num = XATTR_NTFS_EFSINFO;
else else
num = XATTR_UNMAPPED; num = XATTR_UNMAPPED;
return (num); return (num);
@ -2093,10 +2148,6 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size)
#if POSIXACLS #if POSIXACLS
struct SECURITY_CONTEXT security; struct SECURITY_CONTEXT security;
#endif #endif
if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
&& (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR))
return -EOPNOTSUPP;
#if POSIXACLS #if POSIXACLS
/* parent directory must be executable */ /* parent directory must be executable */
if (ntfs_fuse_fill_security_context(&security) if (ntfs_fuse_fill_security_context(&security)
@ -2121,6 +2172,9 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size)
ntfs_inode_close(ni); ntfs_inode_close(ni);
goto exit; goto exit;
} }
if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR)
|| (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) {
while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE,
0, NULL, 0, actx)) { 0, NULL, 0, actx)) {
char *tmp_name = NULL; char *tmp_name = NULL;
@ -2128,7 +2182,8 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size)
if (!actx->attr->name_length) if (!actx->attr->name_length)
continue; continue;
tmp_name_len = ntfs_ucstombs((ntfschar *)((u8*)actx->attr + tmp_name_len = ntfs_ucstombs(
(ntfschar *)((u8*)actx->attr +
le16_to_cpu(actx->attr->name_offset)), le16_to_cpu(actx->attr->name_offset)),
actx->attr->name_length, &tmp_name, 0); actx->attr->name_length, &tmp_name, 0);
if (tmp_name_len < 0) { if (tmp_name_len < 0) {
@ -2147,12 +2202,14 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size)
sizeof(xattr_ntfs_3g)-1)) sizeof(xattr_ntfs_3g)-1))
tmp_name_len = 0; tmp_name_len = 0;
else else
ret += tmp_name_len + nf_ns_user_prefix_len + 1; ret += tmp_name_len
+ nf_ns_user_prefix_len + 1;
} else } else
ret += tmp_name_len + 1; ret += tmp_name_len + 1;
if (size && tmp_name_len) { if (size && tmp_name_len) {
if ((size_t)ret <= size) { if ((size_t)ret <= size) {
if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { if (ctx->streams
== NF_STREAMS_INTERFACE_XATTR) {
strcpy(to, nf_ns_user_prefix); strcpy(to, nf_ns_user_prefix);
to += nf_ns_user_prefix_len; to += nf_ns_user_prefix_len;
} }
@ -2168,6 +2225,18 @@ static int ntfs_fuse_listxattr(const char *path, char *list, size_t size)
} }
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) if (errno != ENOENT)
ret = -errno; ret = -errno;
exit: exit:
@ -2263,6 +2332,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
ntfs_attr *na = NULL; ntfs_attr *na = NULL;
ntfschar *lename = NULL; ntfschar *lename = NULL;
int res, lename_len; int res, lename_len;
s64 rsize;
int attr; int attr;
int namespace; int namespace;
struct SECURITY_CONTEXT security; 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, res = ntfs_get_ntfs_attrib(path,
value,size,ni); value,size,ni);
break; break;
case XATTR_NTFS_EFSINFO :
res = ntfs_get_efs_info(path,
value,size,ni);
break;
case XATTR_NTFS_REPARSE_DATA : case XATTR_NTFS_REPARSE_DATA :
res = ntfs_get_ntfs_reparse_data(path, res = ntfs_get_ntfs_reparse_data(path,
value,size,ni); value,size,ni);
@ -2305,8 +2379,9 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
default : /* not possible */ default : /* not possible */
break; break;
} }
} else } else {
res = -errno; res = -errno;
}
if (ntfs_inode_close(ni)) if (ntfs_inode_close(ni))
set_fuse_error(&res); set_fuse_error(&res);
} else } else
@ -2339,6 +2414,10 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
res = ntfs_get_ntfs_attrib(path, res = ntfs_get_ntfs_attrib(path,
value,size,ni); value,size,ni);
break; break;
case XATTR_NTFS_EFSINFO :
res = ntfs_get_efs_info(path,
value,size,ni);
break;
case XATTR_NTFS_REPARSE_DATA : case XATTR_NTFS_REPARSE_DATA :
res = ntfs_get_ntfs_reparse_data(path, res = ntfs_get_ntfs_reparse_data(path,
value,size,ni); value,size,ni);
@ -2398,15 +2477,20 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
res = -ENODATA; res = -ENODATA;
goto exit; 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) {
if (size >= (size_t)na->data_size) { if (size >= (size_t)rsize) {
res = ntfs_attr_pread(na, 0, na->data_size, value); res = ntfs_attr_pread(na, 0, rsize, value);
if (res != na->data_size) if (res != rsize)
res = -errno; res = -errno;
} else } else
res = -ERANGE; res = -ERANGE;
} else } else
res = na->data_size; res = rsize;
exit: exit:
if (na) if (na)
ntfs_attr_close(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, res = ntfs_set_ntfs_attrib(path,
value,size,flags,ni); value,size,flags,ni);
break; break;
case XATTR_NTFS_EFSINFO :
res = ntfs_set_efs_info(path,
value,size,flags,ni);
break;
case XATTR_NTFS_REPARSE_DATA : case XATTR_NTFS_REPARSE_DATA :
res = ntfs_set_ntfs_reparse_data(path, res = ntfs_set_ntfs_reparse_data(path,
value,size,flags,ni); 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, res = ntfs_set_ntfs_attrib(path,
value,size,flags,ni); value,size,flags,ni);
break; break;
case XATTR_NTFS_EFSINFO :
res = ntfs_set_efs_info(path,
value,size,flags,ni);
break;
case XATTR_NTFS_REPARSE_DATA : case XATTR_NTFS_REPARSE_DATA :
res = ntfs_set_ntfs_reparse_data(path, res = ntfs_set_ntfs_reparse_data(path,
value,size,flags,ni); value,size,flags,ni);
@ -2606,7 +2698,11 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
if (total != size) if (total != size)
res = -errno; res = -errno;
else else
res = ntfs_attr_pclose(na); if (!(res = ntfs_attr_pclose(na)))
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED))
res = ntfs_efs_fixup_attribute(NULL,
na);
} else } else
res = 0; res = 0;
exit: exit:
@ -2644,6 +2740,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name)
*/ */
case XATTR_NTFS_ACL : case XATTR_NTFS_ACL :
case XATTR_NTFS_ATTRIB : case XATTR_NTFS_ATTRIB :
case XATTR_NTFS_EFSINFO :
res = -EPERM; res = -EPERM;
break; break;
case XATTR_POSIX_ACC : 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_ACL :
case XATTR_NTFS_ATTRIB : case XATTR_NTFS_ATTRIB :
case XATTR_NTFS_EFSINFO :
res = -EPERM; res = -EPERM;
break; break;
case XATTR_NTFS_REPARSE_DATA : case XATTR_NTFS_REPARSE_DATA :
@ -2994,6 +3092,7 @@ static char *parse_mount_options(const char *orig_opts)
int default_permissions = 0; int default_permissions = 0;
ctx->secure_flags = 0; ctx->secure_flags = 0;
ctx->efs_raw = FALSE;
options = strdup(orig_opts ? orig_opts : ""); options = strdup(orig_opts ? orig_opts : "");
if (!options) { if (!options) {
ntfs_log_perror("%s: strdup failed", EXEC_NAME); ntfs_log_perror("%s: strdup failed", EXEC_NAME);
@ -3164,6 +3263,10 @@ static char *parse_mount_options(const char *orig_opts)
"'usermapping' option.\n"); "'usermapping' option.\n");
goto err_exit; 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. */ } else { /* Probably FUSE option. */
if (strappend(&ret, opt)) if (strappend(&ret, opt))
goto err_exit; goto err_exit;
@ -3615,6 +3718,7 @@ int main(int argc, char *argv[])
ctx->security.vol = ctx->vol; ctx->security.vol = ctx->vol;
ctx->vol->secure_flags = ctx->secure_flags; ctx->vol->secure_flags = ctx->secure_flags;
ctx->vol->efs_raw = ctx->efs_raw;
if (ctx->secure_flags & (1 << SECURITY_RAW)) { if (ctx->secure_flags & (1 << SECURITY_RAW)) {
ctx->security.uid = ctx->uid; ctx->security.uid = ctx->uid;
ctx->security.gid = ctx->gid; ctx->security.gid = ctx->gid;