diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c deleted file mode 100644 index d7f3091b..00000000 --- a/src/lowntfs-3g.c +++ /dev/null @@ -1,4337 +0,0 @@ -/** - * ntfs-3g - Third Generation NTFS Driver - * - * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2005 Yuval Fledel - * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2009 Jean-Pierre Andre - * Copyright (c) 2009 Erik Larsson - * - * This file is originated from the Linux-NTFS project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the NTFS-3G - * distribution in the file COPYING); if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include - -#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26) -#error "***********************************************************" -#error "* *" -#error "* Compilation requires at least FUSE version 2.6.0! *" -#error "* *" -#error "***********************************************************" -#endif - -#ifdef FUSE_INTERNAL -#define FUSE_TYPE "integrated FUSE low" -#else -#define FUSE_TYPE "external FUSE low" -#endif - -#ifdef HAVE_STDIO_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_LOCALE_H -#include -#endif -#include -#ifdef HAVE_LIMITS_H -#include -#endif -#include -#include -#include - -#ifdef HAVE_SETXATTR -#include -#endif - -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_MKDEV_H -#include -#endif - -#if defined(__APPLE__) || defined(__DARWIN__) -#include -#endif /* defined(__APPLE__) || defined(__DARWIN__) */ - -#include "compat.h" -#include "attrib.h" -#include "inode.h" -#include "volume.h" -#include "dir.h" -#include "unistr.h" -#include "layout.h" -#include "index.h" -#include "ntfstime.h" -#include "security.h" -#include "reparse.h" -#include "object_id.h" -#include "efs.h" -#include "logging.h" -#include "misc.h" - -/* - * The following permission checking modes are governed by - * the LPERMSCONFIG value in param.h - */ - -/* ACLS may be checked by kernel (requires a fuse patch) or here */ -#define KERNELACLS ((LPERMSCONFIG > 6) & (LPERMSCONFIG < 10)) -/* basic permissions may be checked by kernel or here */ -#define KERNELPERMS (((LPERMSCONFIG - 1) % 6) < 3) -/* may want to use fuse/kernel cacheing */ -#define CACHEING (!(LPERMSCONFIG % 3)) - -#if KERNELACLS & !KERNELPERMS -#error "Incompatible options KERNELACLS and KERNELPERMS" -#endif - -#if CACHEING & (KERNELACLS | !KERNELPERMS) -#warning "Fuse cacheing is broken unless basic permissions checked by kernel" -#endif - -#if !CACHEING - /* - * FUSE cacheing is broken except for basic permissions - * checked by the kernel - * So do not use cacheing until this is fixed - */ -#define ATTR_TIMEOUT 0.0 -#define ENTRY_TIMEOUT 0.0 -#else -#define ATTR_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 1.0 : 0.0) -#define ENTRY_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 1.0 : 0.0) -#endif -#define GHOSTLTH 40 /* max length of a ghost file name - see ghostformat */ - - /* sometimes the kernel cannot check access */ -#define ntfs_real_allowed_access(scx, ni, type) ntfs_allowed_access(scx, ni, type) -#if POSIXACLS & KERNELPERMS & !KERNELACLS - /* short-circuit if PERMS checked by kernel and ACLs by fs */ -#define ntfs_allowed_access(scx, ni, type) \ - ((scx)->vol->secure_flags & (1 << SECURITY_DEFAULT) \ - ? 1 : ntfs_allowed_access(scx, ni, type)) -#endif - -#define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE -#define INODE(ino) ((ino) == 1 ? (MFT_REF)FILE_root : (MFT_REF)(ino)) - -typedef enum { - FSTYPE_NONE, - FSTYPE_UNKNOWN, - FSTYPE_FUSE, - FSTYPE_FUSEBLK -} fuse_fstype; - -typedef enum { - ATIME_ENABLED, - ATIME_DISABLED, - ATIME_RELATIVE -} ntfs_atime_t; - -typedef struct fill_item { - struct fill_item *next; - size_t bufsize; - size_t off; - char buf[0]; -} ntfs_fuse_fill_item_t; - -typedef struct fill_context { - struct fill_item *first; - struct fill_item *last; - 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; -} ; - -typedef enum { - NF_STREAMS_INTERFACE_NONE, /* No access to named data streams. */ - NF_STREAMS_INTERFACE_XATTR, /* Map named data streams to xattrs. */ - NF_STREAMS_INTERFACE_OPENXATTR, /* Same, not limited to "user." */ -} ntfs_fuse_streams_interface; - -enum { - CLOSE_GHOST = 1, - CLOSE_COMPRESSED = 2, - CLOSE_ENCRYPTED = 4 -}; - -typedef struct { - ntfs_volume *vol; - unsigned int uid; - unsigned int gid; - unsigned int fmask; - unsigned int dmask; - ntfs_fuse_streams_interface streams; - ntfs_atime_t atime; - BOOL ro; - BOOL show_sys_files; - BOOL silent; - BOOL recover; - BOOL hiberfile; - BOOL debug; - BOOL no_detach; - BOOL blkdev; - BOOL mounted; - BOOL efs_raw; - struct fuse_chan *fc; - BOOL inherit; - unsigned int secure_flags; - char *usermap_path; - char *abs_mnt_point; - struct PERMISSIONS_CACHE *seccache; - struct SECURITY_CONTEXT security; - struct open_file *open_files; - u64 latest_ghost; -} ntfs_fuse_context_t; - -static struct options { - char *mnt_point; /* Mount point */ - char *options; /* Mount options */ - char *device; /* Device to mount */ -} opts; - -static const char *EXEC_NAME = "ntfs-3g"; -static char def_opts[] = "silent,allow_other,nonempty,"; -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-2009 Jean-Pierre Andre\n" -"Copyright (C) 2009 Erik Larsson\n" -"\n" -"Usage: %s [-o option[,...]] \n" -"\n" -"Options: ro (read-only mount), remove_hiberfile, 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; } -static int restore_privs(void) { return 0; } - -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 http://ntfs-3g.org/support.html#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://ntfs-3g.org/support.html#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 && - ni->last_access_time >= ni->last_data_change_time && - ni->last_access_time >= 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; -} - -/* - * Fill a security context as needed by security functions - * returns TRUE if there is a user mapping, - * FALSE if there is none - * This is not an error and the context is filled anyway, - * it is used for implicit Windows-like inheritance - */ - -static BOOL ntfs_fuse_fill_security_context(fuse_req_t req, - struct SECURITY_CONTEXT *scx) -{ - const struct fuse_ctx *fusecontext; - - scx->vol = ctx->vol; - scx->mapping[MAPUSERS] = ctx->security.mapping[MAPUSERS]; - scx->mapping[MAPGROUPS] = ctx->security.mapping[MAPGROUPS]; - scx->pseccache = &ctx->seccache; - if (req) { - fusecontext = fuse_req_ctx(req); - scx->uid = fusecontext->uid; - scx->gid = fusecontext->gid; - scx->tid = fusecontext->pid; -#ifdef FUSE_CAP_DONT_MASK - /* the umask can be processed by the file system */ - scx->umask = fusecontext->umask; -#else - /* the umask if forced by fuse on creation */ - scx->umask = 0; -#endif - - } else { - scx->uid = 0; - scx->gid = 0; - scx->tid = 0; - scx->umask = 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); - if (ntfs_inode_close(dir_ni) - || (inum == (u64)-1)) - ino = (u64)-1; - else - ino = MREF(inum); - } - return (ino); -} - -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - -/* - * Check access to parent directory - * - * file inode is only opened when not fed in and S_ISVTX is requested, - * when already open and S_ISVTX, it *HAS TO* be fed in. - * - * returns 1 if allowed, - * 0 if not allowed or some error occurred (errno tells why) - */ - -static int ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, - ntfs_inode *dir_ni, fuse_ino_t ino, - ntfs_inode *ni, mode_t accesstype) -{ - int allowed; - ntfs_inode *ni2; - struct stat stbuf; - - allowed = ntfs_allowed_access(scx, dir_ni, accesstype); - /* - * for an 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_inode_open(ctx->vol, INODE(ino)); - 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 /* !KERNELPERMS | (POSIXACLS & !KERNELACLS) */ - -/** - * 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 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); - -} - -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 = 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 = 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 = 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__) */ - -#if defined(FUSE_CAP_DONT_MASK) || (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 */ -} -#endif /* defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) */ - -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; - /* - * return data size rounded to next 512 byte boundary for - * encrypted files to include padding required for decryption - * also include 2 bytes for padding info - */ - if (ctx->efs_raw && ni->flags & FILE_ATTR_ENCRYPTED) - stbuf->st_size = ((ni->data_size + 511) & ~511) + 2; - - /* - * Temporary fix to make ActiveSync work via Samba 3.0. - * See more on the ntfs-3g-devel list. - */ - 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) { - goto exit; - } - /* 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 ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES) - + sizeof(ntfschar) * PATH_MAX - && (size_t)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; - stbuf->st_ino = ni->mft_no; - stbuf->st_atime = ni->last_access_time; - stbuf->st_ctime = ni->last_mft_change_time; - stbuf->st_mtime = ni->last_data_change_time; -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); -} - - -static void ntfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent, - const char *name) -{ - struct SECURITY_CONTEXT security; - struct fuse_entry_param entry; - ntfs_inode *dir_ni; - u64 iref; - BOOL ok = FALSE; - - if (strlen(name) < 256) { - dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); - if (dir_ni) { -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - /* - * make sure the parent directory is searchable - */ - if (ntfs_fuse_fill_security_context(req, &security) - && !ntfs_allowed_access(&security,dir_ni,S_IEXEC)) { - ntfs_inode_close(dir_ni); - errno = EACCES; - } else { -#else - ntfs_fuse_fill_security_context(req, &security); -#endif - iref = ntfs_inode_lookup_by_mbsname(dir_ni, - name); - ok = !ntfs_inode_close(dir_ni) - && (iref != (u64)-1) - && ntfs_fuse_fillstat( - &security,&entry,iref); -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - } -#endif - } - } else - errno = ENAMETOOLONG; - if (!ok) - fuse_reply_err(req, errno); - else - fuse_reply_entry(req, &entry); -} - -static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino) -{ - ntfs_inode *ni = NULL; - ntfs_attr *na = NULL; - INTX_FILE *intx_file = NULL; - char *buf = (char*)NULL; - int res = 0; - - /* Get inode. */ - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - if (!ni) { - 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; - } - - if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || - ctx->show_sys_files) { - struct stat st = { .st_ino = MREF(mref) }; - - if (dt_type == NTFS_DT_REG) - st.st_mode = S_IFREG | (0777 & ~ctx->fmask); - else if (dt_type == NTFS_DT_DIR) - st.st_mode = S_IFDIR | (0777 & ~ctx->dmask); - -#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); - } -#endif /* defined(__APPLE__) || defined(__DARWIN__) */ - - current = fill_ctx->last; - sz = fuse_add_direntry(fill_ctx->req, - ¤t->buf[current->off], - current->bufsize - current->off, - filename, &st, current->off); - if (!sz || ((current->off + sz) > current->bufsize)) { - newone = (ntfs_fuse_fill_item_t*)ntfs_malloc - (sizeof(ntfs_fuse_fill_item_t) - + current->bufsize); - newone->off = 0; - newone->bufsize = current->bufsize; - newone->next = (ntfs_fuse_fill_item_t*)NULL; - current->next = newone; - fill_ctx->last = newone; - current = newone; - sz = fuse_add_direntry(fill_ctx->req, current->buf, - current->bufsize - current->off, - filename, &st, current->off); - } - if (sz) { - current->off += sz; - } else { - ret = -1; - errno = EIO; /* ? */ - } - } - - 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; - } - 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; - /* make sure to clear results */ - current = fill->first; - while (current) { - current = current->next; - free(fill->first); - fill->first = current; - } - 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) { - if (!fill->filled) { - /* initial call : build the full list */ - 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; - 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) - fuse_reply_buf(req, first->buf, - first->off); - /* reply sent, now must exit with no error */ - fill->first = first->next; - free(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 (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; - /* 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; - } - 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) - 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; - } - /* limit reads at next 512 byte boundary for encrypted attributes */ - max_read = na->data_size; - if (ctx->efs_raw && (na->data_flags & ATTR_IS_ENCRYPTED) && - NAttrNonResident(na)) { - max_read = ((na->data_size+511) & ~511) + 2; - } - if (offset + (off_t)size > max_read) { - if (max_read < offset) - goto ok; - size = 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 %lld at " - "offset %lld: %lld <> %lld", (long long)ni->mft_no, - (long long)offset, (long long)size, (long long)ret); - 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; - } - na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); - if (!na) { - res = -errno; - goto exit; - } - while (size) { - s64 ret = ntfs_attr_pwrite(na, offset, size, buf + total); - if (ret <= 0) { - res = -errno; - goto exit; - } - size -= ret; - offset += ret; - total += ret; - } - res = total; - if (res > 0) - ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); -exit: - if (na) - ntfs_attr_close(na); - if (total) - set_archive(ni); - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - if (res < 0) - fuse_reply_err(req, -res); - else - fuse_reply_write(req, res); -} - -static int ntfs_fuse_chmod(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, - mode_t mode, struct stat *stbuf) -{ - int res = 0; - ntfs_inode *ni; - - /* return unsupported if no user mapping has been defined */ - if (!scx->mapping[MAPUSERS] && !ctx->silent) { - res = -EOPNOTSUPP; - } else { - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - if (!ni) - res = -errno; - else { - if (scx->mapping[MAPUSERS]) { - if (ntfs_set_mode(scx, ni, mode)) - 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); - } - 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; - - if (!scx->mapping[MAPUSERS] - && !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 { - if (scx->mapping[MAPUSERS] - && (((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; - - if (!scx->mapping[MAPUSERS] - && !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 { - if (scx->mapping[MAPUSERS]) { - if (ntfs_set_ownmod(scx, ni, uid, gid, mode)) - 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_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - off_t size, BOOL chkwrite, struct stat *stbuf) -#else - off_t size, BOOL chkwrite __attribute__((unused)), - struct stat *stbuf) -#endif -{ - ntfs_inode *ni = NULL; - ntfs_attr *na = NULL; - int res; - s64 oldsize; - - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - if (!ni) - goto exit; - - na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); - if (!na) - goto exit; -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - /* - * deny truncation if cannot write to file - * (already checked for ftruncate()) - */ - if (scx->mapping[MAPUSERS] - && chkwrite - && !ntfs_allowed_access(scx, ni, S_IWRITE)) { - errno = EACCES; - goto exit; - } -#endif - /* - * for compressed files, only deleting contents and expanding - * are implemented. Expanding is done by inserting a final - * zero, which is optimized as creating a hole when possible. - */ - if ((na->data_flags & ATTR_COMPRESSION_MASK) - && size - && (size < na->initialized_size)) { - errno = EOPNOTSUPP; - goto exit; - } - 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); - res = ntfs_fuse_getstat(scx, ni, stbuf); - errno = (res ? -res : 0); -exit: - res = -errno; - ntfs_attr_close(na); - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - return res; -} - -static int ntfs_fuse_utime(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, - struct stat *stin, struct stat *stbuf) -{ - ntfs_inode *ni; - int res = 0; -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - BOOL ownerok; - BOOL writeok; -#endif - - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - if (!ni) - return -errno; - -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - ownerok = ntfs_allowed_as_owner(scx, ni); - if (stin) { - /* - * 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 - && (stin->st_atime == stin->st_mtime) - && ntfs_allowed_access(scx, ni, S_IWRITE); - /* Must be owner */ - if (!ownerok && !writeok) - res = (stin->st_atime == stin->st_mtime - ? -EACCES : -EPERM); - else { - ni->last_access_time = stin->st_atime; - ni->last_data_change_time = stin->st_mtime; - 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) { - ni->last_access_time = stin->st_atime; - ni->last_data_change_time = stin->st_mtime; - 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; -} - -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__((unused))) -{ - struct stat stbuf; - ntfs_inode *ni; - int res; - struct SECURITY_CONTEXT security; - - ntfs_fuse_fill_security_context(req, &security); - switch (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)) { - case 0 : - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - if (!ni) - res = -errno; - else { - res = ntfs_fuse_getstat(&security, ni, &stbuf); - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - } - break; - case FUSE_SET_ATTR_MODE : - res = ntfs_fuse_chmod(&security, ino, attr->st_mode & 07777, - &stbuf); - break; - case FUSE_SET_ATTR_UID : - res = ntfs_fuse_chown(&security, ino, attr->st_uid, - (gid_t)-1, &stbuf); - break; - case FUSE_SET_ATTR_GID : - res = ntfs_fuse_chown(&security, ino, (uid_t)-1, - attr->st_gid, &stbuf); - break; - case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_GID : - res = ntfs_fuse_chown(&security, ino, attr->st_uid, - attr->st_gid, &stbuf); - break; - case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_MODE: - res = ntfs_fuse_chownmod(&security, ino, attr->st_uid, - (gid_t)-1,attr->st_mode, &stbuf); - break; - case FUSE_SET_ATTR_GID + FUSE_SET_ATTR_MODE: - res = ntfs_fuse_chownmod(&security, ino, (uid_t)-1, - attr->st_gid,attr->st_mode, &stbuf); - break; - case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_GID + FUSE_SET_ATTR_MODE: - res = ntfs_fuse_chownmod(&security, ino, attr->st_uid, - attr->st_gid,attr->st_mode, &stbuf); - break; - case FUSE_SET_ATTR_SIZE : - res = ntfs_fuse_trunc(&security, ino, attr->st_size, - !fi, &stbuf); - break; - case FUSE_SET_ATTR_ATIME + FUSE_SET_ATTR_MTIME : - res = ntfs_fuse_utime(&security, ino, attr, &stbuf); - break; - default: - ntfs_log_error("Unsupported setattr mode 0x%x\n",(int)to_set); - res = -EOPNOTSUPP; - break; - } - if (res) - 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; - mode_t type = typemode & ~07777; - mode_t perm; - struct SECURITY_CONTEXT security; - int res = 0, uname_len, utarget_len; - - /* Generate unicode filename. */ - uname_len = ntfs_mbstoucs(name, &uname); - if (uname_len < 0) { - res = -errno; - goto exit; - } - /* Open parent directory. */ - dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); - if (!dir_ni) { - res = -errno; - goto exit; - } -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - /* make sure parent directory is writeable and executable */ - if (!ntfs_fuse_fill_security_context(req, &security) - || ntfs_allowed_access(&security, - dir_ni,S_IWRITE + S_IEXEC)) { -#else - ntfs_fuse_fill_security_context(req, &security); -#endif - if (S_ISDIR(type)) - perm = typemode & ~ctx->dmask & 0777; - else - perm = typemode & ~ctx->fmask & 0777; - /* - * Try to get a security id available for - * file creation (from inheritance or argument). - * This is not possible for NTFS 1.x, and we will - * have to build a security attribute later. - */ - if (!ctx->security.mapping[MAPUSERS]) - securid = const_cpu_to_le32(0); - else - if (ctx->inherit) - securid = ntfs_inherited_id(&security, - dir_ni, S_ISDIR(type)); - else -#if POSIXACLS - securid = ntfs_alloc_securid(&security, - security.uid, security.gid, - dir_ni, perm, S_ISDIR(type)); -#else - securid = ntfs_alloc_securid(&security, - security.uid, security.gid, - perm & ~security.umask, S_ISDIR(type)); -#endif - /* Create object specified in @type. */ - switch (type) { - 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, security.gid, - dir_ni, perm) < 0) - set_fuse_error(&res); -#else - if (!securid - && ntfs_set_owner_mode(&security, ni, - security.uid, security.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; - } - /* mark a future need to fixup encrypted inode */ - if (fi - && ctx->efs_raw - && (ni->flags & FILE_ATTR_ENCRYPTED)) - state |= CLOSE_ENCRYPTED; - 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; - } - - /* Generate unicode filename. */ - uname_len = ntfs_mbstoucs(newname, &uname); - if (uname_len < 0) { - 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) -{ - ntfschar *uname = NULL; - ntfs_inode *dir_ni = NULL, *ni = NULL; - int res = 0, uname_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; - } - -{ /* temporary */ -struct open_file *prev = (struct open_file*)NULL; -for (of=ctx->open_files; of; of=of->next) -{ -if (of->previous != prev) ntfs_log_error("bad chaining\n"); -prev = of; -} -} - of = ctx->open_files; - ino = (fuse_ino_t)MREF(iref); - /* improvable search in open files list... */ - while (of - && (of->ino != ino)) - of = of->next; - if (of && !(of->state & CLOSE_GHOST)) { - /* file was open, create a ghost in unlink parent */ - of->state |= CLOSE_GHOST; - of->parent = parent; - of->ghost = ++ctx->latest_ghost; - sprintf(ghostname,ghostformat,of->ghost); - /* need to close the dir for linking the ghost */ - if (ntfs_inode_close(dir_ni)) { - res = -errno; - goto out; - } - /* sweep existing ghost if any */ - ntfs_fuse_rm(req, parent, ghostname); - res = ntfs_fuse_newlink(req, of->ino, parent, ghostname, - (struct fuse_entry_param*)NULL); - if (res) - goto out; - /* now reopen then parent directory */ - dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); - if (!dir_ni) { - res = -errno; - goto exit; - } - } - - ni = ntfs_inode_open(ctx->vol, MREF(iref)); - if (!ni) { - res = -errno; - goto exit; - } - -#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)) { -#endif - 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; -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - } else - res = -EACCES; -#endif -exit: - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - if (ntfs_inode_close(dir_ni)) - set_fuse_error(&res); -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); - 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); - 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); - if (ret) { - if (ntfs_fuse_rm(req, newparent, newname)) - 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) < 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); - if (ret) - ntfs_fuse_rm(req, newparent, newname); - } -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))) { - 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_COMPRESSED) - res = ntfs_attr_pclose(na); - if (of->state & CLOSE_ENCRYPTED) - res = ntfs_efs_fixup_attribute(NULL, na); -exit: - if (na) - ntfs_attr_close(na); - if (ntfs_inode_close(ni)) - set_fuse_error(&res); -out: - /* remove the associate ghost file (even if release failed) */ - if (of) { - if (of->state & CLOSE_GHOST) { - sprintf(ghostname,ghostformat,of->ghost); - ntfs_fuse_rm(req, of->parent, ghostname); - } - /* remove from open files list */ - if (of->next) - of->next->previous = of->previous; - if (of->previous) - of->previous->next = of->next; - else - ctx->open_files = of->next; - free(of); - } - if (res) - fuse_reply_err(req, -res); - else - fuse_reply_err(req, 0); -} - -static void ntfs_fuse_mkdir(fuse_req_t req, fuse_ino_t parent, - const char *name, mode_t mode) -{ - int res; - struct fuse_entry_param entry; - - res = ntfs_fuse_create(req, parent, name, S_IFDIR | (mode & 07777), - 0, &entry, (char*)NULL, (struct fuse_file_info*)NULL); - if (res < 0) - fuse_reply_err(req, -res); - else - fuse_reply_entry(req, &entry); -} - -static void ntfs_fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) -{ - int res; - - res = ntfs_fuse_rm(req, parent, name); - if (res) - fuse_reply_err(req, -res); - else - fuse_reply_err(req, 0); -} - -static void ntfs_fuse_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, - uint64_t vidx) -{ - ntfs_inode *ni; - ntfs_attr *na; - LCN lcn; - 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 - -/* - * Name space identifications and prefixes - */ - -enum { XATTRNS_NONE, - XATTRNS_USER, - XATTRNS_SYSTEM, - XATTRNS_SECURITY, - XATTRNS_TRUSTED, - XATTRNS_OPEN } ; - -static const char nf_ns_user_prefix[] = "user."; -static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; -static const char nf_ns_system_prefix[] = "system."; -static const int nf_ns_system_prefix_len = sizeof(nf_ns_system_prefix) - 1; -static const char nf_ns_security_prefix[] = "security."; -static const int nf_ns_security_prefix_len = sizeof(nf_ns_security_prefix) - 1; -static const char nf_ns_trusted_prefix[] = "trusted."; -static const int nf_ns_trusted_prefix_len = sizeof(nf_ns_trusted_prefix) - 1; - -static const char xattr_ntfs_3g[] = "ntfs-3g."; - -/* - * Identification of data mapped to the system name space - */ - -enum { XATTR_UNMAPPED, - XATTR_NTFS_ACL, - XATTR_NTFS_ATTRIB, - XATTR_NTFS_EFSINFO, - XATTR_NTFS_REPARSE_DATA, - XATTR_NTFS_OBJECT_ID, - XATTR_NTFS_DOS_NAME, - XATTR_NTFS_TIMES, - XATTR_POSIX_ACC, - XATTR_POSIX_DEF } ; - -static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl"; -static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; -static const char nf_ns_xattr_efsinfo[] = "user.ntfs.efsinfo"; -static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; -static const char nf_ns_xattr_object_id[] = "system.ntfs_object_id"; -static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name"; -static const char nf_ns_xattr_times[] = "system.ntfs_times"; -static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; -static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; - -struct XATTRNAME { - int xattr; - const char *name; -} ; - -static struct XATTRNAME nf_ns_xattr_names[] = { - { XATTR_NTFS_ACL, nf_ns_xattr_ntfs_acl }, - { XATTR_NTFS_ATTRIB, nf_ns_xattr_attrib }, - { XATTR_NTFS_EFSINFO, nf_ns_xattr_efsinfo }, - { XATTR_NTFS_REPARSE_DATA, nf_ns_xattr_reparse }, - { XATTR_NTFS_OBJECT_ID, nf_ns_xattr_object_id }, - { XATTR_NTFS_DOS_NAME, nf_ns_xattr_dos_name }, - { XATTR_NTFS_TIMES, nf_ns_xattr_times }, - { XATTR_POSIX_ACC, nf_ns_xattr_posix_access }, - { XATTR_POSIX_DEF, nf_ns_xattr_posix_default }, - { XATTR_UNMAPPED, (char*)NULL } /* terminator */ -}; - -/* - * Check whether access to internal data as an extended - * attribute in system name space is allowed - * - * Returns pointer to inode if allowed, - * NULL and errno set if not allowed - */ - -static ntfs_inode *ntfs_check_access_xattr(fuse_req_t req, - struct SECURITY_CONTEXT *security, - fuse_ino_t ino, int attr, BOOL setting) -{ - ntfs_inode *dir_ni; - ntfs_inode *ni; - BOOL foracl; - BOOL bad; - mode_t acctype; - - ni = (ntfs_inode*)NULL; - foracl = (attr == XATTR_POSIX_ACC) - || (attr == XATTR_POSIX_DEF); - /* - * When accessing Posix ACL, return unsupported if ACL - * were disabled or no user mapping has been defined. - * However no error will be returned to getfacl - */ - if ((!ntfs_fuse_fill_security_context(req, security) - || (ctx->secure_flags - & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_RAW)))) - && foracl) { - 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; - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - /* basic access was checked previously in a lookup */ - if (ni && (acctype != S_IEXEC)) { - bad = FALSE; - /* do not reopen root */ - if (ni->mft_no == FILE_root) { - /* forbid getting/setting names on root */ - if ((attr == XATTR_NTFS_DOS_NAME) - || !ntfs_real_allowed_access(security, - ni, acctype)) - bad = TRUE; - } else { - 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); -} - -/* - * Determine whether an extended attribute is in the system - * name space and mapped to internal data - */ - -static int mapped_xattr_system(const char *name) -{ - struct XATTRNAME *p; - - p = nf_ns_xattr_names; - while (p->name && strcmp(p->name,name)) - p++; - return (p->xattr); -} - -/* - * Determine the name space of an extended attribute - */ - -static int xattr_namespace(const char *name) -{ - int namespace; - - if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { - namespace = XATTRNS_NONE; - if (!strncmp(name, nf_ns_user_prefix, - nf_ns_user_prefix_len) - && (strlen(name) != (size_t)nf_ns_user_prefix_len)) - namespace = XATTRNS_USER; - else if (!strncmp(name, nf_ns_system_prefix, - nf_ns_system_prefix_len) - && (strlen(name) != (size_t)nf_ns_system_prefix_len)) - namespace = XATTRNS_SYSTEM; - else if (!strncmp(name, nf_ns_security_prefix, - nf_ns_security_prefix_len) - && (strlen(name) != (size_t)nf_ns_security_prefix_len)) - namespace = XATTRNS_SECURITY; - else if (!strncmp(name, nf_ns_trusted_prefix, - nf_ns_trusted_prefix_len) - && (strlen(name) != (size_t)nf_ns_trusted_prefix_len)) - namespace = XATTRNS_TRUSTED; - } else - namespace = XATTRNS_OPEN; - return (namespace); -} - -/* - * Fix the prefix of an extended attribute - */ - -static int fix_xattr_prefix(const char *name, int namespace, ntfschar **lename) -{ - int len; - char *prefixed; - - *lename = (ntfschar*)NULL; - 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 *to; - 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; - } -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - /* file must be readable */ -// condition on fill_security ? - 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; - } - } - to = list; - - if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR) - || (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) { - while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, - 0, NULL, 0, actx)) { - char *tmp_name = NULL; - int tmp_name_len; - - if (!actx->attr->name_length) - continue; - tmp_name_len = ntfs_ucstombs( - (ntfschar *)((u8*)actx->attr + - le16_to_cpu(actx->attr->name_offset)), - actx->attr->name_length, &tmp_name, 0); - if (tmp_name_len < 0) { - ret = -errno; - goto exit; - } - /* - * When using name spaces, do not return - * security, trusted nor system attributes - * (filtered elsewhere anyway) - * otherwise insert "user." prefix - */ - if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { - if ((strlen(tmp_name) > sizeof(xattr_ntfs_3g)) - && !strncmp(tmp_name,xattr_ntfs_3g, - sizeof(xattr_ntfs_3g)-1)) - tmp_name_len = 0; - else - ret += tmp_name_len - + nf_ns_user_prefix_len + 1; - } else - ret += tmp_name_len + 1; - if (size && tmp_name_len) { - if ((size_t)ret <= size) { - if (ctx->streams - == NF_STREAMS_INTERFACE_XATTR) { - strcpy(to, nf_ns_user_prefix); - to += nf_ns_user_prefix_len; - } - strncpy(to, tmp_name, tmp_name_len); - to += tmp_name_len; - *to = 0; - to++; - } else { - free(tmp_name); - ret = -ERANGE; - goto exit; - } - } - free(tmp_name); - } - } - - /* List efs info xattr for encrypted files */ - if (ctx->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED)) { - ret += sizeof(nf_ns_xattr_efsinfo); - if ((size_t)ret <= size) { - memcpy(to, nf_ns_xattr_efsinfo, - sizeof(nf_ns_xattr_efsinfo)); - to += sizeof(nf_ns_xattr_efsinfo); - } - } - - if (errno != ENOENT) - ret = -errno; -exit: - 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 __inline__ int ntfs_system_getxattr(struct SECURITY_CONTEXT *scx, - int attr, ntfs_inode *ni, char *value, size_t size) -{ - int res; - ntfs_inode *dir_ni; - - /* - * the returned value is the needed - * size. If it is too small, no copy - * is done, and the caller has to - * issue a new call with correct size. - */ - switch (attr) { - case XATTR_NTFS_ACL : - res = ntfs_get_ntfs_acl(scx, ni, value, size); - break; -#if POSIXACLS - case XATTR_POSIX_ACC : - res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_access, - value, size); - break; - case XATTR_POSIX_DEF : - res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_default, - value, size); - break; -#endif - case XATTR_NTFS_ATTRIB : - res = ntfs_get_ntfs_attrib(ni, value, size); - break; - case XATTR_NTFS_EFSINFO : - if (ctx->efs_raw) - res = ntfs_get_efs_info(ni, value, size); - else - res = -EPERM; - break; - case XATTR_NTFS_REPARSE_DATA : - res = ntfs_get_ntfs_reparse_data(ni, value, size); - break; - case XATTR_NTFS_OBJECT_ID : - res = ntfs_get_ntfs_object_id(ni, value, size); - break; - case XATTR_NTFS_DOS_NAME: - dir_ni = ntfs_dir_parent_inode(ni); - if (dir_ni) { - res = ntfs_get_ntfs_dos_name(ni, dir_ni, value, size); - if (ntfs_inode_close(dir_ni)) - set_fuse_error(&res); - } else - res = -errno; - break; - case XATTR_NTFS_TIMES: - res = ntfs_inode_get_times(ni, value, size); - break; - default : - errno = EOPNOTSUPP; - res = -errno; - break; - } - return (res); -} - -static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - size_t size) -{ - ntfs_inode *ni; - ntfs_attr *na = NULL; - char *value = (char*)NULL; - ntfschar *lename = (ntfschar*)NULL; - int lename_len; - int res; - s64 rsize; - int attr; - int namespace; - struct SECURITY_CONTEXT security; - - attr = mapped_xattr_system(name); - if (attr != XATTR_UNMAPPED) { - /* - * hijack internal data and ACL retrieval, whatever - * mode was selected for xattr (from the user's - * point of view, ACLs are not xattr) - */ - if (size) - value = (char*)ntfs_malloc(size); -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - if (!size || value) { - ni = ntfs_check_access_xattr(req, &security, ino, - attr, FALSE); - if (ni) { - if (ntfs_allowed_access(&security,ni,S_IREAD)) - res = ntfs_system_getxattr(&security, - attr, ni, value, size); - else - res = -errno; - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - } else - res = -errno; - } -#else - /* - * Standard access control has been done by fuse/kernel - */ - if (!size || value) { - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - if (ni) { - /* user mapping not mandatory */ - ntfs_fuse_fill_security_context(req, &security); - res = ntfs_system_getxattr(&security, - attr, ni, value, size); - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - } else - res = -errno; - } else - res = -errno; -#endif - 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); - return; - } - if (ctx->streams == NF_STREAMS_INTERFACE_NONE) { - res = -EOPNOTSUPP; - goto out; - } - 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 = -EPERM; - goto out; - } -#endif - ni = ntfs_inode_open(ctx->vol, INODE(ino)); - if (!ni) { - res = -errno; - goto out; - } -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - /* file must be readable */ -// condition on fill_security - 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 && - (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 __inline__ int ntfs_system_setxattr(struct SECURITY_CONTEXT *scx, - int attr, ntfs_inode *ni, const char *value, - size_t size, int flags) -{ - int res; - ntfs_inode *dir_ni; - - switch (attr) { - case XATTR_NTFS_ACL : - res = ntfs_set_ntfs_acl(scx, ni, value, size, flags); - break; -#if POSIXACLS - case XATTR_POSIX_ACC : - res = ntfs_set_posix_acl(scx ,ni , nf_ns_xattr_posix_access, - value, size, flags); - break; - case XATTR_POSIX_DEF : - res = ntfs_set_posix_acl(scx, ni, nf_ns_xattr_posix_default, - value, size, flags); - break; -#endif - case XATTR_NTFS_ATTRIB : - res = ntfs_set_ntfs_attrib(ni, value, size, flags); - break; - case XATTR_NTFS_EFSINFO : - if (ctx->efs_raw) - res = ntfs_set_efs_info(ni, value, size, flags); - else - res = -EPERM; - break; - case XATTR_NTFS_REPARSE_DATA : - res = ntfs_set_ntfs_reparse_data(ni, value, size, flags); - break; - case XATTR_NTFS_OBJECT_ID : - res = ntfs_set_ntfs_object_id(ni, value, size, flags); - break; - case XATTR_NTFS_DOS_NAME: - dir_ni = ntfs_dir_parent_inode(ni); - if (dir_ni) - /* warning : this closes both inodes */ - res = ntfs_set_ntfs_dos_name(ni, dir_ni, value, - size, flags); - else - res = -errno; - break; - case XATTR_NTFS_TIMES: - res = ntfs_inode_set_times(ni, value, size, flags); - break; - default : - errno = EOPNOTSUPP; - res = -errno; - break; - } - return (res); -} - -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_attr *na = NULL; - ntfschar *lename = NULL; - int res, lename_len; - size_t part, total; - int attr; - int namespace; - struct SECURITY_CONTEXT security; - - attr = mapped_xattr_system(name); - 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 : updating an ACL does not set ctime - */ -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - ni = ntfs_check_access_xattr(req,&security,ino,attr,TRUE); - if (ni) { - if (ntfs_allowed_as_owner(&security, ni)) { - res = ntfs_system_setxattr(&security, - attr, ni, value, size, flags); - if (res) - res = -errno; - } else - res = -errno; - if ((attr != XATTR_NTFS_DOS_NAME) - && 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)) { - res = ntfs_system_setxattr(&security, - attr, ni, value, size, flags); - if (res) - res = -errno; - } else - res = -errno; - if ((attr != XATTR_NTFS_DOS_NAME) - && ntfs_inode_close(ni)) - set_fuse_error(&res); - } else - 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 : - if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { - res = -EACCES; - goto exit; - } - break; - } -#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 && 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; - } - set_archive(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; - 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) - res = -errno; - else - if (!(res = ntfs_attr_pclose(na))) - if (ctx->efs_raw - && (ni->flags & FILE_ATTR_ENCRYPTED)) - res = ntfs_efs_fixup_attribute(NULL, - na); - if (total) - set_archive(ni); - } else - res = 0; -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 __inline__ int ntfs_system_removexattr(fuse_req_t req, fuse_ino_t ino, - int attr) -{ - int res; - ntfs_inode *dir_ni; - ntfs_inode *ni; - struct SECURITY_CONTEXT security; - - res = 0; - switch (attr) { - /* - * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES - * is never allowed - */ - case XATTR_NTFS_ACL : - case XATTR_NTFS_ATTRIB : - case XATTR_NTFS_EFSINFO : - case XATTR_NTFS_TIMES : - res = -EPERM; - break; -#if POSIXACLS - case XATTR_POSIX_ACC : - case XATTR_POSIX_DEF : - ni = ntfs_check_access_xattr(req,&security,ino,attr,TRUE); - if (ni) { - if (!ntfs_allowed_as_owner(&security, ni) - || ntfs_remove_posix_acl(&security, ni, - (attr == XATTR_POSIX_ACC ? - nf_ns_xattr_posix_access : - nf_ns_xattr_posix_default))) - res = -errno; - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - } else - res = -errno; - break; -#endif - case XATTR_NTFS_REPARSE_DATA : - ni = ntfs_check_access_xattr(req, &security, ino, attr, TRUE); - if (ni) { - if (!ntfs_allowed_as_owner(&security, ni) - || ntfs_remove_ntfs_reparse_data(ni)) - res = -errno; - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - } else - res = -errno; - break; - case XATTR_NTFS_OBJECT_ID : - ni = ntfs_check_access_xattr(req, &security, ino, attr, TRUE); - if (ni) { - if (!ntfs_allowed_as_owner(&security, ni) - || ntfs_remove_ntfs_object_id(ni)) - res = -errno; - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - } else - res = -errno; - break; - case XATTR_NTFS_DOS_NAME: - ni = ntfs_check_access_xattr(req,&security,ino,attr,TRUE); - if (ni) { - dir_ni = ntfs_dir_parent_inode(ni); - if (!dir_ni - || ntfs_remove_ntfs_dos_name(ni,dir_ni)) - res = -errno; - } else - res = -errno; - break; - default : - errno = EOPNOTSUPP; - res = -errno; - break; - } - return (res); -} - -static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) -{ - ntfs_inode *ni; - ntfschar *lename = NULL; - int res = 0, lename_len; - int attr; - int namespace; -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - struct SECURITY_CONTEXT security; -#endif - - attr = mapped_xattr_system(name); - if (attr != XATTR_UNMAPPED) { - /* - * hijack internal data and ACL removal, whatever - * mode was selected for xattr (from the user's - * point of view, ACLs are not xattr) - * Note : updating an ACL does not set ctime - */ - res = ntfs_system_removexattr(req, ino, attr); - 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 : - if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { - res = -EACCES; - goto exit; - } - break; - } -#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; - } - set_archive(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, - .bmap = ntfs_fuse_bmap, - .destroy = ntfs_fuse_destroy2, -#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 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__) */ -#if defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) - .init = ntfs_init -#endif -}; - -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; - - if (!ctx->blkdev) - flags |= MS_EXCLUSIVE; - if (ctx->ro) - flags |= MS_RDONLY; - if (ctx->recover) - flags |= MS_RECOVER; - if (ctx->hiberfile) - flags |= MS_IGNORE_HIBERFILE; - - ctx->vol = ntfs_mount(device, flags); - if (!ctx->vol) { - ntfs_log_perror("Failed to mount '%s'", device); - 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((fuse_req_t)NULL,FILE_root,"hiberfil.sys")) - goto err_out; - } - - errno = 0; -err_out: - return ntfs_volume_error(errno); - -} - -#define STRAPPEND_MAX_INSIZE 8192 -#define strappend_is_large(x) ((x) > STRAPPEND_MAX_INSIZE) - -static int strappend(char **dest, const char *append) -{ - char *p; - size_t size_append, size_dest = 0; - - if (!dest) - return -1; - if (!append) - return 0; - - size_append = strlen(append); - if (*dest) - size_dest = strlen(*dest); - - if (strappend_is_large(size_dest) || strappend_is_large(size_append)) { - errno = EOVERFLOW; - ntfs_log_perror("%s: Too large input buffer", EXEC_NAME); - return -1; - } - - p = (char*)realloc(*dest, size_dest + size_append + 1); - if (!p) { - ntfs_log_perror("%s: Memory reallocation failed", EXEC_NAME); - return -1; - } - - *dest = p; - strcpy(*dest + size_dest, append); - - return 0; -} - -static int bogus_option_value(char *val, const char *s) -{ - if (val) { - ntfs_log_error("'%s' option shouldn't have value.\n", s); - return -1; - } - return 0; -} - -static int missing_option_value(char *val, const char *s) -{ - if (!val) { - ntfs_log_error("'%s' option should have a value.\n", s); - return -1; - } - return 0; -} - -static char *parse_mount_options(const char *orig_opts) -{ - char *options, *s, *opt, *val, *ret = NULL; - BOOL no_def_opts = FALSE; - int default_permissions = 0; - int want_permissions = 0; - - ctx->secure_flags = 0; - ctx->efs_raw = FALSE; - options = strdup(orig_opts ? orig_opts : ""); - if (!options) { - ntfs_log_perror("%s: strdup failed", EXEC_NAME); - return NULL; - } - - s = options; - while (s && *s && (val = strsep(&s, ","))) { - opt = strsep(&val, "="); - if (!strcmp(opt, "ro")) { /* Read-only mount. */ - if (bogus_option_value(val, "ro")) - goto err_exit; - ctx->ro = TRUE; - if (strappend(&ret, "ro,")) - goto err_exit; - } else if (!strcmp(opt, "noatime")) { - if (bogus_option_value(val, "noatime")) - goto err_exit; - ctx->atime = ATIME_DISABLED; - } else if (!strcmp(opt, "atime")) { - if (bogus_option_value(val, "atime")) - goto err_exit; - ctx->atime = ATIME_ENABLED; - } else if (!strcmp(opt, "relatime")) { - if (bogus_option_value(val, "relatime")) - goto err_exit; - ctx->atime = ATIME_RELATIVE; - } else if (!strcmp(opt, "fake_rw")) { - if (bogus_option_value(val, "fake_rw")) - goto err_exit; - ctx->ro = TRUE; - } else if (!strcmp(opt, "fsname")) { /* Filesystem name. */ - /* - * We need this to be able to check whether filesystem - * mounted or not. - */ - ntfs_log_error("'fsname' is unsupported option.\n"); - goto err_exit; - } else if (!strcmp(opt, "no_def_opts")) { - if (bogus_option_value(val, "no_def_opts")) - goto err_exit; - no_def_opts = TRUE; /* Don't add default options. */ - } else if (!strcmp(opt, "default_permissions")) { - default_permissions = 1; - } else if (!strcmp(opt, "umask")) { - if (missing_option_value(val, "umask")) - goto err_exit; - sscanf(val, "%o", &ctx->fmask); - ctx->dmask = ctx->fmask; -#if !POSIXACLS - default_permissions = 1; -#endif - } else if (!strcmp(opt, "fmask")) { - if (missing_option_value(val, "fmask")) - goto err_exit; - sscanf(val, "%o", &ctx->fmask); - want_permissions = 1; - } else if (!strcmp(opt, "dmask")) { - if (missing_option_value(val, "dmask")) - goto err_exit; - sscanf(val, "%o", &ctx->dmask); - want_permissions = 1; - } else if (!strcmp(opt, "uid")) { - if (missing_option_value(val, "uid")) - goto err_exit; - sscanf(val, "%i", &ctx->uid); - want_permissions = 1; - } else if (!strcmp(opt, "gid")) { - if (missing_option_value(val, "gid")) - goto err_exit; - sscanf(val, "%i", &ctx->gid); - want_permissions = 1; - } else if (!strcmp(opt, "show_sys_files")) { - if (bogus_option_value(val, "show_sys_files")) - goto err_exit; - ctx->show_sys_files = TRUE; - } else if (!strcmp(opt, "silent")) { - if (bogus_option_value(val, "silent")) - goto err_exit; - ctx->silent = TRUE; - } else if (!strcmp(opt, "recover")) { - if (bogus_option_value(val, "recover")) - goto err_exit; - ctx->recover = TRUE; - } else if (!strcmp(opt, "norecover")) { - if (bogus_option_value(val, "norecover")) - goto err_exit; - ctx->recover = FALSE; - } else if (!strcmp(opt, "remove_hiberfile")) { - if (bogus_option_value(val, "remove_hiberfile")) - goto err_exit; - ctx->hiberfile = TRUE; - } else if (!strcmp(opt, "locale")) { - if (missing_option_value(val, "locale")) - goto err_exit; - ntfs_set_char_encoding(val); -#if defined(__APPLE__) || defined(__DARWIN__) -#ifdef ENABLE_NFCONV - } else if (!strcmp(opt, "nfconv")) { - if (bogus_option_value(val, "nfconv")) - goto err_exit; - if (ntfs_macosx_normalize_filenames(1)) { - ntfs_log_error("ntfs_macosx_normalize_filenames(1) failed!\n"); - goto err_exit; - } - } else if (!strcmp(opt, "nonfconv")) { - if (bogus_option_value(val, "nonfconv")) - goto err_exit; - if (ntfs_macosx_normalize_filenames(0)) { - ntfs_log_error("ntfs_macosx_normalize_filenames(0) failed!\n"); - goto err_exit; - } -#endif /* ENABLE_NFCONV */ -#endif /* defined(__APPLE__) || defined(__DARWIN__) */ - } else if (!strcmp(opt, "streams_interface")) { - if (missing_option_value(val, "streams_interface")) - goto err_exit; - if (!strcmp(val, "none")) - ctx->streams = NF_STREAMS_INTERFACE_NONE; - else if (!strcmp(val, "xattr")) - ctx->streams = NF_STREAMS_INTERFACE_XATTR; - else if (!strcmp(val, "openxattr")) - ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR; - else { - ntfs_log_error("Invalid named data streams " - "access interface.\n"); - goto err_exit; - } - } else if (!strcmp(opt, "user_xattr")) { - ctx->streams = NF_STREAMS_INTERFACE_XATTR; - } else if (!strcmp(opt, "noauto")) { - /* Don't pass noauto option to fuse. */ - } else if (!strcmp(opt, "debug")) { - if (bogus_option_value(val, "debug")) - goto err_exit; - ctx->debug = TRUE; - ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); - ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); - } else if (!strcmp(opt, "no_detach")) { - if (bogus_option_value(val, "no_detach")) - goto err_exit; - ctx->no_detach = TRUE; - } else if (!strcmp(opt, "remount")) { - ntfs_log_error("Remounting is not supported at present." - " You have to umount volume and then " - "mount it once again.\n"); - goto err_exit; - } else if (!strcmp(opt, "blksize")) { - ntfs_log_info("WARNING: blksize option is ignored " - "because ntfs-3g must calculate it.\n"); - } else if (!strcmp(opt, "inherit")) { - /* - * JPA do not overwrite inherited permissions - * in create() - */ - ctx->inherit = TRUE; - } else if (!strcmp(opt, "addsecurids")) { - /* - * JPA create security ids for files being read - * with an individual security attribute - */ - ctx->secure_flags |= (1 << SECURITY_ADDSECURIDS); - } else if (!strcmp(opt, "staticgrps")) { - /* - * JPA use static definition of groups - * for file access control - */ - ctx->secure_flags |= (1 << SECURITY_STATICGRPS); - } else if (!strcmp(opt, "usermapping")) { - if (!val) { - ntfs_log_error("'usermapping' option should have " - "a value.\n"); - goto err_exit; - } - ctx->usermap_path = strdup(val); - if (!ctx->usermap_path) { - ntfs_log_error("no more memory to store " - "'usermapping' option.\n"); - goto err_exit; - } - } else if (!strcmp(opt, "efs_raw")) { - if (bogus_option_value(val, "efs_raw")) - goto err_exit; - ctx->efs_raw = TRUE; - } else { /* Probably FUSE option. */ - if (strappend(&ret, opt)) - goto err_exit; - if (val) { - if (strappend(&ret, "=")) - goto err_exit; - if (strappend(&ret, val)) - goto err_exit; - } - if (strappend(&ret, ",")) - goto err_exit; - } - } - if (!no_def_opts && strappend(&ret, def_opts)) - goto err_exit; -#if KERNELPERMS - if (default_permissions && strappend(&ret, "default_permissions,")) - goto err_exit; -#endif - - if (ctx->atime == ATIME_RELATIVE && strappend(&ret, "relatime,")) - goto err_exit; - else if (ctx->atime == ATIME_ENABLED && strappend(&ret, "atime,")) - goto err_exit; - else if (ctx->atime == ATIME_DISABLED && strappend(&ret, "noatime,")) - goto err_exit; - - if (strappend(&ret, "fsname=")) - goto err_exit; - if (strappend(&ret, opts.device)) - goto err_exit; - if (default_permissions) - ctx->secure_flags |= (1 << SECURITY_DEFAULT); - if (want_permissions) - ctx->secure_flags |= (1 << SECURITY_WANTED); - if (ctx->ro) - ctx->secure_flags &= ~(1 << SECURITY_ADDSECURIDS); -exit: - free(options); - return ret; -err_exit: - free(ret); - ret = NULL; - goto exit; -} - -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); -} - -#ifndef HAVE_REALPATH -/* If there is no realpath() on the system, provide a dummy one. */ -static char *realpath(const char *path, char *resolved_path) -{ - strncpy(resolved_path, path, PATH_MAX); - resolved_path[PATH_MAX] = '\0'; - return resolved_path; -} -#endif - -/** - * parse_options - Read and validate the programs command line - * Read the command line, verify the syntax and parse the options. - * - * Return: 0 success, -1 error. - */ -static int parse_options(int argc, char *argv[]) -{ - int c; - - static const char *sopt = "-o:hvV"; - static const struct option lopt[] = { - { "options", required_argument, NULL, 'o' }, - { "help", no_argument, NULL, 'h' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - opterr = 0; /* We'll handle the errors, thank you. */ - - while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { - switch (c) { - case 1: /* A non-option argument */ - if (!opts.device) { - opts.device = (char*)ntfs_malloc(PATH_MAX + 1); - if (!opts.device) - return -1; - - /* Canonicalize device name (mtab, etc) */ - if (!realpath(optarg, opts.device)) { - ntfs_log_perror("%s: Failed to access " - "volume '%s'", EXEC_NAME, optarg); - free(opts.device); - opts.device = NULL; - return -1; - } - } else if (!opts.mnt_point) { - opts.mnt_point = optarg; - } else { - ntfs_log_error("%s: You must specify exactly one " - "device and exactly one mount " - "point.\n", EXEC_NAME); - return -1; - } - break; - case 'o': - if (opts.options) - if (strappend(&opts.options, ",")) - return -1; - if (strappend(&opts.options, optarg)) - return -1; - break; - case 'h': - usage(); - exit(9); - case 'v': - /* - * We must handle the 'verbose' option even if - * we don't use it because mount(8) passes it. - */ - break; - case 'V': - ntfs_log_info("%s %s %s %d\n", EXEC_NAME, VERSION, - FUSE_TYPE, fuse_version()); - exit(0); - default: - ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME, - argv[optind - 1]); - return -1; - } - } - - if (!opts.device) { - ntfs_log_error("%s: No device is specified.\n", EXEC_NAME); - return -1; - } - if (!opts.mnt_point) { - ntfs_log_error("%s: No mountpoint is specified.\n", EXEC_NAME); - return -1; - } - - return 0; -} - -#if defined(linux) || defined(__uClinux__) - -static const char *dev_fuse_msg = -"HINT: You should be root, or make ntfs-3g setuid root, or load the FUSE\n" -" kernel module as root ('modprobe fuse' or 'insmod /fuse.ko'" -" or insmod /fuse.o'). Make also sure that the fuse device" -" exists. It's usually either /dev/fuse or /dev/misc/fuse."; - -static const char *fuse26_kmod_msg = -"WARNING: Deficient Linux kernel detected. Some driver features are\n" -" not available (swap file on NTFS, boot from NTFS by LILO), and\n" -" unmount is not safe unless it's made sure the ntfs-3g process\n" -" naturally terminates after calling 'umount'. If you wish this\n" -" message to disappear then you should upgrade to at least kernel\n" -" version 2.6.20, or request help from your distribution to fix\n" -" the kernel problem. The below web page has more information:\n" -" http://ntfs-3g.org/support.html#fuse26\n" -"\n"; - -static void mknod_dev_fuse(const char *dev) -{ - struct stat st; - - if (stat(dev, &st) && (errno == ENOENT)) { - mode_t mask = umask(0); - if (mknod(dev, S_IFCHR | 0666, makedev(10, 229))) { - ntfs_log_perror("Failed to create '%s'", dev); - if (errno == EPERM) - ntfs_log_error("%s", dev_fuse_msg); - } - umask(mask); - } -} - -static void create_dev_fuse(void) -{ - mknod_dev_fuse("/dev/fuse"); - -#ifdef __UCLIBC__ - { - struct stat st; - /* The fuse device is under /dev/misc using devfs. */ - if (stat("/dev/misc", &st) && (errno == ENOENT)) { - mode_t mask = umask(0); - mkdir("/dev/misc", 0775); - umask(mask); - } - 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 }; /* 100 msec */ - fuse_fstype fstype; - - if (!stat(cmd, &st) && !geteuid()) { - pid = fork(); - if (!pid) { - execl(cmd, cmd, "fuse", NULL); - _exit(1); - } else if (pid != -1) - waitpid(pid, NULL, 0); - } - - for (i = 0; i < 10; i++) { - /* - * We sleep first because despite the detection of the loaded - * FUSE kernel module, fuse_mount() can still fail if it's not - * fully functional/initialized. Note, of course this is still - * unreliable but usually helps. - */ - nanosleep(&req, NULL); - fstype = get_fuse_fstype(); - if (fstype != FSTYPE_NONE) - break; - } - return fstype; -} - -#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 (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()); - 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; - fuse_fstype fstype = FSTYPE_UNKNOWN; - const char *permissions_mode = (const char*)NULL; - const char *failed_secure = (const char*)NULL; - struct stat sbuf; - 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 (parse_options(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(opts.options); - if (!parsed_options) { - err = NTFS_VOLUME_SYNTAX_ERROR; - 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 (!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); - goto err2; - } -#endif - err = ntfs_open(opts.device); - if (err) - 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; - ctx->vol->efs_raw = ctx->efs_raw; - /* 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)) { -#if POSIXACLS - if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) - permissions_mode = "User mapping built, Posix ACLs not used"; - else { - permissions_mode = "User mapping built, Posix ACLs in use"; -#if KERNELACLS - if (strappend(&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))) { - /* - * No explicit option but user mapping found - * force default security - */ -#if KERNELPERMS - ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); - if (strappend(&parsed_options, ",default_permissions")) { - err = NTFS_VOLUME_SYNTAX_ERROR; - goto err_out; - } -#endif /* KERNELPERMS */ - } - permissions_mode = "User mapping built"; -#endif /* POSIXACLS */ - } 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->secure_flags & (1 << SECURITY_WANTED)) - ctx->secure_flags |= (1 << SECURITY_DEFAULT); - if (ctx->secure_flags & (1 << SECURITY_DEFAULT)) { - ctx->secure_flags |= (1 << SECURITY_RAW); - permissions_mode = "Global ownership and permissions enforced"; - } else { - ctx->secure_flags &= ~(1 << SECURITY_RAW); - permissions_mode = "Ownership and permissions disabled"; - } - } - if (ctx->usermap_path) - free (ctx->usermap_path); - - se = mount_fuse(parsed_options); - if (!se) { - 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, - 5 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING); - - fuse_session_loop(se); - fuse_remove_signal_handlers(se); - - err = 0; - - fuse_unmount(opts.mnt_point, ctx->fc); - fuse_session_destroy(se); -err_out: - ntfs_mount_error(opts.device, opts.mnt_point, err); - if (ctx->abs_mnt_point) - free(ctx->abs_mnt_point); -err2: - ntfs_close(); - free(ctx); - free(parsed_options); - free(opts.options); - free(opts.device); - return err; -} -