Beruflich Dokumente
Kultur Dokumente
#endif
#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__), ... */
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
"compat.h"
"bitmap.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
/*
fuse_req_t req;
fuse_ino_t ino;
BOOL filled;
} ntfs_fuse_fill_context_t;
struct open_file {
struct open_file *next;
struct open_file *previous;
long long ghost;
fuse_ino_t ino;
fuse_ino_t parent;
int state;
} ;
enum {
CLOSE_GHOST = 1,
CLOSE_COMPRESSED = 2,
CLOSE_ENCRYPTED = 4,
CLOSE_DMTIME = 8
};
enum RM_TYPES {
RM_LINK,
RM_DIR,
RM_ANY,
} ;
static struct ntfs_options opts;
const char *EXEC_NAME = "lowntfs-3g";
static ntfs_fuse_context_t *ctx;
static u32 ntfs_sequence;
static const char ghostformat[] = ".ghost-ntfs-3g-%020llu";
static const char *usage_msg =
"\n"
"%s %s %s %d - Third Generation NTFS Driver\n"
"\t\tConfiguration type %d, "
#ifdef HAVE_SETXATTR
"XATTRS are on, "
#else
"XATTRS are off, "
#endif
#if POSIXACLS
"POSIX ACLS are on\n"
#else
"POSIX ACLS are off\n"
#endif
"\n"
"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
"Copyright (C) 2007-2016 Jean-Pierre Andre\n"
"Copyright (C) 2009 Erik Larsson\n"
"\n"
"Usage:
%s [-o option[,...]] <device|image_file> <mount_point>\n"
"\n"
"Options: ro (read-only mount), windows_names, uid=, gid=,\n"
"
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
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;
}
/*
*
*
*
*
*
*/
0;
0;
0;
= 0;
}
return (ctx->security.mapping[MAPUSERS] != (struct MAPPING*)NULL);
}
static u64 ntfs_fuse_inode_lookup(fuse_ino_t parent, const char *name)
{
u64 ino = (u64)-1;
u64 inum;
ntfs_inode *dir_ni;
/* Open target directory. */
dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
if (dir_ni) {
/* Lookup file */
inum = ntfs_inode_lookup_by_mbsname(dir_ni, name);
/* never return inodes 0 and 1 */
if (MREF(inum) <= 1) {
inum = (u64)-1;
errno = ENOENT;
}
if (ntfs_inode_close(dir_ni)
|| (inum == (u64)-1))
ino = (u64)-1;
else
ino = MREF(inum);
}
return (ino);
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/*
*
*
*
*
*
*
*
*/
*
* Returns 0 on success or -errno on error.
*/
static void ntfs_fuse_statfs(fuse_req_t req,
fuse_ino_t ino __attribute__((unused)))
{
struct statvfs sfs;
s64 size;
int delta_bits;
ntfs_volume *vol;
vol = ctx->vol;
if (vol) {
/*
* 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;
/* Free inodes on the free space */
delta_bits = vol->cluster_size_bits - vol->mft_record_size_bits;
if (delta_bits >= 0)
size <<= delta_bits;
else
size >>= -delta_bits;
/* Number of inodes at this point in time. */
sfs.f_files = (vol->mftbmp_na->allocated_size << 3) + size;
/* Free inodes available for all and for non-privileged processes. */
size += vol->free_mft_records;
if (size < 0)
size = 0;
sfs.f_ffree = sfs.f_favail = size;
/* Maximum length of filenames. */
sfs.f_namemax = NTFS_MAX_NAME_LEN;
fuse_reply_statfs(req, &sfs);
} else
fuse_reply_err(req, ENODEV);
}
{
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 = 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(void *userdata __attribute__((unused)),
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) */
}
static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni, struct stat *stbuf)
{
int res = 0;
ntfs_attr *na;
BOOL withusermapping;
memset(stbuf, 0, sizeof(struct stat));
withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL);
if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
|| (ni->flags & FILE_ATTR_REPARSE_POINT)) {
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_count);
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,
NTFS_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) {
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
if (!na) {
stbuf->st_ino = ni->mft_no;
goto nodata;
}
/* Check whether it's Interix FIFO or socket. */
if (!(ni->flags & FILE_ATTR_HIDDEN)) {
/* FIFO. */
if (na->data_size == 0)
stbuf->st_mode = S_IFIFO;
/* Socket link. */
if (na->data_size == 1)
stbuf->st_mode = S_IFSOCK;
}
/*
* 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)) {
INTX_FILE *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 == (s64)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 == (s64)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(scx,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:
return (res);
}
static void ntfs_fuse_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi __attribute__((unused)))
{
int res;
ntfs_inode *ni;
struct stat stbuf;
struct SECURITY_CONTEXT security;
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni)
res = -errno;
else {
ntfs_fuse_fill_security_context(req, &security);
res = ntfs_fuse_getstat(&security, ni, &stbuf);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
if (!res)
fuse_reply_attr(req, &stbuf, ATTR_TIMEOUT);
else
fuse_reply_err(req, -res);
}
static __inline__ BOOL ntfs_fuse_fillstat(struct SECURITY_CONTEXT *scx,
struct fuse_entry_param *pentry, u64 iref)
{
ntfs_inode *ni;
BOOL ok = FALSE;
pentry->ino = MREF(iref);
ni = ntfs_inode_open(ctx->vol, pentry->ino);
if (ni) {
if (!ntfs_fuse_getstat(scx, ni, &pentry->attr)) {
pentry->generation = 1;
pentry->attr_timeout = ATTR_TIMEOUT;
pentry->entry_timeout = ENTRY_TIMEOUT;
ok = TRUE;
}
if (ntfs_inode_close(ni))
ok = FALSE;
}
return (ok);
}
res = -errno;
goto exit;
}
/*
* Reparse point : analyze as a junction point
*/
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
int attr_size;
errno = 0;
res = 0;
buf = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
if (!buf) {
if (errno == EOPNOTSUPP)
buf = strdup(ntfs_bad_reparse);
if (!buf)
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 ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)) {
res = -EINVAL;
goto exit;
}
if ((size_t)na->data_size > sizeof(INTX_FILE_TYPES) +
sizeof(ntfschar) * PATH_MAX) {
res = -ENAMETOOLONG;
goto exit;
}
/* Receive file content. */
intx_file = (INTX_FILE*)ntfs_malloc(na->data_size);
if (!intx_file) {
res = -errno;
goto exit;
}
if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) {
res = -errno;
goto exit;
}
/* Sanity check. */
if (intx_file->magic != INTX_SYMBOLIC_LINK) {
res = -EINVAL;
goto exit;
}
/* Convert link from unicode to local encoding. */
if (ntfs_ucstombs(intx_file->target, (na->data_size offsetof(INTX_FILE, target)) / sizeof(ntfschar),
&buf, 0) < 0) {
res = -errno;
goto exit;
}
exit:
if (intx_file)
free(intx_file);
if (na)
ntfs_attr_close(na);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_readlink(req, buf);
if (buf != ntfs_bad_reparse)
free(buf);
}
static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
const ntfschar *name, const int name_len, const int name_type,
const s64 pos __attribute__((unused)), const MFT_REF mref,
const unsigned dt_type __attribute__((unused)))
{
char *filename = NULL;
int ret = 0;
int filenamelen = -1;
size_t sz;
ntfs_fuse_fill_item_t *current;
ntfs_fuse_fill_item_t *newone;
if (name_type == FILE_NAME_DOS)
return 0;
if ((filenamelen = ntfs_ucstombs(name, name_len, &filename, 0)) < 0) {
ntfs_log_perror("Filename decoding failed (inode %llu)",
(unsigned long long)MREF(mref));
return -1;
}
/* never return inodes 0 and 1 */
if (MREF(mref) > 1) {
struct stat st = { .st_ino = MREF(mref) };
switch (dt_type) {
case NTFS_DT_DIR :
st.st_mode =
break;
case NTFS_DT_LNK :
st.st_mode =
break;
case NTFS_DT_FIFO :
st.st_mode =
break;
case NTFS_DT_SOCK :
st.st_mode =
break;
case NTFS_DT_BLK :
st.st_mode =
break;
case NTFS_DT_CHR :
st.st_mode =
break;
if (!sz) {
errno = EIO;
ntfs_log_error("Could not add a"
" directory entry (inode %lld)\n
",
(unsigned long long)MREF(mref));
}
} else {
sz = 0;
errno = ENOMEM;
}
}
if (sz) {
current->off += sz;
} else {
ret = -1;
}
}
free(filename);
return ret;
}
static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
int res = 0;
ntfs_inode *ni;
int accesstype;
ntfs_fuse_fill_context_t *fill;
struct SECURITY_CONTEXT security;
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (ni) {
if (ntfs_fuse_fill_security_context(req, &security)) {
if (fi->flags & O_WRONLY)
accesstype = S_IWRITE;
else
if (fi->flags & O_RDWR)
accesstype = S_IWRITE | S_IREAD;
else
accesstype = S_IREAD;
if (!ntfs_allowed_access(&security,ni,accesstype))
res = -EACCES;
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
if (!res) {
fill = (ntfs_fuse_fill_context_t*)
ntfs_malloc(sizeof(ntfs_fuse_fill_context_t));
if (!fill)
res = -errno;
else {
fill->first = fill->last
= (ntfs_fuse_fill_item_t*)NULL;
fill->filled = FALSE;
fill->ino = ino;
fill->off = 0;
}
fi->fh = (long)fill;
}
} else
res = -errno;
if (!res)
fuse_reply_open(req, fi);
else
fuse_reply_err(req, -res);
}
static void ntfs_fuse_releasedir(fuse_req_t req,
fuse_ino_t ino __attribute__((unused)),
struct fuse_file_info *fi)
{
ntfs_fuse_fill_context_t *fill;
ntfs_fuse_fill_item_t *current;
fill = (ntfs_fuse_fill_context_t*)(long)fi->fh;
if (fill && (fill->ino == ino)) {
/* make sure to clear results */
current = fill->first;
while (current) {
current = current->next;
free(fill->first);
fill->first = current;
}
fill->ino = 0;
free(fill);
}
fuse_reply_err(req, 0);
}
static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off __attribute__((unused)),
struct fuse_file_info *fi __attribute__((unused)))
{
ntfs_fuse_fill_item_t *first;
ntfs_fuse_fill_item_t *current;
ntfs_fuse_fill_context_t *fill;
ntfs_inode *ni;
s64 pos = 0;
int err = 0;
fill = (ntfs_fuse_fill_context_t*)(long)fi->fh;
if (fill && (fill->ino == ino)) {
if (fill->filled && !off) {
/* Rewinding : make sure to clear existing results */
current = fill->first;
while (current) {
current = current->next;
free(fill->first);
fill->first = current;
}
fill->filled = FALSE;
}
if (!fill->filled) {
/* initial call : build the full list */
current = (ntfs_fuse_fill_item_t*)NULL;
first = (ntfs_fuse_fill_item_t*)ntfs_malloc
(sizeof(ntfs_fuse_fill_item_t) + size);
if (first) {
first->bufsize = size;
first->off = 0;
first->next = (ntfs_fuse_fill_item_t*)NULL;
fill->req = req;
fill->first = first;
fill->last = first;
fill->off = 0;
ni = ntfs_inode_open(ctx->vol,INODE(ino));
if (!ni)
err = -errno;
else {
if (ntfs_readdir(ni, &pos, fill,
(ntfs_filldir_t)
ntfs_fuse_filler))
err = -errno;
fill->filled = TRUE;
ntfs_fuse_update_times(ni,
NTFS_UPDATE_ATIME);
if (ntfs_inode_close(ni))
set_fuse_error(&err);
}
if (!err) {
off_t loc = 0;
/*
* In some circumstances, the queue gets
* reinitialized by releasedir() + opendir(),
* apparently always on end of partial buffer.
* Files may be missing or duplicated.
*/
while (first
&& ((loc < off) || !first->off)) {
loc += first->off;
fill->first = first->next;
free(first);
first = fill->first;
}
current = first;
}
} else
err = -errno;
} else {
/* subsequent call : return next non-empty buffer */
current = fill->first;
while (current && !current->off) {
current = current->next;
free(fill->first);
fill->first = current;
}
}
if (!err) {
if (current) {
fuse_reply_buf(req, current->buf, current->off);
fill->first = current->next;
free(current);
} else {
fuse_reply_buf(req, (char*)NULL, 0);
/* reply sent, now must exit with no error */
}
}
} else {
errno = EIO;
err = -errno;
ntfs_log_error("Uninitialized fuse_readdir()\n");
}
if (err)
fuse_reply_err(req, -err);
}
static void ntfs_fuse_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
ntfs_inode *ni;
ntfs_attr *na;
struct open_file *of;
int state = 0;
char *path = NULL;
int res = 0;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
int accesstype;
struct SECURITY_CONTEXT security;
#endif
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (ni) {
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
if (na) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
if (ntfs_fuse_fill_security_context(req, &security)) {
if (fi->flags & O_WRONLY)
accesstype = S_IWRITE;
else
if (fi->flags & O_RDWR)
accesstype = S_IWRITE | S_IREAD;
else
accesstype = S_IREAD;
/* check whether requested access is allowed */
if (!ntfs_allowed_access(&security,
ni,accesstype))
res = -EACCES;
}
#endif
if ((res >= 0)
&& (fi->flags & (O_WRONLY | O_RDWR))) {
/* mark a future need to compress the last chunk */
if (na->data_flags & ATTR_COMPRESSION_MASK)
state |= CLOSE_COMPRESSED;
#ifdef HAVE_SETXATTR
/* extended attributes interface required */
/* mark a future need to fixup encrypted inode */
if (ctx->efs_raw
&& !(na->data_flags & ATTR_IS_ENCRYPTED)
&& (ni->flags & FILE_ATTR_ENCRYPTED))
state |= CLOSE_ENCRYPTED;
#endif /* HAVE_SETXATTR */
/* mark a future need to update the mtime */
if (ctx->dmtime)
state |= CLOSE_DMTIME;
/* deny opening metadata files for writing */
if (ino < FILE_first_user)
res = -EPERM;
}
ntfs_attr_close(na);
} else
res = -errno;
if (ntfs_inode_close(ni))
set_fuse_error(&res);
} else
res = -errno;
free(path);
if (res >= 0) {
of = (struct open_file*)malloc(sizeof(struct open_file));
if (of) {
of->parent = 0;
of->ino = ino;
of->state = state;
of->next = ctx->open_files;
of->previous = (struct open_file*)NULL;
if (ctx->open_files)
ctx->open_files->previous = of;
ctx->open_files = of;
fi->fh = (long)of;
}
}
if (res)
fuse_reply_err(req, -res);
else
fuse_reply_open(req, fi);
}
static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset,
struct fuse_file_info *fi __attribute__((unused)))
{
ntfs_inode *ni = NULL;
ntfs_attr *na = NULL;
int res;
char *buf = (char*)NULL;
s64 total = 0;
s64 max_read;
if (!size) {
res = 0;
goto exit;
}
buf = (char*)ntfs_malloc(size);
if (!buf) {
res = -errno;
goto exit;
}
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
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 inode %ll
d at "
"offset %lld: %lld <> %lld", (long long)ni->mft_
no,
(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);
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_buf(req, buf, res);
free(buf);
}
static void ntfs_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
size_t size, off_t offset,
struct fuse_file_info *fi __attribute__((unused)))
{
ntfs_inode *ni = NULL;
ntfs_attr *na = NULL;
int res, total = 0;
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
goto exit;
}
*/
res = ntfs_fuse_getstat(scx, ni, stbuf);
}
NInoSetDirty(ni);
} else
res = ntfs_fuse_getstat(scx, ni, stbuf);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
}
return res;
}
static int ntfs_fuse_chown(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
uid_t uid, gid_t gid, struct stat *stbuf)
{
ntfs_inode *ni;
int res;
/* Unsupported if inherit or no user mapping has been defined */
if ((!scx->mapping[MAPUSERS] || ctx->inherit)
&& !ctx->silent
&& ((uid != ctx->uid) || (gid != ctx->gid)))
res = -EOPNOTSUPP;
else {
res = 0;
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni)
res = -errno;
else {
/* ignore if Windows inheritance is forced */
if (scx->mapping[MAPUSERS]
&& !ctx->inherit
&& (((int)uid != -1) || ((int)gid != -1))) {
if (ntfs_set_owner(scx, ni, uid, gid))
res = -errno;
else {
ntfs_fuse_update_times(ni,
NTFS_UPDATE_CTIME);
/*
* Must return updated times, and
* inode has been updated, so hope
* we get no further errors
*/
res = ntfs_fuse_getstat(scx, ni, stbuf);
}
} else
res = ntfs_fuse_getstat(scx, ni, stbuf);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
}
return (res);
}
static int ntfs_fuse_chownmod(struct SECURITY_CONTEXT *scx, fuse_ino_t ino,
uid_t uid, gid_t gid, mode_t mode, struct stat *stbuf)
{
ntfs_inode *ni;
int res;
else {
actime.tv_sec = stin->st_atime;
actime.tv_nsec = 0;
modtime.tv_sec = stin->st_mtime;
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(scx, ni, S_IWRITE);
if (!ownerok && !writeok)
res = -EACCES;
else
ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);
}
#else
if (stin) {
actime.tv_sec = stin->st_atime;
actime.tv_nsec = 0;
modtime.tv_sec = stin->st_mtime;
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
res = ntfs_fuse_getstat(scx, ni, stbuf);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
#endif /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */
static void ntfs_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int to_set, struct fuse_file_info *fi __attribute__((un
used)))
{
struct stat stbuf;
ntfs_inode *ni;
int res;
struct SECURITY_CONTEXT security;
res = 0;
ntfs_fuse_fill_security_context(req, &security);
/* no flags */
if (!(to_set
& (FUSE_SET_ATTR_MODE
| FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID
| FUSE_SET_ATTR_SIZE
| FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni)
res = -errno;
else {
fuse_reply_err(req, -res);
else
fuse_reply_attr(req, &stbuf, ATTR_TIMEOUT);
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
static void ntfs_fuse_access(fuse_req_t req, fuse_ino_t ino, int mask)
{
int res = 0;
int mode;
ntfs_inode *ni;
struct SECURITY_CONTEXT security;
/* JPA return unsupported if no user mapping has been defined */
if (!ntfs_fuse_fill_security_context(req, &security)) {
if (ctx->silent)
res = 0;
else
res = -EOPNOTSUPP;
} else {
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
} else {
mode = 0;
if (mask & (X_OK | W_OK | R_OK)) {
if (mask & X_OK) mode += S_IEXEC;
if (mask & W_OK) mode += S_IWRITE;
if (mask & R_OK) mode += S_IREAD;
if (!ntfs_allowed_access(&security,
ni, mode))
res = -errno;
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
}
}
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_err(req, 0);
}
#endif /* !KERNELPERMS | (POSIXACLS & !KERNELACLS) */
static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t typemode, dev_t dev,
struct fuse_entry_param *e,
const char *target, struct fuse_file_info *fi)
{
ntfschar *uname = NULL, *utarget = NULL;
ntfs_inode *dir_ni = NULL, *ni;
struct open_file *of;
int state = 0;
le32 securid;
gid_t gid;
mode_t dsetgid;
mode_t type = typemode & ~07777;
mode_t perm;
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)) {
state |= 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))
state |= CLOSE_ENCRYPTED;
#endif /* HAVE_SETXATTR */
if (fi && ctx->dmtime)
state |= CLOSE_DMTIME;
ntfs_inode_update_mbsname(dir_ni, name, ni->mft_no);
NInoSetDirty(ni);
e->ino = ni->mft_no;
e->generation = 1;
e->attr_timeout = ATTR_TIMEOUT;
e->entry_timeout = ENTRY_TIMEOUT;
res = ntfs_fuse_getstat(&security, ni, &e->attr);
/*
* 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
exit:
free(uname);
if (ntfs_inode_close(dir_ni))
set_fuse_error(&res);
if (utarget)
free(utarget);
if ((res >= 0) && fi) {
of = (struct open_file*)malloc(sizeof(struct open_file));
if (of) {
of->parent = 0;
of->ino = e->ino;
of->state = state;
of->next = ctx->open_files;
of->previous = (struct open_file*)NULL;
if (ctx->open_files)
ctx->open_files->previous = of;
ctx->open_files = of;
fi->fh = (long)of;
}
}
return res;
}
static void ntfs_fuse_create_file(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode,
struct fuse_file_info *fi)
{
int res;
struct fuse_entry_param entry;
res = ntfs_fuse_create(req, parent, name, mode & (S_IFMT | 07777),
0, &entry, NULL, fi);
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_create(req, &entry, fi);
}
static void ntfs_fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, dev_t rdev)
{
int res;
struct fuse_entry_param e;
res = ntfs_fuse_create(req, parent, name, mode & (S_IFMT | 07777),
rdev, &e,NULL,(struct fuse_file_info*)NULL);
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_entry(req, &e);
}
static void ntfs_fuse_symlink(fuse_req_t req, const char *target,
fuse_ino_t parent, const char *name)
{
int res;
struct fuse_entry_param entry;
res = ntfs_fuse_create(req, parent, name, S_IFLNK, 0,
&entry, target, (struct fuse_file_info*)NULL);
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_entry(req, &entry);
}
static int ntfs_fuse_newlink(fuse_req_t req __attribute__((unused)),
fuse_ino_t ino, fuse_ino_t newparent,
const char *newname, struct fuse_entry_param *e)
{
ntfschar *uname = NULL;
ntfs_inode *dir_ni = NULL, *ni;
int res = 0, uname_len;
struct SECURITY_CONTEXT security;
/* Open file for which create hard link. */
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
goto exit;
}
/* Do not accept linking to a directory (except for renaming) */
if (e && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
errno = EPERM;
res = -errno;
goto exit;
}
/* Generate unicode filename. */
uname_len = ntfs_mbstoucs(newname, &uname);
if ((uname_len < 0)
|| (ctx->windows_names
&& ntfs_forbidden_names(ctx->vol,uname,uname_len))) {
res = -errno;
goto exit;
}
/* Open parent directory. */
dir_ni = ntfs_inode_open(ctx->vol, INODE(newparent));
if (!dir_ni) {
res = -errno;
goto exit;
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* make sure the target parent directory is writeable */
if (ntfs_fuse_fill_security_context(req, &security)
&& !ntfs_allowed_access(&security,dir_ni,S_IWRITE + S_IEXEC))
res = -EACCES;
else
#else
ntfs_fuse_fill_security_context(req, &security);
#endif
{
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
res = -errno;
goto exit;
}
ntfs_inode_update_mbsname(dir_ni, newname, ni->mft_no);
if (e) {
e->ino = ni->mft_no;
e->generation = 1;
e->attr_timeout = ATTR_TIMEOUT;
e->entry_timeout = ENTRY_TIMEOUT;
res = ntfs_fuse_getstat(&security, ni, &e->attr);
}
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);
return (res);
}
static void ntfs_fuse_link(fuse_req_t req, fuse_ino_t ino,
fuse_ino_t newparent, const char *newname)
{
struct fuse_entry_param entry;
int res;
res = ntfs_fuse_newlink(req, ino, newparent, newname, &entry);
if (res)
fuse_reply_err(req, -res);
else
fuse_reply_entry(req, &entry);
}
static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name,
enum RM_TYPES rm_type __attribute__((unused)))
{
ntfschar *uname = NULL;
ntfschar *ugname;
ntfs_inode *dir_ni = NULL, *ni = NULL;
int res = 0, uname_len;
int ugname_len;
u64 iref;
fuse_ino_t ino;
struct open_file *of;
char ghostname[GHOSTLTH];
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
/* Open parent directory. */
dir_ni = ntfs_inode_open(ctx->vol, INODE(parent));
if (!dir_ni) {
res = -errno;
goto exit;
}
/* Generate unicode filename. */
uname_len = ntfs_mbstoucs(name, &uname);
if (uname_len < 0) {
res = -errno;
goto exit;
}
/* Open object for delete. */
iref = ntfs_inode_lookup_by_mbsname(dir_ni, name);
if (iref == (u64)-1) {
res = -errno;
goto exit;
}
ino = (fuse_ino_t)MREF(iref);
/* deny unlinking metadata files */
if (ino < FILE_first_user) {
res = -EPERM;
goto exit;
}
ni = ntfs_inode_open(ctx->vol, ino);
if (!ni) {
res = -errno;
goto exit;
}
#if defined(__sun) && defined (__SVR4)
/* on Solaris : deny unlinking directories */
if (rm_type
== (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? RM_LINK : RM_DIR)) {
errno = EPERM;
res = -errno;
goto exit;
}
#endif /* defined(__sun) && defined (__SVR4) */
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* JPA deny unlinking if directory is not writable and executable */
if (ntfs_fuse_fill_security_context(req, &security)
&& !ntfs_allowed_dir_access(&security, dir_ni, ino, ni,
S_IEXEC + S_IWRITE + S_ISVTX)) {
errno = EACCES;
res = -errno;
goto exit;
}
#endif
/*
* We keep one open_file record per opening, to avoid
* having to check the list of open files when opening
* and closing (which are more frequent than unlinking).
* As a consequence, we may have to create several
* ghosts names for the same file.
* The file may have been opened with a different name
if (!ni) {
res = -errno;
goto exit;
}
}
if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni,
uname, uname_len))
res = -errno;
/* ntfs_delete() always closes ni and dir_ni */
ni = dir_ni = NULL;
exit:
if (ntfs_inode_close(ni) && !res)
res = -errno;
if (ntfs_inode_close(dir_ni) && !res)
res = -errno;
out :
free(uname);
return res;
}
static void ntfs_fuse_unlink(fuse_req_t req, fuse_ino_t parent,
const char *name)
{
int res;
res = ntfs_fuse_rm(req, parent, name, RM_LINK);
if (res)
fuse_reply_err(req, -res);
else
fuse_reply_err(req, 0);
}
static int ntfs_fuse_safe_rename(fuse_req_t req, fuse_ino_t ino,
fuse_ino_t parent, const char *name, fuse_ino_t xino,
fuse_ino_t newparent, const char *newname,
const char *tmp)
{
int ret;
ntfs_log_trace("Entering\n");
ret = ntfs_fuse_newlink(req, xino, newparent, tmp,
(struct fuse_entry_param*)NULL);
if (ret)
return ret;
ret = ntfs_fuse_rm(req, newparent, newname, RM_ANY);
if (!ret) {
ret = ntfs_fuse_newlink(req, ino, newparent, newname,
(struct fuse_entry_param*)NULL);
if (ret)
goto restore;
ret = ntfs_fuse_rm(req, parent, name, RM_ANY);
if (ret) {
if (ntfs_fuse_rm(req, newparent, newname, RM_ANY))
goto err;
goto restore;
}
}
goto cleanup;
restore:
if (ntfs_fuse_newlink(req, xino, newparent, newname,
(struct fuse_entry_param*)NULL)) {
err:
ntfs_log_perror("Rename failed. Existing file '%s' was renamed "
"to '%s'", newname, 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_rm(req, newparent, tmp, RM_ANY) < 0)
ntfs_log_perror("Rename failed. Existing file '%s' still
present "
"as '%s'", newname, tmp);
}
return ret;
}
static int ntfs_fuse_rename_existing_dest(fuse_req_t req, fuse_ino_t ino,
fuse_ino_t parent, const char *name,
fuse_ino_t xino, fuse_ino_t newparent,
const char *newname)
{
int ret, len;
char *tmp;
const char *ext = ".ntfs-3g-";
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
ntfs_inode *newdir_ni;
struct SECURITY_CONTEXT security;
#endif
ntfs_log_trace("Entering\n");
len = strlen(newname) + strlen(ext) + 10 + 1; /* wc(str(2^32)) + \0 */
tmp = (char*)ntfs_malloc(len);
if (!tmp)
return -errno;
ret = snprintf(tmp, len, "%s%s%010d", newname, 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
*/
newdir_ni = ntfs_inode_open(ctx->vol, INODE(newparent));
if (newdir_ni) {
if (!ntfs_fuse_fill_security_context(req,&security)
|| ntfs_allowed_dir_access(&security, newdir_ni,
xino, (ntfs_inode*)NULL,
S_IEXEC + S_IWRITE + S_ISVTX)) {
if (ntfs_inode_close(newdir_ni))
ret = -errno;
else
ret = ntfs_fuse_safe_rename(req, ino,
parent, name, xino,
newparent, newname,
tmp);
} else {
ntfs_inode_close(newdir_ni);
ret = -EACCES;
}
} else
ret = -errno;
#else
ret = ntfs_fuse_safe_rename(req, ino, parent, name,
xino, newparent, newname, tmp);
#endif
}
free(tmp);
return ret;
}
static void ntfs_fuse_rename(fuse_req_t req, fuse_ino_t parent,
const char *name, fuse_ino_t newparent,
const char *newname)
{
int ret;
fuse_ino_t ino;
fuse_ino_t xino;
ntfs_inode *ni;
ntfs_log_debug("rename: old: '%s' new: '%s'\n", name, newname);
/*
* FIXME: Rename should be atomic.
*/
ino = ntfs_fuse_inode_lookup(parent, name);
if (ino == (fuse_ino_t)-1) {
ret = -errno;
goto out;
}
/* Check whether target is present */
xino = ntfs_fuse_inode_lookup(newparent, newname);
if (xino != (fuse_ino_t)-1) {
/*
* Target exists : no need to check whether it
* designates the same inode, this has already
* been checked (by fuse ?)
*/
ni = ntfs_inode_open(ctx->vol, INODE(xino));
if (!ni)
ret = -errno;
else {
ret = ntfs_check_empty_dir(ni);
if (ret < 0) {
ret = -errno;
ntfs_inode_close(ni);
goto out;
}
if (ntfs_inode_close(ni)) {
set_fuse_error(&ret);
goto out;
}
ret = ntfs_fuse_rename_existing_dest(req, ino, parent,
name, xino, newparent, newname);
}
} else {
/* target does not exist */
ret = ntfs_fuse_newlink(req, ino, newparent, newname,
(struct fuse_entry_param*)NULL);
if (ret)
goto out;
ret = ntfs_fuse_rm(req, parent, name, RM_ANY);
if (ret)
ntfs_fuse_rm(req, newparent, newname, RM_ANY);
}
out:
if (ret)
fuse_reply_err(req, -ret);
else
fuse_reply_err(req, 0);
}
static void ntfs_fuse_release(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
ntfs_inode *ni = NULL;
ntfs_attr *na = NULL;
struct open_file *of;
char ghostname[GHOSTLTH];
int res;
of = (struct open_file*)(long)fi->fh;
/* Only for marked descriptors there is something to do */
if (!of
|| !(of->state & (CLOSE_COMPRESSED
| CLOSE_ENCRYPTED | CLOSE_DMTIME))) {
res = 0;
goto out;
}
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
if (!na) {
res = -errno;
goto exit;
}
res = 0;
if (of->state & CLOSE_DMTIME)
ntfs_inode_update_times(ni,NTFS_UPDATE_MCTIME);
uint64_t lidx = 0;
int ret = 0;
int cl_per_bl = ctx->vol->cluster_size / blocksize;
if (blocksize > ctx->vol->cluster_size) {
ret = -EINVAL;
goto done;
}
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
ret = -errno;
goto done;
}
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, vidx / cl_per_bl);
lidx = (lcn > 0) ? lcn * cl_per_bl + vidx % cl_per_bl : 0;
close_attr:
ntfs_attr_close(na);
close_inode:
if (ntfs_inode_close(ni))
set_fuse_error(&ret);
done :
if (ret < 0)
fuse_reply_err(req, -ret);
else
fuse_reply_bmap(req, lidx);
}
#ifdef HAVE_SETXATTR
/*
*
*/
enum {
XATTRNS_NONE,
XATTRNS_USER,
XATTRNS_SYSTEM,
XATTRNS_SECURITY,
XATTRNS_TRUSTED,
XATTRNS_OPEN
} ;
/*
*
*
*
*
*
*/
dir_ni = ntfs_dir_parent_inode(ni);
if (dir_ni) {
if (!ntfs_real_allowed_access(security,
dir_ni, acctype))
bad = TRUE;
if (ntfs_inode_close(dir_ni))
bad = TRUE;
} else
bad = TRUE;
}
if (bad) {
ntfs_inode_close(ni);
ni = (ntfs_inode*)NULL;
}
}
}
return (ni);
}
/*
*
*/
switch (namespace) {
case XATTRNS_USER :
/*
* user name space : remove user prefix
*/
len = ntfs_mbstoucs(name + nf_ns_user_prefix_len, lename);
break;
case XATTRNS_SYSTEM :
case XATTRNS_SECURITY :
case XATTRNS_TRUSTED :
/*
* security, trusted and unmapped system name spaces :
* insert ntfs-3g prefix
*/
prefixed = (char*)ntfs_malloc(strlen(xattr_ntfs_3g)
+ strlen(name) + 1);
if (prefixed) {
strcpy(prefixed,xattr_ntfs_3g);
strcat(prefixed,name);
len = ntfs_mbstoucs(prefixed, lename);
free(prefixed);
} else
len = -1;
break;
case XATTRNS_OPEN :
/*
* in open name space mode : do no fix prefix
*/
len = ntfs_mbstoucs(name, lename);
break;
default :
len = -1;
}
return (len);
}
static void ntfs_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
ntfs_attr_search_ctx *actx = NULL;
ntfs_inode *ni;
char *list = (char*)NULL;
int ret = 0;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
ntfs_fuse_fill_security_context(req, &security);
#endif
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
ret = -errno;
goto out;
}
/* 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 (size) {
list = (char*)malloc(size);
if (!list) {
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);
out :
if (ret < 0)
fuse_reply_err(req, -ret);
else
if (size)
fuse_reply_buf(req, list, ret);
else
fuse_reply_xattr(req, ret);
free(list);
}
static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
ntfs_inode *ni;
ntfs_inode *dir_ni;
ntfs_attr *na = NULL;
char *value = (char*)NULL;
ntfschar *lename = (ntfschar*)NULL;
int lename_len;
int res;
s64 rsize;
enum SYSTEMXATTRS attr;
int namespace;
struct SECURITY_CONTEXT security;
attr = ntfs_xattr_system_type(name,ctx->vol);
if (attr != XATTR_UNMAPPED) {
/*
return;
}
if (ctx->streams == NF_STREAMS_INTERFACE_NONE) {
res = -EOPNOTSUPP;
goto out;
}
namespace = xattr_namespace(name);
if (namespace == XATTRNS_NONE) {
res = -EOPNOTSUPP;
goto out;
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
ntfs_fuse_fill_security_context(req,&security);
/* trusted only readable by root */
if ((namespace == XATTRNS_TRUSTED)
&& security.uid) {
res = -ENODATA;
goto out;
}
#endif
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
goto out;
}
/* 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) {
value = (char*)ntfs_malloc(rsize);
if (value)
res = ntfs_attr_pread(na, 0, rsize, value);
if (!value || (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);
out :
if (res < 0)
fuse_reply_err(req, -res);
else
if (size)
fuse_reply_buf(req, value, res);
else
fuse_reply_xattr(req, res);
free(value);
}
static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, 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) {
/*
* 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
*/
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
ni = ntfs_check_access_xattr(req,&security,ino,attr,TRUE);
if (ni) {
if (ntfs_allowed_as_owner(&security, ni)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = ntfs_dir_parent_inode(ni);
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
/* creation of a new name is not controlled by fuse */
if (attr == XATTR_NTFS_DOS_NAME)
ni = ntfs_check_access_xattr(req, &security,
ino, attr, TRUE);
else
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (ni) {
/*
* user mapping is not mandatory
* if defined, only owner is allowed
*/
if (!ntfs_fuse_fill_security_context(req, &security)
|| ntfs_allowed_as_owner(&security, ni)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = ntfs_dir_parent_inode(ni);
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;
#endif
#if CACHEING && !defined(FUSE_INTERNAL)
/*
* Most of system xattr settings cause changes to some
* file attribute (st_mode, st_nlink, st_mtime, etc.),
* so we must invalidate cached data when cacheing is
* in use (not possible with internal fuse or external
* fuse before 2.8)
*/
if ((res >= 0)
&& fuse_lowlevel_notify_inval_inode(ctx->fc, ino, -1, 0))
res = -errno;
#endif
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_err(req, 0);
return;
}
if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
&& (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) {
res = -EOPNOTSUPP;
goto out;
}
namespace = xattr_namespace(name);
if (namespace == XATTRNS_NONE) {
res = -EOPNOTSUPP;
goto out;
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
ntfs_fuse_fill_security_context(req,&security);
/* security and trusted only settable by root */
if (((namespace == XATTRNS_SECURITY)
|| (namespace == XATTRNS_TRUSTED))
&& security.uid) {
res = -EPERM;
goto out;
}
#endif
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
goto out;
}
#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);
out :
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_err(req, 0);
}
static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na
me)
{
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)
ni = ntfs_check_access_xattr(req, &security, ino,
attr,TRUE);
if (ni) {
if (ntfs_allowed_as_owner(&security, ni)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = ntfs_dir_parent_inode(n
i);
else
dir_ni = (ntfs_inode*)NULL;
res = ntfs_xattr_system_removexattr(&sec
urity,
attr, ni, dir_ni);
if (res)
res = -errno;
/* never have to close dir_ni */
} 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
/* creation of a new name is not controlled by fuse */
if (attr == XATTR_NTFS_DOS_NAME)
ni = ntfs_check_access_xattr(req, &security,
ino, attr, TRUE);
else
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (ni) {
/*
* user mapping is not mandatory
* if defined, only owner is allowed
*/
if (!ntfs_fuse_fill_security_context(req, &secur
ity)
|| ntfs_allowed_as_owner(&security, ni)) {
if (attr == XATTR_NTFS_DOS_NAME)
dir_ni = ntfs_dir_parent_inode(n
i);
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;
#endif
#if CACHEING && !defined(FUSE_INTERNAL)
/*
* Some allowed system xattr removals cause changes to
* some file attribute (st_mode, st_nlink, etc.),
* so we must invalidate cached data when cacheing is
* in use (not possible with internal fuse or external
* fuse before 2.8)
*/
if ((res >= 0)
&& fuse_lowlevel_notify_inval_inode(ctx->fc,
ino, -1, 0))
res = -errno;
#endif
break;
}
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_err(req, 0);
return;
}
if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR)
&& (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) {
res = -EOPNOTSUPP;
goto out;
}
namespace = xattr_namespace(name);
if (namespace == XATTRNS_NONE) {
res = -EOPNOTSUPP;
goto out;
}
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
ntfs_fuse_fill_security_context(req,&security);
/* security and trusted only settable by root */
if (((namespace == XATTRNS_SECURITY)
|| (namespace == XATTRNS_TRUSTED))
&& security.uid) {
res = -EACCES;
goto out;
}
#endif
ni = ntfs_inode_open(ctx->vol, INODE(ino));
if (!ni) {
res = -errno;
goto out;
}
#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);
out :
if (res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_err(req, 0);
return;
}
#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((fuse_req_t)NULL, &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 *notused __attribute__((unused)))
{
ntfs_close();
}
static struct fuse_lowlevel_ops ntfs_3g_ops = {
.lookup
= ntfs_fuse_lookup,
.getattr
= ntfs_fuse_getattr,
.readlink
= ntfs_fuse_readlink,
.opendir
= ntfs_fuse_opendir,
.readdir
= ntfs_fuse_readdir,
.releasedir
= ntfs_fuse_releasedir,
.open
= ntfs_fuse_open,
.release
= ntfs_fuse_release,
.read
= ntfs_fuse_read,
.write
= ntfs_fuse_write,
.setattr
= ntfs_fuse_setattr,
.statfs
= ntfs_fuse_statfs,
.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,
.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,
#endif
#ifdef HAVE_SETXATTR
.getxattr
= ntfs_fuse_getxattr,
.setxattr
= ntfs_fuse_setxattr,
.removexattr
= ntfs_fuse_removexattr,
.listxattr
= ntfs_fuse_listxattr,
#endif /* HAVE_SETXATTR */
#if 0 && (defined(__APPLE__) || defined(__DARWIN__)) /* Unfinished. */
/* 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_fuse_context_t*)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;
ntfs_volume *vol;
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 = vol = ntfs_mount(device, flags);
if (!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;
if (ctx->ignore_case && ntfs_set_ignore_case(vol))
goto err_out;
vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na);
if (vol->free_clusters < 0) {
ntfs_log_perror("Failed to read NTFS $Bitmap");
goto err_out;
}
vol->free_mft_records = ntfs_get_nr_free_mft_records(vol);
if (vol->free_mft_records < 0) {
ntfs_log_perror("Failed to calculate free MFT records");
goto err_out;
}
if (ctx->hiberfile && ntfs_volume_check_hiberfile(vol, 0)) {
if (errno != EPERM)
goto err_out;
if (ntfs_fuse_rm((fuse_req_t)NULL,FILE_root,"hiberfil.sys",
RM_LINK))
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(),
5 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING,
EXEC_NAME, ntfs_home);
}
#if defined(linux) || defined(__uClinux__)
static
"HINT:
"
"
"
"
"
"
"\n";
}
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 */
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_session *mount_fuse(char *parsed_options)
{
struct fuse_session *se = 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 (ctx->debug)
if (fuse_opt_add_arg(&args, "-odebug") == -1)
goto err;
se = fuse_lowlevel_new(&args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL);
if (!se)
goto err;
if (fuse_set_signal_handlers(se))
goto err_destroy;
fuse_session_add_chan(se, ctx->fc);
out:
fuse_opt_free_args(&args);
return se;
err_destroy:
fuse_session_destroy(se);
se = 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_session *se;
#if !(defined(__sun) && defined (__SVR4))
fuse_fstype fstype = FSTYPE_UNKNOWN;
#endif
const char *permissions_mode = (const char*)NULL;
const char *failed_secure = (const char*)NULL;
#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS)
struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL;
#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */
struct stat sbuf;
unsigned long existing_mount;
int err, fd;
/*
* Make sure file descriptors 0, 1 and 2 are open,
* otherwise chaos would ensue.
*/
do {
fd = open("/dev/null", O_RDWR);
if (fd > 2)
close(fd);
} while (fd >= 0 && fd <= 2);
#ifndef FUSE_INTERNAL
if ((getuid() != geteuid()) || (getgid() != getegid())) {
fprintf(stderr, "%s", setuid_msg);
return NTFS_VOLUME_INSECURE;
}
#endif
if (drop_privs())
return NTFS_VOLUME_NO_PRIVILEGE;
ntfs_set_locale();
ntfs_log_set_handler(ntfs_log_handler_stderr);
if (ntfs_parse_options(&opts, usage, argc, argv)) {
usage();
return NTFS_VOLUME_SYNTAX_ERROR;
}
if (ntfs_fuse_init()) {
err = NTFS_VOLUME_OUT_OF_MEMORY;
goto err2;
}
parsed_options = parse_mount_options(ctx, &opts, TRUE);
if (!parsed_options) {
err = NTFS_VOLUME_SYNTAX_ERROR;
goto err_out;
}
if (!ntfs_check_if_mounted(opts.device,&existing_mount)
&& (existing_mount & NTFS_MF_MOUNTED)
/* accept multiple read-only mounts */
&& (!(existing_mount & NTFS_MF_READONLY) || !ctx->ro)) {
err = NTFS_VOLUME_LOCKED;
goto err_out;
}
/* need absolute mount point for junctions */
if (opts.mnt_point[0] == '/')
ctx->abs_mnt_point = strdup(opts.mnt_point);
else {
ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX);
if (ctx->abs_mnt_point) {
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_permissions,acl")) {
err = NTFS_VOLUME_SYNTAX_ERROR;
goto err_out;
}
#endif /* KERNELACLS */
}
#else /* POSIXACLS */
if (!(ctx->vol->secure_flags
& ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) {
/*
* No explicit option but user mapping found
* force default security
*/
#if KERNELPERMS
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