193 files changed, 80411 insertions, 44563 deletions
diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index a943e60..25940c3 100755 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2010 Jean-Pierre Andre + * Copyright (c) 2007-2013 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -38,12 +38,6 @@ #error "***********************************************************" #endif -#ifdef FUSE_INTERNAL -#define FUSE_TYPE "integrated FUSE low" -#else -#define FUSE_TYPE "external FUSE low" -#endif - #ifdef HAVE_STDIO_H #include <stdio.h> #endif @@ -69,7 +63,6 @@ #ifdef HAVE_LIMITS_H #include <limits.h> #endif -#include <getopt.h> #include <syslog.h> #include <sys/wait.h> @@ -102,8 +95,11 @@ #include "object_id.h" #include "efs.h" #include "logging.h" +#include "xattrs.h" #include "misc.h" +#include "ntfs-3g_common.h" + /* * The following permission checking modes are governed by * the LPERMSCONFIG value in param.h @@ -121,18 +117,17 @@ #endif #if CACHEING & (KERNELACLS | !KERNELPERMS) -#warning "Fuse cacheing is broken unless basic permissions checked by kernel" +#warning "Fuse cacheing is only usable with 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 + /* + * FUSE cacheing is only usable with basic permissions + * checked by the kernel with external fuse >= 2.8 + */ #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 @@ -157,12 +152,6 @@ typedef enum { 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; @@ -187,57 +176,23 @@ struct open_file { 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 + CLOSE_ENCRYPTED = 4, + CLOSE_DMTIME = 8 }; -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; -#ifdef HAVE_SETXATTR /* extended attributes interface required */ - BOOL efs_raw; -#endif /* HAVE_SETXATTR */ - 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,"; +enum RM_TYPES { + RM_LINK, + RM_DIR, + RM_ANY, +} ; + +static struct ntfs_options opts; + +const char *EXEC_NAME = "lowntfs-3g"; + static ntfs_fuse_context_t *ctx; static u32 ntfs_sequence; static const char ghostformat[] = ".ghost-ntfs-3g-%020llu"; @@ -259,12 +214,12 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2010 Jean-Pierre Andre\n" +"Copyright (C) 2007-2013 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n" "\n" -"Options: ro (read-only mount), remove_hiberfile, uid=, gid=,\n" +"Options: ro (read-only mount), windows_names, uid=, gid=,\n" " umask=, fmask=, dmask=, streams_interface=.\n" " Please see the details in the manual (type: man ntfs-3g).\n" "\n" @@ -291,13 +246,14 @@ 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"; +"Please see more information at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; static const char *unpriv_fuseblk_msg = "Unprivileged user can not mount NTFS block devices using the external FUSE\n" "library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n" "FUSE support and make it setuid root. Please see more information at\n" -"http://ntfs-3g.org/support.html#unprivileged\n"; +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; #endif @@ -374,6 +330,11 @@ static u64 ntfs_fuse_inode_lookup(fuse_ino_t parent, const char *name) if (dir_ni) { /* Lookup file */ inum = ntfs_inode_lookup_by_mbsname(dir_ni, name); + /* never return inodes 0 and 1 */ + if (MREF(inum) <= 1) { + inum = (u64)-1; + errno = ENOENT; + } if (ntfs_inode_close(dir_ni) || (inum == (u64)-1)) ino = (u64)-1; @@ -444,7 +405,7 @@ static int ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, * * Returns 0 on success or -errno on error. */ - +#if HAVE_SYS_STATVFS_H static void ntfs_fuse_statfs(fuse_req_t req, fuse_ino_t ino __attribute__((unused))) { @@ -491,7 +452,7 @@ static void ntfs_fuse_statfs(fuse_req_t req, size += vol->free_mft_records; if (size < 0) size = 0; - sfs.f_ffree = /*sfs.f_favail =*/ size; + sfs.f_ffree = sfs.f_favail = size; /* Maximum length of filenames. */ sfs.f_namemax = NTFS_MAX_NAME_LEN; @@ -500,6 +461,7 @@ static void ntfs_fuse_statfs(fuse_req_t req, fuse_reply_err(req, ENODEV); } +#endif static void set_fuse_error(int *err) { @@ -603,7 +565,8 @@ int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv) } #endif /* defined(__APPLE__) || defined(__DARWIN__) */ -#if defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) +#if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \ + || (defined(__APPLE__) || defined(__DARWIN__)) static void ntfs_init(void *userdata __attribute__((unused)), struct fuse_conn_info *conn) { @@ -614,6 +577,12 @@ static void ntfs_init(void *userdata __attribute__((unused)), /* request umask not to be enforced by fuse */ conn->want |= FUSE_CAP_DONT_MASK; #endif /* defined FUSE_CAP_DONT_MASK */ +#ifdef FUSE_CAP_BIG_WRITES + if (ctx->big_writes + && ((ctx->vol->nr_clusters << ctx->vol->cluster_size_bits) + >= SAFE_CAPACITY_FOR_BIG_WRITES)) + conn->want |= FUSE_CAP_BIG_WRITES; +#endif } #endif /* defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) */ @@ -685,7 +654,9 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, * encrypted files to include padding required for decryption * also include 2 bytes for padding info */ - if (ctx->efs_raw && ni->flags & FILE_ATTR_ENCRYPTED) + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED) + && ni->data_size) stbuf->st_size = ((ni->data_size + 511) & ~511) + 2; #endif /* HAVE_SETXATTR */ /* @@ -697,7 +668,8 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, if (ni->flags & FILE_ATTR_SYSTEM) { na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { - goto exit; + stbuf->st_ino = ni->mft_no; + goto nodata; } /* Check whether it's Interix FIFO or socket. */ if (!(ni->flags & FILE_ATTR_HIDDEN)) { @@ -712,9 +684,9 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, * Check whether it's Interix symbolic link, block or * character device. */ - if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES) + if ((u64)na->data_size <= sizeof(INTX_FILE_TYPES) + sizeof(ntfschar) * PATH_MAX - && (size_t)na->data_size > + && (u64)na->data_size > sizeof(INTX_FILE_TYPES)) { INTX_FILE *intx_file; @@ -767,6 +739,7 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, } if (S_ISLNK(stbuf->st_mode)) stbuf->st_mode |= 0777; +nodata : stbuf->st_ino = ni->mft_no; #ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC stbuf->st_atimespec = ntfs2timespec(ni->last_access_time); @@ -878,6 +851,11 @@ static void ntfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent, #endif iref = ntfs_inode_lookup_by_mbsname(dir_ni, name); + /* never return inodes 0 and 1 */ + if (MREF(iref) <= 1) { + iref = (u64)-1; + errno = ENOENT; + } ok = !ntfs_inode_close(dir_ni) && (iref != (u64)-1) && ntfs_fuse_fillstat( @@ -1002,15 +980,34 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, (unsigned long long)MREF(mref)); return -1; } - - if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || - ctx->show_sys_files) { + /* never return inodes 0 and 1 */ + if (MREF(mref) > 1) { 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) + switch (dt_type) { + case NTFS_DT_DIR : st.st_mode = S_IFDIR | (0777 & ~ctx->dmask); + break; + case NTFS_DT_LNK : + st.st_mode = S_IFLNK | 0777; + break; + case NTFS_DT_FIFO : + st.st_mode = S_IFIFO; + break; + case NTFS_DT_SOCK : + st.st_mode = S_IFSOCK; + break; + case NTFS_DT_BLK : + st.st_mode = S_IFBLK; + break; + case NTFS_DT_CHR : + st.st_mode = S_IFCHR; + break; + default : /* unexpected types shown as plain files */ + case NTFS_DT_REG : + st.st_mode = S_IFREG | (0777 & ~ctx->fmask); + break; + } #if defined(__APPLE__) || defined(__DARWIN__) /* @@ -1122,14 +1119,17 @@ static void ntfs_fuse_releasedir(fuse_req_t req, 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); + if (fill && (fill->ino == ino)) { + /* make sure to clear results */ + current = fill->first; + while (current) { + current = current->next; + free(fill->first); + fill->first = current; + } + fill->ino = 0; + free(fill); + } fuse_reply_err(req, 0); } @@ -1145,7 +1145,7 @@ static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, int err = 0; fill = (ntfs_fuse_fill_context_t*)(long)fi->fh; - if (fill) { + if (fill && (fill->ino == ino)) { if (!fill->filled) { /* initial call : build the full list */ first = (ntfs_fuse_fill_item_t*)ntfs_malloc @@ -1250,6 +1250,12 @@ static void ntfs_fuse_open(fuse_req_t req, fuse_ino_t ino, && (ni->flags & FILE_ATTR_ENCRYPTED)) state |= CLOSE_ENCRYPTED; #endif /* HAVE_SETXATTR */ + /* mark a future need to update the mtime */ + if (ctx->dmtime) + state |= CLOSE_DMTIME; + /* deny opening metadata files for writing */ + if (ino < FILE_first_user) + res = -EPERM; } ntfs_attr_close(na); } else @@ -1290,8 +1296,10 @@ static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, s64 total = 0; s64 max_read; - if (!size) + if (!size) { + res = 0; goto exit; + } buf = (char*)ntfs_malloc(size); if (!buf) { res = -errno; @@ -1311,8 +1319,10 @@ static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, max_read = na->data_size; #ifdef HAVE_SETXATTR /* extended attributes interface required */ /* limit reads at next 512 byte boundary for encrypted attributes */ - if (ctx->efs_raw && (na->data_flags & ATTR_IS_ENCRYPTED) && - NAttrNonResident(na)) { + if (ctx->efs_raw + && max_read + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { max_read = ((na->data_size+511) & ~511) + 2; } #endif /* HAVE_SETXATTR */ @@ -1379,7 +1389,10 @@ static void ntfs_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, total += ret; } res = total; - if (res > 0) + if ((res > 0) + && (!ctx->dmtime + || (le64_to_cpu(ntfs_current_time()) + - le64_to_cpu(ni->last_data_change_time)) > ctx->dmtime)) ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); exit: if (na) @@ -1525,6 +1538,11 @@ static int ntfs_fuse_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, if (!ni) goto exit; + /* deny truncating metadata files */ + if (ino < FILE_first_user) { + errno = EPERM; + goto exit; + } na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) goto exit; @@ -1541,16 +1559,9 @@ static int ntfs_fuse_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, } #endif /* - * for compressed files, only deleting contents and expanding - * are implemented. Expanding is done by inserting a final + * for compressed files, upsizing 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)) { @@ -1574,7 +1585,7 @@ exit: return res; } -#ifdef HAVE_UTIMENSAT +#if defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) static int ntfs_fuse_utimens(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, struct stat *stin, struct stat *stbuf, int to_set) @@ -1621,7 +1632,7 @@ static int ntfs_fuse_utimens(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, return res; } -#else /* HAVE_UTIMENSAT */ +#else /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ static int ntfs_fuse_utime(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, struct stat *stin, struct stat *stbuf) @@ -1692,7 +1703,7 @@ static int ntfs_fuse_utime(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, return res; } -#endif /* HAVE_UTIMENSAT */ +#endif /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ static void ntfs_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi __attribute__((unused))) @@ -1767,11 +1778,11 @@ static void ntfs_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, } /* some set of atime/mtime */ if (!res && (to_set & (FUSE_SET_ATTR_ATIME + FUSE_SET_ATTR_MTIME))) { -#ifdef HAVE_UTIMENSAT +#if defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) res = ntfs_fuse_utimens(&security, ino, attr, &stbuf, to_set); -#else /* HAVE_UTIMENSAT */ +#else /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ res = ntfs_fuse_utime(&security, ino, attr, &stbuf); -#endif /* HAVE_UTIMENSAT */ +#endif /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ } if (res) fuse_reply_err(req, -res); @@ -1830,6 +1841,8 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, struct open_file *of; int state = 0; le32 securid; + gid_t gid; + mode_t dsetgid; mode_t type = typemode & ~07777; mode_t perm; struct SECURITY_CONTEXT security; @@ -1837,7 +1850,9 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, /* Generate unicode filename. */ uname_len = ntfs_mbstoucs(name, &uname); - if (uname_len < 0) { + if ((uname_len < 0) + || (ctx->windows_names + && ntfs_forbidden_chars(uname,uname_len))) { res = -errno; goto exit; } @@ -1850,13 +1865,15 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, #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)) { + || ntfs_allowed_create(&security, + dir_ni, &gid, &dsetgid)) { #else ntfs_fuse_fill_security_context(req, &security); + ntfs_allowed_create(&security, dir_ni, &gid, &dsetgid); #endif if (S_ISDIR(type)) - perm = typemode & ~ctx->dmask & 0777; + perm = (typemode & ~ctx->dmask & 0777) + | (dsetgid & S_ISGID); else perm = typemode & ~ctx->fmask & 0777; /* @@ -1874,11 +1891,11 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, else #if POSIXACLS securid = ntfs_alloc_securid(&security, - security.uid, security.gid, + security.uid, gid, dir_ni, perm, S_ISDIR(type)); #else securid = ntfs_alloc_securid(&security, - security.uid, security.gid, + security.uid, gid, perm & ~security.umask, S_ISDIR(type)); #endif /* Create object specified in @type. */ @@ -1912,13 +1929,13 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, #if POSIXACLS if (!securid && ntfs_set_inherited_posix(&security, ni, - security.uid, security.gid, + security.uid, gid, dir_ni, perm) < 0) set_fuse_error(&res); #else if (!securid && ntfs_set_owner_mode(&security, ni, - security.uid, security.gid, + security.uid, gid, perm & ~security.umask) < 0) set_fuse_error(&res); #endif @@ -1935,6 +1952,8 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, && (ni->flags & FILE_ATTR_ENCRYPTED)) state |= CLOSE_ENCRYPTED; #endif /* HAVE_SETXATTR */ + if (fi && ctx->dmtime) + state |= CLOSE_DMTIME; ntfs_inode_update_mbsname(dir_ni, name, ni->mft_no); NInoSetDirty(ni); e->ino = ni->mft_no; @@ -2039,9 +2058,17 @@ static int ntfs_fuse_newlink(fuse_req_t req __attribute__((unused)), goto exit; } + /* Do not accept linking to a directory (except for renaming) */ + if (e && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + errno = EPERM; + res = -errno; + goto exit; + } /* Generate unicode filename. */ uname_len = ntfs_mbstoucs(newname, &uname); - if (uname_len < 0) { + if ((uname_len < 0) + || (ctx->windows_names + && ntfs_forbidden_chars(uname,uname_len))) { res = -errno; goto exit; } @@ -2104,11 +2131,14 @@ static void ntfs_fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_reply_entry(req, &entry); } -static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name) +static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name, + enum RM_TYPES rm_type __attribute__((unused))) { ntfschar *uname = NULL; + ntfschar *ugname; ntfs_inode *dir_ni = NULL, *ni = NULL; int res = 0, uname_len; + int ugname_len; u64 iref; fuse_ino_t ino; struct open_file *of; @@ -2135,72 +2165,121 @@ static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name) 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; - } + /* deny unlinking metadata files */ + if (ino < FILE_first_user) { + res = -EPERM; + goto exit; } - ni = ntfs_inode_open(ctx->vol, MREF(iref)); + ni = ntfs_inode_open(ctx->vol, ino); if (!ni) { res = -errno; goto exit; } +#if defined(__sun) && defined (__SVR4) + /* on Solaris : deny unlinking directories */ + if (rm_type + == (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? RM_LINK : RM_DIR)) { + errno = EPERM; + res = -errno; + goto exit; + } +#endif /* defined(__sun) && defined (__SVR4) */ + #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) /* JPA deny unlinking if directory is not writable and executable */ - if (!ntfs_fuse_fill_security_context(req, &security) - || ntfs_allowed_dir_access(&security, dir_ni, ino, ni, + if (ntfs_fuse_fill_security_context(req, &security) + && !ntfs_allowed_dir_access(&security, dir_ni, ino, ni, S_IEXEC + S_IWRITE + S_ISVTX)) { + errno = EACCES; + res = -errno; + goto exit; + } #endif - if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni, - uname, uname_len)) + /* + * We keep one open_file record per opening, to avoid + * having to check the list of open files when opening + * and closing (which are more frequent than unlinking). + * As a consequence, we may have to create several + * ghosts names for the same file. + * The file may have been opened with a different name + * in a different parent directory. The ghost is + * nevertheless created in the parent directory of the + * name being unlinked, and permissions to do so are the + * same as required for unlinking. + */ + for (of=ctx->open_files; of; of = of->next) { + if ((of->ino == ino) && !(of->state & CLOSE_GHOST)) { + /* file was open, create a ghost in unlink parent */ + ntfs_inode *gni; + u64 gref; + + /* ni has to be closed for linking ghost */ + if (ni) { + if (ntfs_inode_close(ni)) { + res = -errno; + goto exit; + } + ni = (ntfs_inode*)NULL; + } + of->state |= CLOSE_GHOST; + of->parent = parent; + of->ghost = ++ctx->latest_ghost; + sprintf(ghostname,ghostformat,of->ghost); + /* Generate unicode filename. */ + ugname = (ntfschar*)NULL; + ugname_len = ntfs_mbstoucs(ghostname, &ugname); + if (ugname_len < 0) { + res = -errno; + goto exit; + } + /* sweep existing ghost if any, ignoring errors */ + gref = ntfs_inode_lookup_by_mbsname(dir_ni, ghostname); + if (gref != (u64)-1) { + gni = ntfs_inode_open(ctx->vol, MREF(gref)); + ntfs_delete(ctx->vol, (char*)NULL, gni, dir_ni, + ugname, ugname_len); + /* ntfs_delete() always closes gni and dir_ni */ + dir_ni = (ntfs_inode*)NULL; + } else { + if (ntfs_inode_close(dir_ni)) { + res = -errno; + goto out; + } + dir_ni = (ntfs_inode*)NULL; + } + free(ugname); + 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; + } + } + } + if (!ni) { + ni = ntfs_inode_open(ctx->vol, ino); + if (!ni) { res = -errno; + goto exit; + } + } + if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni, + uname, uname_len)) + res = -errno; /* ntfs_delete() always closes ni and dir_ni */ - ni = dir_ni = NULL; -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - } else - res = -EACCES; -#endif + ni = dir_ni = NULL; exit: - if (ntfs_inode_close(ni)) - set_fuse_error(&res); - if (ntfs_inode_close(dir_ni)) - set_fuse_error(&res); + if (ntfs_inode_close(ni) && !res) + res = -errno; + if (ntfs_inode_close(dir_ni) && !res) + res = -errno; out : free(uname); return res; @@ -2211,7 +2290,7 @@ static void ntfs_fuse_unlink(fuse_req_t req, fuse_ino_t parent, { int res; - res = ntfs_fuse_rm(req, parent, name); + res = ntfs_fuse_rm(req, parent, name, RM_LINK); if (res) fuse_reply_err(req, -res); else @@ -2232,7 +2311,7 @@ static int ntfs_fuse_safe_rename(fuse_req_t req, fuse_ino_t ino, if (ret) return ret; - ret = ntfs_fuse_rm(req, newparent, newname); + ret = ntfs_fuse_rm(req, newparent, newname, RM_ANY); if (!ret) { ret = ntfs_fuse_newlink(req, ino, newparent, newname, @@ -2240,9 +2319,9 @@ static int ntfs_fuse_safe_rename(fuse_req_t req, fuse_ino_t ino, if (ret) goto restore; - ret = ntfs_fuse_rm(req, parent, name); + ret = ntfs_fuse_rm(req, parent, name, RM_ANY); if (ret) { - if (ntfs_fuse_rm(req, newparent, newname)) + if (ntfs_fuse_rm(req, newparent, newname, RM_ANY)) goto err; goto restore; } @@ -2263,7 +2342,7 @@ cleanup: * fail (unless concurrent access to directories when fuse * is multithreaded) */ - if (ntfs_fuse_rm(req, newparent, tmp) < 0) + if (ntfs_fuse_rm(req, newparent, tmp, RM_ANY) < 0) ntfs_log_perror("Rename failed. Existing file '%s' still present " "as '%s'", newname, tmp); } @@ -2384,9 +2463,9 @@ static void ntfs_fuse_rename(fuse_req_t req, fuse_ino_t parent, if (ret) goto out; - ret = ntfs_fuse_rm(req, parent, name); + ret = ntfs_fuse_rm(req, parent, name, RM_ANY); if (ret) - ntfs_fuse_rm(req, newparent, newname); + ntfs_fuse_rm(req, newparent, newname, RM_ANY); } out: if (ret) @@ -2406,7 +2485,9 @@ static void ntfs_fuse_release(fuse_req_t req, fuse_ino_t ino, of = (struct open_file*)(long)fi->fh; /* Only for marked descriptors there is something to do */ - if (!of || !(of->state & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED))) { + if (!of + || !(of->state & (CLOSE_COMPRESSED + | CLOSE_ENCRYPTED | CLOSE_DMTIME))) { res = 0; goto out; } @@ -2421,6 +2502,8 @@ static void ntfs_fuse_release(fuse_req_t req, fuse_ino_t ino, goto exit; } res = 0; + if (of->state & CLOSE_DMTIME) + ntfs_inode_update_times(ni,NTFS_UPDATE_MCTIME); if (of->state & CLOSE_COMPRESSED) res = ntfs_attr_pclose(na); #ifdef HAVE_SETXATTR /* extended attributes interface required */ @@ -2437,7 +2520,7 @@ out: if (of) { if (of->state & CLOSE_GHOST) { sprintf(ghostname,ghostformat,of->ghost); - ntfs_fuse_rm(req, of->parent, ghostname); + ntfs_fuse_rm(req, of->parent, ghostname, RM_ANY); } /* remove from open files list */ if (of->next) @@ -2472,13 +2555,25 @@ 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); + res = ntfs_fuse_rm(req, parent, name, RM_DIR); if (res) fuse_reply_err(req, -res); else fuse_reply_err(req, 0); } +static void ntfs_fuse_fsync(fuse_req_t req, + fuse_ino_t ino __attribute__((unused)), + int type __attribute__((unused)), + struct fuse_file_info *fi __attribute__((unused))) +{ + /* sync the full device */ + if (ntfs_device_sync(ctx->vol->dev)) + fuse_reply_err(req, errno); + 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) { @@ -2538,67 +2633,15 @@ done : * Name space identifications and prefixes */ -enum { XATTRNS_NONE, +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; + XATTRNS_OPEN } ; -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 @@ -2671,21 +2714,6 @@ static ntfs_inode *ntfs_check_access_xattr(fuse_req_t req, } /* - * 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 */ @@ -2766,7 +2794,6 @@ 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) @@ -2781,9 +2808,11 @@ static void ntfs_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) ret = -errno; goto out; } + /* Return with no result for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) + goto exit; + /* otherwise file must be readable */ #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - /* file must be readable */ -// condition on fill_security ? if (!ntfs_allowed_access(&security,ni,S_IREAD)) { ret = -EACCES; goto exit; @@ -2801,72 +2830,14 @@ static void ntfs_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) 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); - } + ret = ntfs_fuse_listxattr_common(ni, actx, list, size, + ctx->streams == NF_STREAMS_INTERFACE_XATTR); + if (ret < 0) + goto exit; } - if (errno != ENOENT) ret = -errno; exit: @@ -2885,82 +2856,22 @@ out : 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_inode *dir_ni; ntfs_attr *na = NULL; char *value = (char*)NULL; ntfschar *lename = (ntfschar*)NULL; int lename_len; int res; s64 rsize; - int attr; + enum SYSTEMXATTRS attr; int namespace; struct SECURITY_CONTEXT security; - attr = mapped_xattr_system(name); + attr = ntfs_xattr_system_type(name,ctx->vol); if (attr != XATTR_UNMAPPED) { /* * hijack internal data and ACL retrieval, whatever @@ -2974,10 +2885,16 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, 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 + if (ntfs_allowed_access(&security,ni,S_IREAD)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_getxattr(&security, + attr, ni, dir_ni, value, size); + if (dir_ni && ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + } else res = -errno; if (ntfs_inode_close(ni)) set_fuse_error(&res); @@ -2993,8 +2910,14 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, if (ni) { /* user mapping not mandatory */ ntfs_fuse_fill_security_context(req, &security); - res = ntfs_system_getxattr(&security, - attr, ni, value, size); + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_getxattr(&security, + attr, ni, dir_ni, value, size); + if (dir_ni && ntfs_inode_close(dir_ni)) + set_fuse_error(&res); if (ntfs_inode_close(ni)) set_fuse_error(&res); } else @@ -3016,10 +2939,6 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, 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; @@ -3030,7 +2949,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, /* trusted only readable by root */ if ((namespace == XATTRNS_TRUSTED) && security.uid) { - res = -EPERM; + res = -ENODATA; goto out; } #endif @@ -3039,9 +2958,13 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, res = -errno; goto out; } + /* Return with no result for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -ENODATA; + goto exit; + } + /* otherwise file must be readable */ #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) - /* file must be readable */ -// condition on fill_security if (!ntfs_allowed_access(&security, ni, S_IREAD)) { res = -errno; goto exit; @@ -3058,10 +2981,11 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, 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 (ctx->efs_raw + && rsize + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) + rsize = ((na->data_size + 511) & ~511) + 2; if (size) { if (size >= (size_t)rsize) { value = (char*)ntfs_malloc(rsize); @@ -3091,75 +3015,21 @@ out : 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_inode *dir_ni; ntfs_attr *na = NULL; ntfschar *lename = NULL; int res, lename_len; - size_t part, total; - int attr; + size_t total; + s64 part; + enum SYSTEMXATTRS attr; int namespace; struct SECURITY_CONTEXT security; - attr = mapped_xattr_system(name); + attr = ntfs_xattr_system_type(name,ctx->vol); if (attr != XATTR_UNMAPPED) { /* * hijack internal data and ACL setting, whatever @@ -3171,8 +3041,13 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, 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 (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_setxattr(&security, + attr, ni, dir_ni, value, size, flags); + /* never have to close dir_ni */ if (res) res = -errno; } else @@ -3196,8 +3071,13 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, */ 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 (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_setxattr(&security, + attr, ni, dir_ni, value, size, flags); + /* never have to close dir_ni */ if (res) res = -errno; } else @@ -3208,6 +3088,18 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } else res = -errno; #endif +#if CACHEING && !defined(FUSE_INTERNAL) + /* + * Most of system xattr settings cause changes to some + * file attribute (st_mode, st_nlink, st_mtime, etc.), + * so we must invalidate cached data when cacheing is + * in use (not possible with internal fuse or external + * fuse before 2.8) + */ + if ((res >= 0) + && fuse_lowlevel_notify_inval_inode(ctx->fc, ino, -1, 0)) + res = -errno; +#endif if (res < 0) fuse_reply_err(req, -res); else @@ -3255,15 +3147,29 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } break; default : + /* User xattr not allowed for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -EPERM; + goto exit; + } if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { res = -EACCES; goto exit; } break; } +#else + /* User xattr not allowed for symlinks, fifo, etc. */ + if ((namespace == XATTRNS_USER) + && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + res = -EPERM; + goto exit; + } #endif lename_len = fix_xattr_prefix(name, namespace, &lename); - if (lename_len == -1) { + if ((lename_len == -1) + || (ctx->windows_names + && ntfs_forbidden_chars(lename,lename_len))) { res = -errno; goto exit; } @@ -3298,6 +3204,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } } total = 0; + res = 0; if (size) { do { part = ntfs_attr_pwrite(na, total, size - total, @@ -3305,20 +3212,20 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, 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 && !(ni->flags & FILE_ATTR_ARCHIVE)) { - set_archive(ni); - NInoFileNameSetDirty(ni); + } + if ((total != size) || ntfs_attr_pclose(na)) + res = -errno; + else { + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) { + if (ntfs_efs_fixup_attribute(NULL,na)) + res = -errno; } - } else - res = 0; + } + if (!res && !(ni->flags & FILE_ATTR_ARCHIVE)) { + set_archive(ni); + NInoFileNameSetDirty(ni); + } exit: if (na) ntfs_attr_close(na); @@ -3332,103 +3239,101 @@ out : 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; + ntfs_inode *dir_ni; ntfschar *lename = NULL; int res = 0, lename_len; - int attr; + enum SYSTEMXATTRS attr; int namespace; -#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) struct SECURITY_CONTEXT security; -#endif - attr = mapped_xattr_system(name); + attr = ntfs_xattr_system_type(name,ctx->vol); if (attr != XATTR_UNMAPPED) { + switch (attr) { + /* + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_ATTRIB_BE : + case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : + case XATTR_NTFS_TIMES_BE : + case XATTR_NTFS_CRTIME : + case XATTR_NTFS_CRTIME_BE : + res = -EPERM; + break; + default : +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ni = ntfs_check_access_xattr(req, &security, ino, + attr,TRUE); + if (ni) { + if (ntfs_allowed_as_owner(&security, ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_removexattr(&security, + attr, ni, dir_ni); + if (res) + res = -errno; + /* never have to close dir_ni */ + } 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)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_removexattr(&security, + attr, ni, dir_ni); + /* never have to close dir_ni */ + 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 CACHEING && !defined(FUSE_INTERNAL) /* - * 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 + * Some allowed system xattr removals cause changes to + * some file attribute (st_mode, st_nlink, etc.), + * so we must invalidate cached data when cacheing is + * in use (not possible with internal fuse or external + * fuse before 2.8) */ - res = ntfs_system_removexattr(req, ino, attr); + if ((res >= 0) + && fuse_lowlevel_notify_inval_inode(ctx->fc, + ino, -1, 0)) + res = -errno; +#endif + break; + } if (res < 0) fuse_reply_err(req, -res); else @@ -3476,12 +3381,24 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na } break; default : + /* User xattr not allowed for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -EPERM; + goto exit; + } if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { res = -EACCES; goto exit; } break; } +#else + /* User xattr not allowed for symlinks, fifo, etc. */ + if ((namespace == XATTRNS_USER) + && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + res = -EPERM; + goto exit; + } #endif lename_len = fix_xattr_prefix(name, namespace, &lename); if (lename_len == -1) { @@ -3567,7 +3484,9 @@ static struct fuse_lowlevel_ops ntfs_3g_ops = { .read = ntfs_fuse_read, .write = ntfs_fuse_write, .setattr = ntfs_fuse_setattr, +#if HAVE_SYS_STATVFS_H .statfs = ntfs_fuse_statfs, +#endif .create = ntfs_fuse_create_file, .mknod = ntfs_fuse_mknod, .symlink = ntfs_fuse_symlink, @@ -3576,6 +3495,8 @@ static struct fuse_lowlevel_ops ntfs_3g_ops = { .rename = ntfs_fuse_rename, .mkdir = ntfs_fuse_mkdir, .rmdir = ntfs_fuse_rmdir, + .fsync = ntfs_fuse_fsync, + .fsyncdir = ntfs_fuse_fsync, .bmap = ntfs_fuse_bmap, .destroy = ntfs_fuse_destroy2, #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) @@ -3594,7 +3515,8 @@ static struct fuse_lowlevel_ops ntfs_3g_ops = { .setbkuptime = ntfs_macfuse_setbkuptime, .setchgtime = ntfs_macfuse_setchgtime, #endif /* defined(__APPLE__) || defined(__DARWIN__) */ -#if defined(FUSE_CAP_DONT_MASK) || (defined(__APPLE__) || defined(__DARWIN__)) +#if defined(FUSE_CAP_DONT_MASK) || defined(FUSE_CAP_BIG_WRITES) \ + || (defined(__APPLE__) || defined(__DARWIN__)) .init = ntfs_init #endif }; @@ -3623,38 +3545,57 @@ static int ntfs_fuse_init(void) static int ntfs_open(const char *device) { unsigned long flags = 0; + ntfs_volume *vol; if (!ctx->blkdev) - flags |= MS_EXCLUSIVE; + flags |= NTFS_MNT_EXCLUSIVE; if (ctx->ro) - flags |= MS_RDONLY; + flags |= NTFS_MNT_RDONLY; if (ctx->recover) - flags |= MS_RECOVER; + flags |= NTFS_MNT_RECOVER; if (ctx->hiberfile) - flags |= MS_IGNORE_HIBERFILE; + flags |= NTFS_MNT_IGNORE_HIBERFILE; - ctx->vol = ntfs_mount(device, flags); - if (!ctx->vol) { + ctx->vol = vol = ntfs_mount(device, flags); + if (!vol) { ntfs_log_perror("Failed to mount '%s'", device); goto err_out; } + if (ctx->sync && ctx->vol->dev) + NDevSetSync(ctx->vol->dev); + if (ctx->compression) + NVolSetCompression(ctx->vol); + else + NVolClearCompression(ctx->vol); +#ifdef HAVE_SETXATTR + /* archivers must see hidden files */ + if (ctx->efs_raw) + ctx->hide_hid_files = FALSE; +#endif + if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files, + !ctx->hide_hid_files, ctx->hide_dot_files)) + goto err_out; + + if (ctx->ignore_case && ntfs_set_ignore_case(vol)) + goto err_out; - ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na); - if (ctx->vol->free_clusters < 0) { + vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na); + if (vol->free_clusters < 0) { ntfs_log_perror("Failed to read NTFS $Bitmap"); goto err_out; } - ctx->vol->free_mft_records = ntfs_get_nr_free_mft_records(ctx->vol); - if (ctx->vol->free_mft_records < 0) { + vol->free_mft_records = ntfs_get_nr_free_mft_records(vol); + if (vol->free_mft_records < 0) { ntfs_log_perror("Failed to calculate free MFT records"); goto err_out; } - if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) { + if (ctx->hiberfile && ntfs_volume_check_hiberfile(vol, 0)) { if (errno != EPERM) goto err_out; - if (ntfs_fuse_rm((fuse_req_t)NULL,FILE_root,"hiberfil.sys")) + if (ntfs_fuse_rm((fuse_req_t)NULL,FILE_root,"hiberfil.sys", + RM_LINK)) goto err_out; } @@ -3664,300 +3605,6 @@ err_out: } -#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; -#ifdef HAVE_SETXATTR /* extended attributes interface required */ - ctx->efs_raw = FALSE; -#endif /* HAVE_SETXATTR */ - 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; - want_permissions = 1; - } 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; - } -#ifdef HAVE_SETXATTR /* extended attributes interface required */ - } else if (!strcmp(opt, "efs_raw")) { - if (bogus_option_value(val, "efs_raw")) - goto err_exit; - ctx->efs_raw = TRUE; -#endif /* HAVE_SETXATTR */ - } 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(), @@ -3965,101 +3612,6 @@ static void usage(void) 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 = @@ -4076,7 +3628,7 @@ static const char *fuse26_kmod_msg = " 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" +" http://tuxera.com/community/ntfs-3g-faq/#fuse26\n" "\n"; static void mknod_dev_fuse(const char *dev) @@ -4205,7 +3757,7 @@ static int set_fuseblk_options(char **parsed_options) blksize = pagesize; snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize); - if (strappend(parsed_options, options)) + if (ntfs_strappend(parsed_options, options)) return -1; return 0; } @@ -4261,6 +3813,9 @@ static void setup_logging(char *parsed_options) ctx->seccache = (struct PERMISSIONS_CACHE*)NULL; ntfs_log_info("Version %s %s %d\n", VERSION, FUSE_TYPE, fuse_version()); + if (strcmp(opts.arg_device,opts.device)) + ntfs_log_info("Requested device %s canonicalized as %s\n", + opts.arg_device,opts.device); ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n", opts.device, (ctx->ro) ? "Read-Only" : "Read-Write", ctx->vol->vol_name, ctx->vol->major_ver, @@ -4278,7 +3833,11 @@ int main(int argc, char *argv[]) #endif const char *permissions_mode = (const char*)NULL; const char *failed_secure = (const char*)NULL; +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL; +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ struct stat sbuf; + unsigned long existing_mount; int err, fd; /* @@ -4303,7 +3862,7 @@ int main(int argc, char *argv[]) ntfs_set_locale(); ntfs_log_set_handler(ntfs_log_handler_stderr); - if (parse_options(argc, argv)) { + if (ntfs_parse_options(&opts, usage, argc, argv)) { usage(); return NTFS_VOLUME_SYNTAX_ERROR; } @@ -4313,12 +3872,19 @@ int main(int argc, char *argv[]) goto err2; } - parsed_options = parse_mount_options(opts.options); + parsed_options = parse_mount_options(ctx, &opts, TRUE); if (!parsed_options) { err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; } - + if (!ntfs_check_if_mounted(opts.device,&existing_mount) + && (existing_mount & NTFS_MF_MOUNTED) + /* accept multiple read-only mounts */ + && (!(existing_mount & NTFS_MF_READONLY) || !ctx->ro)) { + err = NTFS_VOLUME_LOCKED; + goto err_out; + } + /* need absolute mount point for junctions */ if (opts.mnt_point[0] == '/') ctx->abs_mnt_point = strdup(opts.mnt_point); @@ -4329,6 +3895,10 @@ int main(int argc, char *argv[]) PATH_MAX - strlen(opts.mnt_point) - 1)) { strcat(ctx->abs_mnt_point, "/"); strcat(ctx->abs_mnt_point, opts.mnt_point); +#if defined(__sun) && defined (__SVR4) + /* Solaris also wants the absolute mount point */ + opts.mnt_point = ctx->abs_mnt_point; +#endif /* defined(__sun) && defined (__SVR4) */ } } } @@ -4383,6 +3953,12 @@ int main(int argc, char *argv[]) if (err) goto err_out; + /* Force read-only mount if the device was found read-only */ + if (!ctx->ro && NVolReadOnly(ctx->vol)) { + ctx->ro = TRUE; + if (ntfs_strinsert(&parsed_options, ",ro")) + goto err_out; + } /* We must do this after ntfs_open() to be able to set the blksize */ if (ctx->blkdev && set_fuseblk_options(&parsed_options)) goto err_out; @@ -4396,14 +3972,18 @@ int main(int argc, char *argv[]) /* 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 (!ntfs_build_mapping(&ctx->security,ctx->usermap_path, + (ctx->vol->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL))) + && !(ctx->vol->secure_flags & (1 << SECURITY_WANTED)))) { #if POSIXACLS + /* use basic permissions if requested */ if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) permissions_mode = "User mapping built, Posix ACLs not used"; else { permissions_mode = "User mapping built, Posix ACLs in use"; #if KERNELACLS - if (strappend(&parsed_options, + if (ntfs_strinsert(&parsed_options, ",default_permissions,acl")) { err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; @@ -4411,14 +3991,15 @@ int main(int argc, char *argv[]) #endif /* KERNELACLS */ } #else /* POSIXACLS */ - if (!(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) { + if (!(ctx->vol->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) { /* * No explicit option but user mapping found * force default security */ #if KERNELPERMS ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); - if (strappend(&parsed_options, ",default_permissions")) { + if (ntfs_strinsert(&parsed_options, ",default_permissions")) { err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; } @@ -4435,7 +4016,7 @@ int main(int argc, char *argv[]) if ((ctx->vol->secure_flags & (1 << SECURITY_WANTED)) && !(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) { ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); - if (strappend(&parsed_options, ",default_permissions")) { + if (ntfs_strinsert(&parsed_options, ",default_permissions")) { err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; } @@ -4451,6 +4032,18 @@ int main(int argc, char *argv[]) if (ctx->usermap_path) free (ctx->usermap_path); +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + xattr_mapping = ntfs_xattr_build_mapping(ctx->vol, + ctx->xattrmap_path); + ctx->vol->xattr_mapping = xattr_mapping; + /* + * Errors are logged, do not refuse mounting, it would be + * too difficult to fix the unmountable mapping file. + */ + if (ctx->xattrmap_path) + free(ctx->xattrmap_path); +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ + se = mount_fuse(parsed_options); if (!se) { err = NTFS_VOLUME_FUSE_ERROR; @@ -4481,6 +4074,9 @@ err_out: ntfs_mount_error(opts.device, opts.mnt_point, err); if (ctx->abs_mnt_point) free(ctx->abs_mnt_point); +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + ntfs_xattr_free_mapping(xattr_mapping); +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ err2: ntfs_close(); free(ctx); |