Beruflich Dokumente
Kultur Dokumente
#include <signal.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <syslog.h>
#include <sys/wait.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_MKDEV_H
#include <sys/mkdev.h>
#endif
#if defined(__APPLE__) || defined(__DARWIN__)
#include <sys/dirent.h>
#elif defined(__sun) && defined (__SVR4)
#include <sys/param.h>
#endif /* defined(__APPLE__) || defined(__DARWIN__), ... */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
"compat.h"
"attrib.h"
"inode.h"
"volume.h"
"dir.h"
"unistr.h"
"layout.h"
"index.h"
"ntfstime.h"
"security.h"
"reparse.h"
"object_id.h"
"efs.h"
"logging.h"
"xattrs.h"
"misc.h"
"ioctl.h"
#include "ntfs-3g_common.h"
/*
*
*
*/
/*
#define
/*
#define
/*
#define
"
umask=, fmask=, dmask=, streams_interface=.\n"
"
Please see the details in the manual (type: man ntfs-3g).\n"
"\n"
"Example: ntfs-3g /dev/sda1 /mnt/windows\n"
"\n"
"%s";
static const char ntfs_bad_reparse[] = "unsupported reparse point";
#ifdef FUSE_INTERNAL
int drop_privs(void);
int restore_privs(void);
#else
/*
* setuid and setgid root ntfs-3g denies to start with external FUSE,
* therefore the below functions are no-op in such case.
*/
static int drop_privs(void)
{ return 0; }
#if defined(linux) || defined(__uClinux__)
static int restore_privs(void) { return 0; }
#endif
static const char *setuid_msg =
"Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n"
"external FUSE library. Either remove the setuid/setgid bit from the binary\n"
"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n"
"Please see more information at\n"
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
static const char *unpriv_fuseblk_msg =
"Unprivileged user can not mount NTFS block devices using the external FUSE\n"
"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n"
"FUSE support and make it setuid root. Please see more information at\n"
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
#endif
/**
* ntfs_fuse_is_named_data_stream - check path to be to named data stream
* @path:
path to check
*
* Returns 1 if path is to named data stream or 0 otherwise.
*/
static int ntfs_fuse_is_named_data_stream(const char *path)
{
if (strchr(path, ':') && ctx->streams == NF_STREAMS_INTERFACE_WINDOWS)
return 1;
return 0;
}
static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask)
{
if (ctx->atime == ATIME_DISABLED)
mask &= ~NTFS_UPDATE_ATIME;
else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME &&
(sle64_to_cpu(ni->last_access_time)
>= sle64_to_cpu(ni->last_data_change_time)) &&
(sle64_to_cpu(ni->last_access_time)
>= sle64_to_cpu(ni->last_mft_change_time)))
return;
ntfs_inode_update_times(ni, mask);
}
static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol)
{
ntfs_attr *na = vol->mftbmp_na;
s64 nr_free = ntfs_attr_get_free_bits(na);
if (nr_free >= 0)
nr_free += (na->allocated_size - na->data_size) << 3;
return nr_free;
}
/*
*
*
*
*
*
*/
{
int allowed;
ntfs_inode *ni2;
ntfs_inode *dir_ni2;
char *dirpath;
char *name;
struct stat stbuf;
#if POSIXACLS & KERNELPERMS & !KERNELACLS
/* short-circuit if PERMS checked by kernel and ACLs by fs */
if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT))
allowed = 1;
else
#endif
if (dir_ni)
allowed = ntfs_real_allowed_access(scx, dir_ni,
accesstype);
else {
allowed = 0;
dirpath = strdup(path);
if (dirpath) {
/* the root of file system is seen as a parent of itself */
/* is that correct ? */
name = strrchr(dirpath, '/');
*name = 0;
dir_ni2 = ntfs_pathname_to_inode(scx->vol,
NULL, dirpath);
if (dir_ni2) {
allowed = ntfs_real_allowed_access(scx,
dir_ni2, accesstype);
if (ntfs_inode_close(dir_ni2))
allowed = 0;
}
free(dirpath);
}
}
/*
* for a not-owned sticky directory, have to
* check whether file itself is owned
*/
if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
&& (allowed == 2)) {
if (ni)
ni2 = ni;
else
ni2 = ntfs_pathname_to_inode(scx->vol, NULL,
path);
allowed = 0;
if (ni2) {
allowed = (ntfs_get_owner_mode(scx,ni2,&stbuf)
>= 0)
&& (stbuf.st_uid == scx->uid);
if (!ni)
ntfs_inode_close(ni2);
}
}
return (allowed);
}
#endif
#ifdef HAVE_SETXATTR
/*
*
*
*
*
*
*
*
*
*/
NULL, dirpath);
}
free(dirpath);
} else
errno = ENOMEM;
return (dir_ni);
}
#endif /* HAVE_SETXATTR */
/**
* ntfs_fuse_statfs - return information about mounted NTFS volume
* @path:
ignored (but fuse requires it)
* @sfs:
statfs structure in which to return the information
*
* Return information about the mounted NTFS volume @sb in the statfs structure
* pointed to by @sfs (this is initialized with zeros before ntfs_statfs is
* called). We interpret the values to be correct of the moment in time at
* which we are called. Most values are variable otherwise and this isn't just
* the free values but the totals as well. For example we can increase the
* total number of file nodes if we run out and we can keep doing this until
* there is no more space on the volume left at all.
*
* This code based on ntfs_statfs from ntfs kernel driver.
*
* Returns 0 on success or -errno on error.
*/
static int ntfs_fuse_statfs(const char *path __attribute__((unused)),
struct statvfs *sfs)
{
s64 size;
int delta_bits;
ntfs_volume *vol;
vol = ctx->vol;
if (!vol)
return -ENODEV;
/*
* File system block size. Used to calculate used/free space by df.
* Incorrectly documented as "optimal transfer block size".
*/
sfs->f_bsize = vol->cluster_size;
/* Fundamental file system block size, used as the unit. */
sfs->f_frsize = vol->cluster_size;
/*
* Total number of blocks on file system in units of f_frsize.
* Since inodes are also stored in blocks ($MFT is a file) hence
* this is the number of clusters on the volume.
*/
sfs->f_blocks = vol->nr_clusters;
/* Free blocks available for all and for non-privileged processes. */
size = vol->free_clusters;
if (size < 0)
size = 0;
sfs->f_bavail = sfs->f_bfree = size;
return 0;
}
static void set_fuse_error(int *err)
{
if (!*err)
*err = -errno;
}
#if defined(__APPLE__) || defined(__DARWIN__)
static int ntfs_macfuse_getxtimes(const char *org_path,
struct timespec *bkuptime, struct timespec *crtime)
{
int res = 0;
ntfs_inode *ni;
char *path = NULL;
ntfschar *stream_name;
int stream_name_len;
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
if (stream_name_len < 0)
return stream_name_len;
memset(bkuptime, 0, sizeof(struct timespec));
memset(crtime, 0, sizeof(struct timespec));
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni) {
res = -errno;
goto exit;
}
/* We have no backup timestamp in NTFS. */
crtime->tv_sec = sle64_to_cpu(ni->creation_time);
exit:
if (ntfs_inode_close(ni))
set_fuse_error(&res);
free(path);
if (stream_name_len)
free(stream_name);
return res;
}
int ntfs_macfuse_setcrtime(const char *path, const struct timespec *tv)
{
ntfs_inode *ni;
int res = 0;
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
if (tv) {
ni->creation_time = cpu_to_sle64(tv->tv_sec);
ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
int ntfs_macfuse_setbkuptime(const char *path, const struct timespec *tv)
{
ntfs_inode *ni;
int res = 0;
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
/*
* Only pretending to set backup time successfully to please the APIs of
* Mac OS X. In reality, NTFS has no backup time.
*/
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv)
{
ntfs_inode *ni;
int res = 0;
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
if (tv) {
ni->last_mft_change_time = cpu_to_sle64(tv->tv_sec);
ntfs_fuse_update_times(ni, 0);
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
static void *ntfs_init(struct fuse_conn_info *conn)
{
#if defined(__APPLE__) || defined(__DARWIN__)
FUSE_ENABLE_XTIMES(conn);
#endif
#ifdef FUSE_CAP_DONT_MASK
/* request umask not to be enforced by fuse */
conn->want |= FUSE_CAP_DONT_MASK;
#endif /* defined FUSE_CAP_DONT_MASK */
#ifdef FUSE_CAP_BIG_WRITES
if (ctx->big_writes
&& ((ctx->vol->nr_clusters << ctx->vol->cluster_size_bits)
>= SAFE_CAPACITY_FOR_BIG_WRITES))
conn->want |= FUSE_CAP_BIG_WRITES;
#endif
#ifdef FUSE_CAP_IOCTL_DIR
conn->want |= FUSE_CAP_IOCTL_DIR;
#endif /* defined(FUSE_CAP_IOCTL_DIR) */
return NULL;
}
static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
{
int res = 0;
ntfs_inode *ni;
ntfs_attr *na;
char *path = NULL;
ntfschar *stream_name;
int stream_name_len;
BOOL withusermapping;
struct SECURITY_CONTEXT security;
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
if (stream_name_len < 0)
return stream_name_len;
memset(stbuf, 0, sizeof(struct stat));
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni) {
res = -errno;
goto exit;
}
withusermapping = ntfs_fuse_fill_security_context(&security);
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/*
* make sure the parent directory is searchable
*/
if (withusermapping
&& !ntfs_allowed_dir_access(&security,path,
(!strcmp(org_path,"/") ? ni : (ntfs_inode*)NULL),
ni, S_IEXEC)) {
res = -EACCES;
goto exit;
}
#endif
if (((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
|| (ni->flags & FILE_ATTR_REPARSE_POINT))
&& !stream_name_len) {
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
char *target;
int attr_size;
errno = 0;
target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr
_size);
/*
* If the reparse point is not a valid
* directory junction, and there is no error
* we still display as a symlink
*/
if (target || (errno == EOPNOTSUPP)) {
/* returning attribute size */
if (target)
stbuf->st_size = attr_size;
else
stbuf->st_size = sizeof(ntfs_bad_reparse
);
stbuf->st_blocks = (ni->allocated_size + 511) >>
9;
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_cou
nt);
stbuf->st_mode = S_IFLNK;
free(target);
} else {
res = -errno;
goto exit;
}
} else {
/* Directory. */
stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
/* get index size, if not known */
if (!test_nino_flag(ni, KnownSize)) {
na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTF
S_INDEX_I30, 4);
if (na) {
ni->data_size = na->data_size;
ni->allocated_size = na->allocated_size;
set_nino_flag(ni, KnownSize);
ntfs_attr_close(na);
}
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
stbuf->st_nlink = 1;
/* Make find(1) work */
}
} else {
/* Regular or Interix (INTX) file. */
stbuf->st_mode = S_IFREG;
stbuf->st_size = ni->data_size;
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
/*
* 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)
&& ni->data_size)
stbuf->st_size = ((ni->data_size + 511) & ~511) + 2;
#endif /* HAVE_SETXATTR */
/*
* Temporary fix to make ActiveSync work via Samba 3.0.
* See more on the ntfs-3g-devel list.
*/
stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);
if (ni->flags & FILE_ATTR_SYSTEM || stream_name_len) {
na = ntfs_attr_open(ni, AT_DATA, stream_name,
stream_name_len);
if (!na) {
if (stream_name_len) {
res = -ENOENT;
goto exit;
} else
goto nodata;
}
if (stream_name_len) {
stbuf->st_size = na->data_size;
stbuf->st_blocks = na->allocated_size >> 9;
}
/* Check whether it's Interix FIFO or socket. */
if (!(ni->flags & FILE_ATTR_HIDDEN) &&
!stream_name_len) {
/* FIFO. */
if (na->data_size == 0)
stbuf->st_mode = S_IFIFO;
/* Socket link. */
if (na->data_size == 1)
stbuf->st_mode = S_IFSOCK;
}
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
/* 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;
#endif /* HAVE_SETXATTR */
/*
* Check whether it's Interix symbolic link, block or
* character device.
*/
if ((u64)na->data_size <= sizeof(INTX_FILE_TYPES)
+ sizeof(ntfschar) * PATH_MAX
&& (u64)na->data_size >
sizeof(INTX_FILE_TYPES)
&& !stream_name_len) {
INTX_FILE *intx_file;
intx_file = ntfs_malloc(na->data_size);
if (!intx_file) {
res = -errno;
ntfs_attr_close(na);
goto exit;
}
if (ntfs_attr_pread(na, 0, na->data_size,
intx_file) != na->data_size) {
res = -errno;
free(intx_file);
ntfs_attr_close(na);
goto exit;
}
if (intx_file->magic == INTX_BLOCK_DEVICE &&
na->data_size == offsetof(
INTX_FILE, device_end)) {
stbuf->st_mode = S_IFBLK;
stbuf->st_rdev = makedev(le64_to_cpu(
intx_file->major),
le64_to_cpu(
intx_file->minor));
}
if (intx_file->magic == INTX_CHARACTER_DEVICE &&
na->data_size == offsetof(
INTX_FILE, device_end)) {
stbuf->st_mode = S_IFCHR;
stbuf->st_rdev = makedev(le64_to_cpu(
intx_file->major),
le64_to_cpu(
intx_file->minor));
}
if (intx_file->magic == INTX_SYMBOLIC_LINK)
stbuf->st_mode = S_IFLNK;
free(intx_file);
}
ntfs_attr_close(na);
}
stbuf->st_mode |= (0777 & ~ctx->fmask);
}
if (withusermapping) {
if (ntfs_get_owner_mode(&security,ni,stbuf) < 0)
set_fuse_error(&res);
} else {
stbuf->st_uid = ctx->uid;
stbuf->st_gid = ctx->gid;
}
if (S_ISLNK(stbuf->st_mode))
stbuf->st_mode |= 0777;
nodata :
stbuf->st_ino = ni->mft_no;
#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC
stbuf->st_atimespec = ntfs2timespec(ni->last_access_time);
stbuf->st_ctimespec = ntfs2timespec(ni->last_mft_change_time);
stbuf->st_mtimespec = ntfs2timespec(ni->last_data_change_time);
#elif defined(HAVE_STRUCT_STAT_ST_ATIM)
stbuf->st_atim = ntfs2timespec(ni->last_access_time);
stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time);
stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time);
#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
{
struct timespec ts;
ts = ntfs2timespec(ni->last_access_time);
stbuf->st_atime = ts.tv_sec;
stbuf->st_atimensec = ts.tv_nsec;
ts = ntfs2timespec(ni->last_mft_change_time);
stbuf->st_ctime = ts.tv_sec;
stbuf->st_ctimensec = ts.tv_nsec;
ts = ntfs2timespec(ni->last_data_change_time);
stbuf->st_mtime = ts.tv_sec;
stbuf->st_mtimensec = ts.tv_nsec;
}
#else
#warning "No known way to set nanoseconds in struct stat !"
{
struct timespec ts;
ts = ntfs2timespec(ni->last_access_time);
stbuf->st_atime = ts.tv_sec;
ts = ntfs2timespec(ni->last_mft_change_time);
stbuf->st_ctime = ts.tv_sec;
ts = ntfs2timespec(ni->last_data_change_time);
stbuf->st_mtime = ts.tv_sec;
}
#endif
exit:
if (ntfs_inode_close(ni))
set_fuse_error(&res);
free(path);
if (stream_name_len)
free(stream_name);
return res;
}
static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
{
char *path = NULL;
ntfschar *stream_name;
ntfs_inode *ni = NULL;
ntfs_attr *na = NULL;
INTX_FILE *intx_file = NULL;
int stream_name_len, res = 0;
/* Get inode. */
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
if (stream_name_len < 0)
return stream_name_len;
if (stream_name_len > 0) {
res = -EINVAL;
goto exit;
}
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni) {
res = -errno;
goto exit;
}
/*
* Reparse point : analyze as a junction point
*/
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
char *target;
int attr_size;
errno = 0;
res = 0;
target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
if (target) {
strncpy(buf,target,buf_size);
free(target);
} else
if (errno == EOPNOTSUPP)
strcpy(buf,ntfs_bad_reparse);
else
res = -errno;
goto exit;
}
/* Sanity checks. */
if (!(ni->flags & FILE_ATTR_SYSTEM)) {
res = -EINVAL;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
if (!na) {
res = -errno;
goto exit;
}
}
if (ntfs_fuse_is_named_data_stream(filename)) {
ntfs_log_error("Unable to access '%s' (inode %llu) with "
"current named streams access interface.\n",
filename, (unsigned long long)MREF(mref));
free(filename);
return 0;
} else {
struct stat st = { .st_ino = MREF(mref) };
switch (dt_type) {
case NTFS_DT_DIR :
st.st_mode = S_IFDIR | (0777 & ~ctx->dmask);
break;
case NTFS_DT_LNK :
st.st_mode = S_IFLNK | 0777;
break;
case NTFS_DT_FIFO :
st.st_mode = S_IFIFO;
break;
case NTFS_DT_SOCK :
st.st_mode = S_IFSOCK;
break;
case NTFS_DT_BLK :
st.st_mode = S_IFBLK;
break;
case NTFS_DT_CHR :
st.st_mode = S_IFCHR;
break;
default : /* unexpected types shown as plain files */
case NTFS_DT_REG :
st.st_mode = S_IFREG | (0777 & ~ctx->fmask);
break;
}
#if defined(__APPLE__) || defined(__DARWIN__)
/*
* Returning file names larger than MAXNAMLEN (255) bytes
* causes Darwin/Mac OS X to bug out and skip the entry.
*/
if (filenamelen > MAXNAMLEN) {
ntfs_log_debug("Truncating %d byte filename to "
"%d bytes.\n", filenamelen, MAXNAMLEN);
ntfs_log_debug(" before: '%s'\n", filename);
memset(filename + MAXNAMLEN, 0, filenamelen - MAXNAMLEN)
;
ntfs_log_debug(" after: '%s'\n", filename);
}
#elif defined(__sun) && defined (__SVR4)
/*
* Returning file names larger than MAXNAMELEN (256) bytes
* causes Solaris/Illumos to return an I/O error from the system
* call.
* However we also need space for a terminating NULL, or user
* space tools will bug out since they expect a NULL terminator.
* Effectively the maximum length of a file name is MAXNAMELEN * 1 (255).
*/
if (filenamelen > (MAXNAMELEN - 1)) {
if (!ni) {
res = -errno;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
res = -errno;
goto exit;
}
max_read = na->data_size;
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
/* limit reads at next 512 byte boundary for encrypted attributes */
if (ctx->efs_raw
&& max_read
&& (na->data_flags & ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na)) {
max_read = ((na->data_size+511) & ~511) + 2;
}
#endif /* HAVE_SETXATTR */
if (offset + (off_t)size > max_read) {
if (max_read < offset)
goto ok;
size = max_read - offset;
}
while (size > 0) {
s64 ret = ntfs_attr_pread(na, offset, size, buf + total);
if (ret != (s64)size)
ntfs_log_perror("ntfs_attr_pread error reading '%s' at "
"offset %lld: %lld <> %lld", org_path,
(long long)offset, (long long)size, (long long)r
et);
if (ret <= 0 || ret > (s64)size) {
res = (ret < 0) ? -errno : -EIO;
goto exit;
}
size -= ret;
offset += ret;
total += ret;
}
ok:
ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME);
res = total;
exit:
if (na)
ntfs_attr_close(na);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
free(path);
if (stream_name_len)
free(stream_name);
return res;
}
static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi __attribute__((unused)))
{
ntfs_inode *ni = NULL;
ntfs_attr *na = NULL;
char *path = NULL;
ntfschar *stream_name;
}
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
if (stream_name_len < 0) {
res = stream_name_len;
goto out;
}
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni) {
res = -errno;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
res = -errno;
goto exit;
}
res = 0;
if (fi->fh & CLOSE_DMTIME)
ntfs_inode_update_times(na->ni,NTFS_UPDATE_MCTIME);
if (fi->fh & CLOSE_COMPRESSED)
res = ntfs_attr_pclose(na);
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
if (fi->fh & CLOSE_ENCRYPTED)
res = ntfs_efs_fixup_attribute(NULL, na);
#endif /* HAVE_SETXATTR */
exit:
if (na)
ntfs_attr_close(na);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
free(path);
if (stream_name_len)
free(stream_name);
out:
return res;
}
/*
*
*/
if (stream_name_len < 0)
return stream_name_len;
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
goto exit;
/* deny truncating metadata files */
if (ni->mft_no < FILE_first_user) {
errno = EPERM;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na)
goto exit;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/*
* JPA deny truncation if cannot search in parent directory
* or cannot write to file (already checked for ftruncate())
*/
if (ntfs_fuse_fill_security_context(&security)
&& (!ntfs_allowed_dir_access(&security, path,
(ntfs_inode*)NULL, ni, S_IEXEC)
|| (chkwrite
&& !ntfs_allowed_access(&security, ni, S_IWRITE)))) {
errno = EACCES;
goto exit;
}
#endif
/*
* For compressed files, upsizing is done by inserting a final
* zero, which is optimized as creating a hole when possible.
*/
oldsize = na->data_size;
if ((na->data_flags & ATTR_COMPRESSION_MASK)
&& (size > na->initialized_size)) {
char zero = 0;
if (ntfs_attr_pwrite(na, size - 1, 1, &zero) <= 0)
goto exit;
} else
if (ntfs_attr_truncate(na, size))
goto exit;
if (oldsize != size)
set_archive(ni);
ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME);
errno = 0;
exit:
res = -errno;
ntfs_attr_close(na);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
free(path);
if (stream_name_len)
free(stream_name);
return res;
}
static int ntfs_fuse_truncate(const char *org_path, off_t size)
{
return ntfs_fuse_trunc(org_path, size, TRUE);
}
static int ntfs_fuse_ftruncate(const char *org_path, off_t size,
struct fuse_file_info *fi __attribute__((unused)))
{
/*
* in ->ftruncate() the file handle is guaranteed
* to have been opened for write.
*/
return (ntfs_fuse_trunc(org_path, size, FALSE));
}
static int ntfs_fuse_chmod(const char *path,
mode_t mode)
{
int res = 0;
ntfs_inode *ni;
struct SECURITY_CONTEXT security;
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
/*
* Return unsupported if no user mapping has been defined
* or enforcing Windows-type inheritance
*/
if (ctx->inherit
|| !ntfs_fuse_fill_security_context(&security)) {
if (ctx->silent)
res = 0;
else
res = -EOPNOTSUPP;
} else {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_allowed_dir_access(&security,path,
(ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
res = -errno;
else {
if (ntfs_set_mode(&security,ni,mode))
res = -errno;
else
ntfs_fuse_update_times(ni, NTFS_UPDATE_C
TIME);
NInoSetDirty(ni);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
} else
res = -errno;
#endif
}
return res;
}
static int ntfs_fuse_chown(const char *path, uid_t uid, gid_t gid)
{
ntfs_inode *ni;
int res;
struct SECURITY_CONTEXT security;
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
/*
* Return unsupported if no user mapping has been defined
* or enforcing Windows-type inheritance
*/
if (ctx->inherit
|| !ntfs_fuse_fill_security_context(&security)) {
if (ctx->silent)
return 0;
if (uid == ctx->uid && gid == ctx->gid)
return 0;
return -EOPNOTSUPP;
} else {
res = 0;
if (((int)uid != -1) || ((int)gid != -1)) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_allowed_dir_access(&security,path,
(ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path
);
if (!ni)
res = -errno;
else {
if (ntfs_set_owner(&security,
ni,uid,gid))
res = -errno;
else
ntfs_fuse_update_times(ni, NTFS_
UPDATE_CTIME);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
} else
res = -errno;
#endif
}
}
return (res);
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
static int ntfs_fuse_access(const char *path, int type)
{
int res = 0;
int mode;
ntfs_inode *ni;
struct SECURITY_CONTEXT security;
if (ntfs_fuse_is_named_data_stream(path))
case S_IFCHR:
case S_IFBLK:
ni = ntfs_create_device(dir_ni, securid,
uname, uname_len, type, dev);
break;
case S_IFLNK:
utarget_len = ntfs_mbstoucs(target, &utarget);
if (utarget_len < 0) {
res = -errno;
goto exit;
}
ni = ntfs_create_symlink(dir_ni, securid,
uname, uname_len,
utarget, utarget_len);
break;
default:
ni = ntfs_create(dir_ni, securid, uname,
uname_len, type);
break;
}
if (ni) {
/*
* set the security attribute if a security id
* could not be allocated (eg NTFS 1.x)
*/
if (ctx->security.mapping[MAPUSERS]) {
#if POSIXACLS
if (!securid
&& ntfs_set_inherited_posix(&security, ni,
security.uid, gid,
dir_ni, perm) < 0)
set_fuse_error(&res);
#else
if (!securid
&& ntfs_set_owner_mode(&security, ni,
security.uid, gid,
perm & ~security.umask) < 0)
set_fuse_error(&res);
#endif
}
set_archive(ni);
/* mark a need to compress the end of file */
if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) {
fi->fh |= CLOSE_COMPRESSED;
}
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
/* mark a future need to fixup encrypted inode */
if (fi
&& ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED))
fi->fh |= CLOSE_ENCRYPTED;
#endif /* HAVE_SETXATTR */
/* mark a need to update the mtime */
if (fi && ctx->dmtime)
fi->fh |= CLOSE_DMTIME;
NInoSetDirty(ni);
/*
* closing ni requires access to dir_ni to
* synchronize the index, avoid double opening.
*/
if (ntfs_inode_close_in_dir(ni, dir_ni))
set_fuse_error(&res);
ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME);
} else
res = -errno;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
} else
res = -errno;
#endif
free(path);
exit:
free(uname);
if (ntfs_inode_close(dir_ni))
set_fuse_error(&res);
if (utarget)
free(utarget);
free(dir_path);
return res;
}
static int ntfs_fuse_create_stream(const char *path,
ntfschar *stream_name, const int stream_name_len,
struct fuse_file_info *fi)
{
ntfs_inode *ni;
int res = 0;
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni) {
res = -errno;
if (res == -ENOENT) {
/*
* If such file does not exist, create it and try once
* again to add stream to it.
* Note : no fuse_file_info for creation of main file
*/
res = ntfs_fuse_create(path, S_IFREG, 0, NULL,
(struct fuse_file_info*)NULL);
if (!res)
return ntfs_fuse_create_stream(path,
stream_name, stream_name_len,fi)
;
else
res = -errno;
}
return res;
}
if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0))
res = -errno;
else
set_archive(ni);
if ((res >= 0)
&& fi
&& (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;
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
if (ntfs_fuse_is_named_data_stream(from))
return -EINVAL; /* n/a for named data streams. */
return ntfs_fuse_create(from, S_IFLNK, 0, to,
(struct fuse_file_info*)NULL);
}
static int ntfs_fuse_link(const char *old_path, const char *new_path)
{
char *name;
ntfschar *uname = NULL;
ntfs_inode *dir_ni = NULL, *ni;
char *path;
int res = 0, uname_len;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
BOOL samedir;
struct SECURITY_CONTEXT security;
#endif
if (ntfs_fuse_is_named_data_stream(old_path))
return -EINVAL; /* n/a for named data streams. */
if (ntfs_fuse_is_named_data_stream(new_path))
return -EINVAL; /* n/a for named data streams. */
path = strdup(new_path);
if (!path)
return -errno;
/* Open file for which create hard link. */
ni = ntfs_pathname_to_inode(ctx->vol, NULL, old_path);
if (!ni) {
res = -errno;
goto exit;
}
/* Generate unicode filename. */
name = strrchr(path, '/');
name++;
uname_len = ntfs_mbstoucs(name, &uname);
if ((uname_len < 0)
|| (ctx->windows_names
&& ntfs_forbidden_names(ctx->vol,uname,uname_len))) {
res = -errno;
goto exit;
}
/* Open parent directory. */
*--name = 0;
dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!dir_ni) {
res = -errno;
goto exit;
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
samedir = !strncmp(old_path, path, strlen(path))
&& (old_path[strlen(path)] == '/');
/* JPA make sure the parent directories are writeable */
if (ntfs_fuse_fill_security_context(&security)
&& ((!samedir && !ntfs_allowed_dir_access(&security,old_path,
(ntfs_inode*)NULL,ni,S_IWRITE + S_IEXEC))
|| !ntfs_allowed_access(&security,dir_ni,S_IWRITE + S_IEXEC)))
res = -EACCES;
else
#endif
{
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
res = -errno;
goto exit;
}
set_archive(ni);
ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME);
}
exit:
/*
* Must close dir_ni first otherwise ntfs_inode_sync_file_name(ni)
* may fail because ni may not be in parent's index on the disk yet.
*/
if (ntfs_inode_close(dir_ni))
set_fuse_error(&res);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
free(uname);
free(path);
return res;
}
static int ntfs_fuse_rm(const char *org_path)
{
char *name;
ntfschar *uname = NULL;
ntfs_inode *dir_ni = NULL, *ni;
char *path;
int res = 0, uname_len;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
path = strdup(org_path);
if (!path)
return -errno;
/* Open object for delete. */
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni) {
res = -errno;
goto exit;
}
/* deny unlinking metadata files */
if (ni->mft_no < FILE_first_user) {
errno = EPERM;
res = -errno;
goto exit;
}
/* Generate unicode filename. */
name = strrchr(path, '/');
name++;
uname_len = ntfs_mbstoucs(name, &uname);
if (uname_len < 0) {
res = -errno;
goto exit;
}
err:
ntfs_log_perror("Rename failed. Existing file '%s' was renamed "
"to '%s'", new_path, tmp);
} else {
cleanup:
/*
* Condition for this unlink has already been checked in
* "ntfs_fuse_rename_existing_dest()", so it should never
* fail (unless concurrent access to directories when fuse
* is multithreaded)
*/
if (ntfs_fuse_unlink(tmp) < 0)
ntfs_log_perror("Rename failed. Existing file '%s' still
present "
"as '%s'", new_path, tmp);
}
return ret;
}
static int ntfs_fuse_rename_existing_dest(const char *old_path, const char *new_
path)
{
int ret, len;
char *tmp;
const char *ext = ".ntfs-3g-";
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
ntfs_log_trace("Entering\n");
len = strlen(new_path) + strlen(ext) + 10 + 1; /* wc(str(2^32)) + \0 */
tmp = ntfs_malloc(len);
if (!tmp)
return -errno;
ret = snprintf(tmp, len, "%s%s%010d", new_path, ext, ++ntfs_sequence);
if (ret != len - 1) {
ntfs_log_error("snprintf failed: %d != %d\n", ret, len - 1);
ret = -EOVERFLOW;
} else {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/*
* Make sure existing dest can be removed.
* This is only needed if parent directory is
* sticky, because in this situation condition
* for unlinking is different from condition for
* linking
*/
if (!ntfs_fuse_fill_security_context(&security)
|| ntfs_allowed_dir_access(&security, new_path,
(ntfs_inode*)NULL, (ntfs_inode*)NULL,
S_IEXEC + S_IWRITE + S_ISVTX))
ret = ntfs_fuse_safe_rename(old_path, new_path, tmp);
else
ret = -EACCES;
#else
ret = ntfs_fuse_safe_rename(old_path, new_path, tmp);
#endif
}
free(tmp);
return ret;
}
static int ntfs_fuse_rename(const char *old_path, const char *new_path)
{
int ret, stream_name_len;
char *path = NULL;
ntfschar *stream_name;
ntfs_inode *ni;
u64 inum;
BOOL same;
ntfs_log_debug("rename: old: '%s' new: '%s'\n", old_path, new_path);
/*
* FIXME: Rename should be atomic.
*/
stream_name_len = ntfs_fuse_parse_path(new_path, &path, &stream_name);
if (stream_name_len < 0)
return stream_name_len;
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (ni) {
ret = ntfs_check_empty_dir(ni);
if (ret < 0) {
ret = -errno;
ntfs_inode_close(ni);
goto out;
}
inum = ni->mft_no;
if (ntfs_inode_close(ni)) {
set_fuse_error(&ret);
goto out;
}
free(path);
path = (char*)NULL;
if (stream_name_len)
free(stream_name);
/* silently ignore a rename to same inode */
stream_name_len = ntfs_fuse_parse_path(old_path,
&path, &stream_name);
if (stream_name_len < 0)
return stream_name_len;
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (ni) {
same = ni->mft_no == inum;
if (ntfs_inode_close(ni))
ret = -errno;
else
if (!same)
ret = ntfs_fuse_rename_existing_dest(
old_path, new_path);
} else
ret = -errno;
goto out;
}
ret = ntfs_fuse_link(old_path, new_path);
if (ret)
goto out;
ret = ntfs_fuse_unlink(old_path);
if (ret)
ntfs_fuse_unlink(new_path);
out:
free(path);
if (stream_name_len)
free(stream_name);
return ret;
}
static int ntfs_fuse_mkdir(const char *path,
mode_t mode)
{
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL,
(struct fuse_file_info*)NULL);
}
static int ntfs_fuse_rmdir(const char *path)
{
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
return ntfs_fuse_rm(path);
}
#ifdef HAVE_UTIMENSAT
static int ntfs_fuse_utimens(const char *path, const struct timespec tv[2])
{
ntfs_inode *ni;
int res = 0;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_fuse_fill_security_context(&security)
&& !ntfs_allowed_dir_access(&security,path,
(ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
return (-errno);
}
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
/* no check or update if both UTIME_OMIT */
if ((tv[0].tv_nsec != UTIME_OMIT) || (tv[1].tv_nsec != UTIME_OMIT)) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
if (ntfs_allowed_as_owner(&security, ni)
|| ((tv[0].tv_nsec == UTIME_NOW)
&& (tv[1].tv_nsec == UTIME_NOW)
&& ntfs_allowed_access(&security, ni, S_IWRITE))) {
#endif
ntfs_time_update_flags mask = NTFS_UPDATE_CTIME;
if (tv[0].tv_nsec == UTIME_NOW)
mask |= NTFS_UPDATE_ATIME;
else
if (tv[0].tv_nsec != UTIME_OMIT)
ni->last_access_time
= timespec2ntfs(tv[0]);
if (tv[1].tv_nsec == UTIME_NOW)
mask |= NTFS_UPDATE_MTIME;
else
if (tv[1].tv_nsec != UTIME_OMIT)
ni->last_data_change_time
= timespec2ntfs(tv[1]);
ntfs_inode_update_times(ni, mask);
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
} else
res = -errno;
#endif
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
#else /* HAVE_UTIMENSAT */
static int ntfs_fuse_utime(const char *path, struct utimbuf *buf)
{
ntfs_inode *ni;
int res = 0;
struct timespec actime;
struct timespec modtime;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
BOOL ownerok;
BOOL writeok;
struct SECURITY_CONTEXT security;
#endif
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_fuse_fill_security_context(&security)
&& !ntfs_allowed_dir_access(&security,path,
(ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
return (-errno);
}
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
ownerok = ntfs_allowed_as_owner(&security, ni);
if (buf) {
/*
* fuse never calls with a NULL buf and we do not
* know whether the specific condition can be applied
* So we have to accept updating by a non-owner having
* write access.
*/
writeok = !ownerok
&& (buf->actime == buf->modtime)
&& ntfs_allowed_access(&security, ni, S_IWRITE);
/* Must be owner */
if (!ownerok && !writeok)
res = (buf->actime == buf->modtime ? -EACCES : -EPERM);
else {
actime.tv_sec = buf->actime;
actime.tv_nsec = 0;
modtime.tv_sec = buf->modtime;
modtime.tv_nsec = 0;
ni->last_access_time = timespec2ntfs(actime);
ni->last_data_change_time = timespec2ntfs(modtime);
ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
}
} else {
/* Must be owner or have write access */
writeok = !ownerok
&& ntfs_allowed_access(&security, ni, S_IWRITE);
if (!ownerok && !writeok)
res = -EACCES;
else
ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);
}
#else
if (buf) {
actime.tv_sec = buf->actime;
actime.tv_nsec = 0;
modtime.tv_sec = buf->modtime;
modtime.tv_nsec = 0;
ni->last_access_time = timespec2ntfs(actime);
ni->last_data_change_time = timespec2ntfs(modtime);
ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
} else
ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);
#endif
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
#endif /* HAVE_UTIMENSAT */
static int ntfs_fuse_fsync(const char *path __attribute__((unused)),
int type __attribute__((unused)),
struct fuse_file_info *fi __attribute__((unused)))
{
int ret;
/* sync the full device */
ret = ntfs_device_sync(ctx->vol->dev);
if (ret)
ret = -errno;
return (ret);
}
#if defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28)
static int ntfs_fuse_ioctl(const char *path,
int cmd, void *arg,
struct fuse_file_info *fi __attribute__((unused)),
unsigned int flags, void *data)
{
ntfs_inode *ni;
int ret;
if (flags & FUSE_IOCTL_COMPAT)
return -ENOSYS;
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
ret = ntfs_ioctl(ni, cmd, arg, flags, data);
if (ntfs_inode_close (ni))
set_fuse_error(&ret);
return ret;
}
#endif /* defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) */
static int ntfs_fuse_bmap(const char *path, size_t blocksize, uint64_t *idx)
{
ntfs_inode *ni;
ntfs_attr *na;
LCN lcn;
int ret = 0;
int cl_per_bl = ctx->vol->cluster_size / blocksize;
if (blocksize > ctx->vol->cluster_size)
return -EINVAL;
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL;
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
if (!na) {
ret = -errno;
goto close_inode;
}
if ((na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED))
|| !NAttrNonResident(na)) {
ret = -EINVAL;
goto close_attr;
}
if (ntfs_attr_map_whole_runlist(na)) {
ret = -errno;
goto close_attr;
}
lcn = ntfs_rl_vcn_to_lcn(na->rl, *idx / cl_per_bl);
*idx = (lcn > 0) ? lcn * cl_per_bl + *idx % cl_per_bl : 0;
close_attr:
ntfs_attr_close(na);
close_inode:
if (ntfs_inode_close(ni))
set_fuse_error(&ret);
return ret;
}
#ifdef HAVE_SETXATTR
/*
*
*/
enum {
XATTRNS_NONE,
XATTRNS_USER,
XATTRNS_SYSTEM,
XATTRNS_SECURITY,
XATTRNS_TRUSTED,
XATTRNS_OPEN
} ;
/*
*
*
*
*
*
*/
&& foracl) {
if (ctx->silent)
errno = 0;
else
errno = EOPNOTSUPP;
} else {
/*
* parent directory must be executable, and
* for setting a DOS name it must be writeable
*/
if (setting && (attr == XATTR_NTFS_DOS_NAME))
acctype = S_IEXEC | S_IWRITE;
else
acctype = S_IEXEC;
if ((attr == XATTR_NTFS_DOS_NAME)
&& !strcmp(path,"/"))
/* forbid getting/setting names on root */
errno = EPERM;
else
if (ntfs_allowed_real_dir_access(security, path,
(ntfs_inode*)NULL ,acctype)) {
ni = ntfs_pathname_to_inode(ctx->vol,
NULL, path);
}
}
}
return (ni);
}
/*
*
*/
/*
*
*/
}
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
/* Return with no result for symlinks, fifo, etc. */
if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))
goto exit;
/* otherwise file must be readable */
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
if (!ntfs_allowed_access(&security,ni,S_IREAD)) {
ret = -EACCES;
goto exit;
}
#endif
actx = ntfs_attr_get_search_ctx(ni, NULL);
if (!actx) {
ret = -errno;
goto exit;
}
if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR)
|| (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) {
ret = ntfs_fuse_listxattr_common(ni, actx, list, size,
ctx->streams == NF_STREAMS_INTERFACE_XATTR);
if (ret < 0)
goto exit;
}
if (errno != ENOENT)
ret = -errno;
exit:
if (actx)
ntfs_attr_put_search_ctx(actx);
if (ntfs_inode_close(ni))
set_fuse_error(&ret);
return ret;
}
static int ntfs_fuse_getxattr_windows(const char *path, const char *name,
char *value, size_t size)
{
ntfs_attr_search_ctx *actx = NULL;
ntfs_inode *ni;
char *to = value;
int ret = 0;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
if (strcmp(name, "ntfs.streams.list"))
return -EOPNOTSUPP;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_fuse_fill_security_context(&security)
&& !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
(ntfs_inode*)NULL,S_IEXEC)) {
return (-errno);
}
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
if (!ntfs_allowed_access(&security,ni,S_IREAD)) {
ret = -errno;
goto exit;
}
#endif
actx = ntfs_attr_get_search_ctx(ni, NULL);
if (!actx) {
ret = -errno;
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)),
actx->attr->name_length, &tmp_name, 0);
if (tmp_name_len < 0) {
ret = -errno;
goto exit;
}
if (ret)
ret++; /* For space delimiter. */
ret += tmp_name_len;
if (size) {
if ((size_t)ret <= size) {
/* Don't add space to the beginning of line. */
if (to != value) {
*to = '\0';
to++;
}
strncpy(to, tmp_name, tmp_name_len);
to += tmp_name_len;
} else {
free(tmp_name);
ret = -ERANGE;
goto exit;
}
}
free(tmp_name);
}
if (errno != ENOENT)
ret = -errno;
exit:
if (actx)
ntfs_attr_put_search_ctx(actx);
if (ntfs_inode_close(ni))
set_fuse_error(&ret);
return ret;
}
static int ntfs_fuse_getxattr(const char *path, const char *name,
char *value, size_t size)
{
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfs_attr *na = NULL;
ntfschar *lename = NULL;
int res, lename_len;
s64 rsize;
enum SYSTEMXATTRS attr;
int namespace;
struct SECURITY_CONTEXT security;
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/*
* hijack internal data and ACL retrieval, whatever
* mode was selected for xattr (from the user's
* point of view, ACLs are not xattr)
*/
ni = ntfs_check_access_xattr(&security, path, attr, FALSE);
if (ni) {
if (ntfs_allowed_access(&security,ni,S_IREAD)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = get_parent_dir(path);
else
dir_ni = (ntfs_inode*)NULL;
res = ntfs_xattr_system_getxattr(&security,
attr, ni, dir_ni, value, size);
if (dir_ni && ntfs_inode_close(dir_ni))
set_fuse_error(&res);
} else {
res = -errno;
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
} else
res = -errno;
#else
/*
* Only hijack NTFS ACL retrieval if POSIX ACLS
* option is not selected
* Access control is done by fuse
*/
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);
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = get_parent_dir(path);
else
dir_ni = (ntfs_inode*)NULL;
res = ntfs_xattr_system_getxattr(&security,
attr, ni, dir_ni, value, size);
if (dir_ni && ntfs_inode_close(dir_ni))
set_fuse_error(&res);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
} else
res = -errno;
}
#endif
return (res);
}
if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS)
return ntfs_fuse_getxattr_windows(path, name, value, size);
if (ctx->streams == NF_STREAMS_INTERFACE_NONE)
return -EOPNOTSUPP;
namespace = xattr_namespace(name);
if (namespace == XATTRNS_NONE)
return -EOPNOTSUPP;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_fuse_fill_security_context(&security)
&& !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
(ntfs_inode*)NULL,S_IEXEC)) {
return (-errno);
}
/* trusted only readable by root */
if ((namespace == XATTRNS_TRUSTED)
&& security.uid)
return -ENODATA;
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
/* Return with no result for symlinks, fifo, etc. */
if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) {
res = -ENODATA;
goto exit;
}
/* otherwise file must be readable */
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
if (!ntfs_allowed_access(&security, ni, S_IREAD)) {
res = -errno;
goto exit;
}
#endif
lename_len = fix_xattr_prefix(name, namespace, &lename);
if (lename_len == -1) {
res = -errno;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
if (!na) {
res = -ENODATA;
goto exit;
}
rsize = na->data_size;
if (ctx->efs_raw
&& rsize
&& (na->data_flags & ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na))
rsize = ((na->data_size + 511) & ~511) + 2;
if (size) {
if (size >= (size_t)rsize) {
res = ntfs_attr_pread(na, 0, rsize, value);
if (res != rsize)
res = -errno;
} else
res = -ERANGE;
} else
res = rsize;
exit:
if (na)
ntfs_attr_close(na);
free(lename);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
static int ntfs_fuse_setxattr(const char *path, const char *name,
const char *value, size_t size, int flags)
{
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfs_attr *na = NULL;
ntfschar *lename = NULL;
int res, lename_len;
size_t total;
s64 part;
enum SYSTEMXATTRS attr;
int namespace;
struct SECURITY_CONTEXT security;
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/*
* hijack internal data and ACL setting, whatever
* mode was selected for xattr (from the user's
* point of view, ACLs are not xattr)
* Note : ctime updated on successful settings
*/
ni = ntfs_check_access_xattr(&security,path,attr,TRUE);
if (ni) {
if (ntfs_allowed_as_owner(&security,ni)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = get_parent_dir(path);
else
dir_ni = (ntfs_inode*)NULL;
res = ntfs_xattr_system_setxattr(&security,
attr, ni, dir_ni, value, size, flags);
/* never have to close dir_ni */
if (res)
res = -errno;
} else
res = -errno;
if (attr != XATTR_NTFS_DOS_NAME) {
if (!res)
ntfs_fuse_update_times(ni,
NTFS_UPDATE_CTIME);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
} else
res = -errno;
#else
/*
* Only hijack NTFS ACL setting 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 {
/* creation of a new name is not controlled by fuse */
if (attr == XATTR_NTFS_DOS_NAME)
ni = ntfs_check_access_xattr(&security,path,attr
,TRUE);
else
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,ni)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = get_parent_dir(path);
else
dir_ni = (ntfs_inode*)NULL;
res = ntfs_xattr_system_setxattr(&securi
ty,
attr,
size,
/* never have
if (res)
res =
} else
res = -errno;
if (attr != XATTR_NTFS_DOS_NAME) {
if (!res)
ntfs_fuse_update_times(ni,
NTFS_UPDATE_CTIME);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
} else
res = -errno;
}
#endif
return (res);
}
if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
&& (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR))
return -EOPNOTSUPP;
namespace = xattr_namespace(name);
if (namespace == XATTRNS_NONE)
return -EOPNOTSUPP;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_fuse_fill_security_context(&security)
&& !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL,
(ntfs_inode*)NULL,S_IEXEC)) {
return (-errno);
}
/* security and trusted only settable by root */
if (((namespace == XATTRNS_SECURITY)
|| (namespace == XATTRNS_TRUSTED))
&& security.uid)
return -EPERM;
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
switch (namespace) {
case XATTRNS_SECURITY :
case XATTRNS_TRUSTED :
if (security.uid) {
res = -EPERM;
goto exit;
}
break;
case XATTRNS_SYSTEM :
if (!ntfs_allowed_as_owner(&security,ni)) {
res = -EACCES;
goto exit;
}
break;
default :
/* User xattr not allowed for symlinks, fifo, etc. */
if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) {
res = -EPERM;
goto exit;
}
if (!ntfs_allowed_access(&security,ni,S_IWRITE)) {
res = -EACCES;
goto exit;
}
break;
}
#else
/* User xattr not allowed for symlinks, fifo, etc. */
if ((namespace == XATTRNS_USER)
&& (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) {
res = -EPERM;
goto exit;
}
#endif
lename_len = fix_xattr_prefix(name, namespace, &lename);
if ((lename_len == -1)
|| (ctx->windows_names
&& ntfs_forbidden_chars(lename,lename_len))) {
res = -errno;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
if (na && flags == XATTR_CREATE) {
res = -EEXIST;
goto exit;
}
if (!na) {
if (flags == XATTR_REPLACE) {
res = -ENODATA;
goto exit;
}
if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) {
res = -errno;
goto exit;
}
if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
set_archive(ni);
NInoFileNameSetDirty(ni);
}
na = ntfs_attr_open(ni, AT_DATA, lename, lename_len);
if (!na) {
res = -errno;
goto exit;
}
} else {
/* currently compressed streams can only be wiped out */
if (ntfs_attr_truncate(na, (s64)0 /* size */)) {
res = -errno;
goto exit;
}
}
total = 0;
res = 0;
if (size) {
do {
part = ntfs_attr_pwrite(na, total, size - total,
&value[total]);
if (part > 0)
total += part;
} while ((part > 0) && (total < size));
}
if ((total != size) || ntfs_attr_pclose(na))
res = -errno;
else {
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED)) {
if (ntfs_efs_fixup_attribute(NULL,na))
res = -errno;
}
}
if (!res) {
ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
set_archive(ni);
NInoFileNameSetDirty(ni);
}
}
exit:
if (na)
ntfs_attr_close(na);
free(lename);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
static int ntfs_fuse_removexattr(const char *path, const char *name)
{
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfschar *lename = NULL;
int res = 0, lename_len;
enum SYSTEMXATTRS attr;
int namespace;
struct SECURITY_CONTEXT security;
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
switch (attr) {
/*
* Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES
* is never allowed
*/
case XATTR_NTFS_ACL :
case XATTR_NTFS_ATTRIB :
case XATTR_NTFS_ATTRIB_BE :
case XATTR_NTFS_EFSINFO :
case XATTR_NTFS_TIMES :
case XATTR_NTFS_TIMES_BE :
case XATTR_NTFS_CRTIME :
case XATTR_NTFS_CRTIME_BE :
res = -EPERM;
break;
default :
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/*
* hijack internal data and ACL removal, whatever
* mode was selected for xattr (from the user's
* point of view, ACLs are not xattr)
* Note : ctime updated on successful settings
*/
ni = ntfs_check_access_xattr(&security,path,attr,TRUE);
if (ni) {
if (ntfs_allowed_as_owner(&security,ni)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = get_parent_dir(path);
else
dir_ni = (ntfs_inode*)NULL;
res = ntfs_xattr_system_removexattr(&sec
urity,
attr, ni, dir_ni);
/* never have to close dir_ni */
if (res)
res = -errno;
} else
res = -errno;
if (attr != XATTR_NTFS_DOS_NAME) {
if (!res)
ntfs_fuse_update_times(ni,
NTFS_UPDATE_CTIME);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
} else
res = -errno;
#else
/*
* Only hijack NTFS ACL setting if POSIX ACLS
* option is not selected
}
/* security and trusted only settable by root */
if (((namespace == XATTRNS_SECURITY)
|| (namespace == XATTRNS_TRUSTED))
&& security.uid)
return -EACCES;
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
switch (namespace) {
case XATTRNS_SECURITY :
case XATTRNS_TRUSTED :
if (security.uid) {
res = -EPERM;
goto exit;
}
break;
case XATTRNS_SYSTEM :
if (!ntfs_allowed_as_owner(&security,ni)) {
res = -EACCES;
goto exit;
}
break;
default :
/* User xattr not allowed for symlinks, fifo, etc. */
if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) {
res = -EPERM;
goto exit;
}
if (!ntfs_allowed_access(&security,ni,S_IWRITE)) {
res = -EACCES;
goto exit;
}
break;
}
#else
/* User xattr not allowed for symlinks, fifo, etc. */
if ((namespace == XATTRNS_USER)
&& (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) {
res = -EPERM;
goto exit;
}
#endif
lename_len = fix_xattr_prefix(name, namespace, &lename);
if (lename_len == -1) {
res = -errno;
goto exit;
}
if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) {
if (errno == ENOENT)
errno = ENODATA;
res = -errno;
}
if (!res) {
ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME);
if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
set_archive(ni);
NInoFileNameSetDirty(ni);
}
}
exit:
free(lename);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
#else
#if POSIXACLS
#error "Option inconsistency : POSIXACLS requires SETXATTR"
#endif
#endif /* HAVE_SETXATTR */
static void ntfs_close(void)
{
struct SECURITY_CONTEXT security;
if (!ctx)
return;
if (!ctx->vol)
return;
if (ctx->mounted) {
ntfs_log_info("Unmounting %s (%s)\n", opts.device,
ctx->vol->vol_name);
if (ntfs_fuse_fill_security_context(&security)) {
if (ctx->seccache && ctx->seccache->head.p_reads) {
ntfs_log_info("Permissions cache : %lu writes, "
"%lu reads, %lu.%1lu%% hits\n",
ctx->seccache->head.p_writes,
ctx->seccache->head.p_reads,
100 * ctx->seccache->head.p_hits
/ ctx->seccache->head.p_reads,
1000 * ctx->seccache->head.p_hits
/ ctx->seccache->head.p_reads % 10);
}
}
ntfs_close_secure(&security);
}
if (ntfs_umount(ctx->vol, FALSE))
ntfs_log_perror("Failed to close volume %s", opts.device);
ctx->vol = NULL;
}
static void ntfs_fuse_destroy2(void *unused __attribute__((unused)))
{
ntfs_close();
}
static struct fuse_operations ntfs_3g_ops = {
.getattr
= ntfs_fuse_getattr,
.readlink
= ntfs_fuse_readlink,
.readdir
= ntfs_fuse_readdir,
.open
= ntfs_fuse_open,
.release
= ntfs_fuse_release,
.read
= ntfs_fuse_read,
.write
= ntfs_fuse_write,
.truncate
= ntfs_fuse_truncate,
.ftruncate
= ntfs_fuse_ftruncate,
.statfs
= ntfs_fuse_statfs,
.chmod
= ntfs_fuse_chmod,
.chown
= ntfs_fuse_chown,
.create
= ntfs_fuse_create_file,
.mknod
= ntfs_fuse_mknod,
.symlink
= ntfs_fuse_symlink,
.link
= ntfs_fuse_link,
.unlink
= ntfs_fuse_unlink,
.rename
= ntfs_fuse_rename,
.mkdir
= ntfs_fuse_mkdir,
.rmdir
= ntfs_fuse_rmdir,
#ifdef HAVE_UTIMENSAT
.utimens
= ntfs_fuse_utimens,
#else
.utime
= ntfs_fuse_utime,
#endif
.fsync
= ntfs_fuse_fsync,
.fsyncdir
= ntfs_fuse_fsync,
.bmap
= ntfs_fuse_bmap,
.destroy
= ntfs_fuse_destroy2,
#if defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28)
.ioctl
= ntfs_fuse_ioctl,
#endif /* defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) */
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
.access
= ntfs_fuse_access,
.opendir
= ntfs_fuse_opendir,
#endif
#ifdef HAVE_SETXATTR
.getxattr
= ntfs_fuse_getxattr,
.setxattr
= ntfs_fuse_setxattr,
.removexattr
= ntfs_fuse_removexattr,
.listxattr
= ntfs_fuse_listxattr,
#endif /* HAVE_SETXATTR */
#if defined(__APPLE__) || defined(__DARWIN__)
/* MacFUSE extensions. */
.getxtimes
= ntfs_macfuse_getxtimes,
.setcrtime
= ntfs_macfuse_setcrtime,
.setbkuptime
= ntfs_macfuse_setbkuptime,
.setchgtime
= ntfs_macfuse_setchgtime,
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
.init
= ntfs_init
};
static int ntfs_fuse_init(void)
{
ctx = ntfs_calloc(sizeof(ntfs_fuse_context_t));
if (!ctx)
return -1;
*ctx = (ntfs_fuse_context_t) {
.uid
= getuid(),
.gid
= getgid(),
#if defined(linux)
.streams = NF_STREAMS_INTERFACE_XATTR,
#else
.streams = NF_STREAMS_INTERFACE_NONE,
#endif
.atime = ATIME_RELATIVE,
.silent = TRUE,
.recover = TRUE
};
return 0;
}
static int ntfs_open(const char *device)
{
unsigned long flags = 0;
if (!ctx->blkdev)
flags |= NTFS_MNT_EXCLUSIVE;
if (ctx->ro)
flags |= NTFS_MNT_RDONLY;
if (ctx->recover)
flags |= NTFS_MNT_RECOVER;
if (ctx->hiberfile)
flags |= NTFS_MNT_IGNORE_HIBERFILE;
ctx->vol = ntfs_mount(device, flags);
if (!ctx->vol) {
ntfs_log_perror("Failed to mount '%s'", device);
goto err_out;
}
if (ctx->sync && ctx->vol->dev)
NDevSetSync(ctx->vol->dev);
if (ctx->compression)
NVolSetCompression(ctx->vol);
else
NVolClearCompression(ctx->vol);
#ifdef HAVE_SETXATTR
/* archivers must see hidden files */
if (ctx->efs_raw)
ctx->hide_hid_files = FALSE;
#endif
if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files,
!ctx->hide_hid_files, ctx->hide_dot_files))
goto err_out;
ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na);
if (ctx->vol->free_clusters < 0) {
ntfs_log_perror("Failed to read NTFS $Bitmap");
goto err_out;
}
ctx->vol->free_mft_records = ntfs_get_nr_free_mft_records(ctx->vol);
if (ctx->vol->free_mft_records < 0) {
ntfs_log_perror("Failed to calculate free MFT records");
goto err_out;
}
if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) {
if (errno != EPERM)
goto err_out;
if (ntfs_fuse_rm("/hiberfil.sys"))
goto err_out;
}
errno = 0;
err_out:
return ntfs_volume_error(errno);
}
static void usage(void)
{
ntfs_log_info(usage_msg, EXEC_NAME, VERSION, FUSE_TYPE, fuse_version(),
4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING,
EXEC_NAME, ntfs_home);
}
#if defined(linux) || defined(__uClinux__)
static
"HINT:
"
"
"
mknod_dev_fuse("/dev/misc/fuse");
}
#endif
}
static fuse_fstype get_fuse_fstype(void)
{
char buf[256];
fuse_fstype fstype = FSTYPE_NONE;
FILE *f = fopen("/proc/filesystems", "r");
if (!f) {
ntfs_log_perror("Failed to open /proc/filesystems");
return FSTYPE_UNKNOWN;
}
while (fgets(buf, sizeof(buf), f)) {
if (strstr(buf, "fuseblk\n")) {
fstype = FSTYPE_FUSEBLK;
break;
}
if (strstr(buf, "fuse\n"))
fstype = FSTYPE_FUSE;
}
fclose(f);
return fstype;
}
static fuse_fstype load_fuse_module(void)
{
int i;
struct stat st;
pid_t pid;
const char *cmd = "/sbin/modprobe";
struct timespec req = { 0, 100000000 };
fuse_fstype fstype;
/* 100 msec */
}
#endif
static struct fuse_chan *try_fuse_mount(char *parsed_options)
{
struct fuse_chan *fc = NULL;
struct fuse_args margs = FUSE_ARGS_INIT(0, NULL);
/* The fuse_mount() options get modified, so we always rebuild it */
if ((fuse_opt_add_arg(&margs, EXEC_NAME) == -1 ||
fuse_opt_add_arg(&margs, "-o") == -1 ||
fuse_opt_add_arg(&margs, parsed_options) == -1)) {
ntfs_log_error("Failed to set FUSE options.\n");
goto free_args;
}
fc = fuse_mount(opts.mnt_point, &margs);
free_args:
fuse_opt_free_args(&margs);
return fc;
}
static int set_fuseblk_options(char **parsed_options)
{
char options[64];
long pagesize;
u32 blksize = ctx->vol->cluster_size;
pagesize = sysconf(_SC_PAGESIZE);
if (pagesize < 1)
pagesize = 4096;
if (blksize > (u32)pagesize)
blksize = pagesize;
snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize);
if (ntfs_strappend(parsed_options, options))
return -1;
return 0;
}
static struct fuse *mount_fuse(char *parsed_options)
{
struct fuse *fh = NULL;
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
ctx->fc = try_fuse_mount(parsed_options);
if (!ctx->fc)
return NULL;
if (fuse_opt_add_arg(&args, "") == -1)
goto err;
#if !CACHEING
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=0") ==
-1)
goto err;
#else
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=1") ==
-1)
goto err;
#endif
if (ctx->debug)
if (fuse_opt_add_arg(&args, "-odebug") == -1)
goto err;
fh = fuse_new(ctx->fc, &args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL);
if (!fh)
goto err;
if (fuse_set_signal_handlers(fuse_get_session(fh)))
goto err_destory;
out:
fuse_opt_free_args(&args);
return fh;
err_destory:
fuse_destroy(fh);
fh = NULL;
err:
fuse_unmount(opts.mnt_point, ctx->fc);
goto out;
}
static void setup_logging(char *parsed_options)
{
if (!ctx->no_detach) {
if (daemon(0, ctx->debug))
ntfs_log_error("Failed to daemonize.\n");
else if (!ctx->debug) {
#ifndef DEBUG
ntfs_log_set_handler(ntfs_log_handler_syslog);
/* Override default libntfs identify. */
openlog(EXEC_NAME, LOG_PID, LOG_DAEMON);
#endif
}
}
ctx->seccache = (struct PERMISSIONS_CACHE*)NULL;
ntfs_log_info("Version %s %s %d\n", VERSION, FUSE_TYPE, fuse_version());
if (strcmp(opts.arg_device,opts.device))
ntfs_log_info("Requested device %s canonicalized as %s\n",
opts.arg_device,opts.device);
ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n",
opts.device, (ctx->ro) ? "Read-Only" : "Read-Write",
ctx->vol->vol_name, ctx->vol->major_ver,
ctx->vol->minor_ver);
ntfs_log_info("Cmdline options: %s\n", opts.options ? opts.options : "")
;
ntfs_log_info("Mount options: %s\n", parsed_options);
}
int main(int argc, char *argv[])
{
char *parsed_options = NULL;
struct fuse *fh;
#if !(defined(__sun) && defined (__SVR4))
fuse_fstype fstype = FSTYPE_UNKNOWN;
#endif
if (getcwd(ctx->abs_mnt_point,
PATH_MAX - strlen(opts.mnt_point) - 1)) {
strcat(ctx->abs_mnt_point, "/");
strcat(ctx->abs_mnt_point, opts.mnt_point);
#if defined(__sun) && defined (__SVR4)
/* Solaris also wants the absolute mount point */
opts.mnt_point = ctx->abs_mnt_point;
#endif /* defined(__sun) && defined (__SVR4) */
}
}
}
if (!ctx->abs_mnt_point) {
err = NTFS_VOLUME_OUT_OF_MEMORY;
goto err_out;
}
ctx->security.uid = 0;
ctx->security.gid = 0;
if ((opts.mnt_point[0] == '/')
&& !stat(opts.mnt_point,&sbuf)) {
/* collect owner of mount point, useful for default mapping */
ctx->security.uid = sbuf.st_uid;
ctx->security.gid = sbuf.st_gid;
}
#if defined(linux) || defined(__uClinux__)
fstype = get_fuse_fstype();
err = NTFS_VOLUME_NO_PRIVILEGE;
if (restore_privs())
goto err_out;
if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN)
fstype = load_fuse_module();
create_dev_fuse();
if (drop_privs())
goto err_out;
#endif
if (stat(opts.device, &sbuf)) {
ntfs_log_perror("Failed to access '%s'", opts.device);
err = NTFS_VOLUME_NO_PRIVILEGE;
goto err_out;
}
#if !(defined(__sun) && defined (__SVR4))
/* Always use fuseblk for block devices unless it's surely missing. */
if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE))
ctx->blkdev = TRUE;
#endif
#ifndef FUSE_INTERNAL
if (getuid() && ctx->blkdev) {
ntfs_log_error("%s", unpriv_fuseblk_msg);
err = NTFS_VOLUME_NO_PRIVILEGE;
goto err2;
}
#endif
err = ntfs_open(opts.device);
if (err)
goto err_out;
/* Force read-only mount if the device was found read-only */
if (!ctx->ro && NVolReadOnly(ctx->vol)) {
ctx->ro = TRUE;
if (ntfs_strinsert(&parsed_options, ",ro"))
goto err_out;
}
/* We must do this after ntfs_open() to be able to set the blksize */
if (ctx->blkdev && set_fuseblk_options(&parsed_options))
goto err_out;
ctx->security.vol = ctx->vol;
ctx->vol->secure_flags = ctx->secure_flags;
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
ctx->vol->efs_raw = ctx->efs_raw;
#endif /* HAVE_SETXATTR */
/* JPA open $Secure, (whatever NTFS version !) */
/* to initialize security data */
if (ntfs_open_secure(ctx->vol) && (ctx->vol->major_ver >= 3))
failed_secure = "Could not open file $Secure";
if (!ntfs_build_mapping(&ctx->security,ctx->usermap_path,
(ctx->vol->secure_flags
& ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))
&& !ctx->inherit
&& !(ctx->vol->secure_flags & (1 << SECURITY_WANTED)))) {
#if POSIXACLS
/* use basic permissions if requested */
if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))
permissions_mode = "User mapping built, Posix ACLs not u
sed";
else {
permissions_mode = "User mapping built, Posix ACLs in us
e";
#if KERNELACLS
if (ntfs_strinsert(&parsed_options, ",default_permission
s,acl")) {
err = NTFS_VOLUME_SYNTAX_ERROR;
goto err_out;
}
#endif /* KERNELACLS */
}
#else /* POSIXACLS */
#if KERNELPERMS
if (!(ctx->vol->secure_flags
& ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) {
/*
* No explicit option but user mapping found
* force default security
*/
ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT);
if (ntfs_strinsert(&parsed_options, ",default_permission
s")) {
err = NTFS_VOLUME_SYNTAX_ERROR;
goto err_out;
}
}
#endif /* KERNELPERMS */
permissions_mode = "User mapping built";
#endif /* POSIXACLS */
ctx->dmask = ctx->fmask = 0;
} else {
ctx->security.uid = ctx->uid;
ctx->security.gid = ctx->gid;
/* same ownership/permissions for all files */
ctx->security.mapping[MAPUSERS] = (struct MAPPING*)NULL;
ctx->security.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
if ((ctx->vol->secure_flags & (1 << SECURITY_WANTED))
&& !(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) {
ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT);
if (ntfs_strinsert(&parsed_options, ",default_permission
s")) {
err = NTFS_VOLUME_SYNTAX_ERROR;
goto err_out;
}
}
if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) {
ctx->vol->secure_flags |= (1 << SECURITY_RAW);
permissions_mode = "Global ownership and permissions enf
orced";
} else {
ctx->vol->secure_flags &= ~(1 << SECURITY_RAW);
permissions_mode = "Ownership and permissions disabled";
}
}
if (ctx->usermap_path)
free (ctx->usermap_path);
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
xattr_mapping = ntfs_xattr_build_mapping(ctx->vol,
ctx->xattrmap_path);
ctx->vol->xattr_mapping = xattr_mapping;
/*
* Errors are logged, do not refuse mounting, it would be
* too difficult to fix the unmountable mapping file.
*/
if (ctx->xattrmap_path)
free(ctx->xattrmap_path);
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
fh = mount_fuse(parsed_options);
if (!fh) {
err = NTFS_VOLUME_FUSE_ERROR;
goto err_out;
}
ctx->mounted = TRUE;
#if defined(linux) || defined(__uClinux__)
if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE))
ntfs_log_info("%s", fuse26_kmod_msg);
#endif
setup_logging(parsed_options);
if (failed_secure)
ntfs_log_info("%s\n",failed_secure);
if (permissions_mode)
ntfs_log_info("%s, configuration type %d\n",permissions_mode,
4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING);
if ((ctx->vol->secure_flags & (1 << SECURITY_RAW))
&& !ctx->uid && ctx->gid)