From 8de4c3457838b31d5a67e400168aef12e33e00ba Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 26 Sep 2007 19:13:32 +0000 Subject: [PATCH 001/328] try_fuse_mount(): remove non-specific, redundant error message --- src/ntfs-3g.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 480b3c6c..ea961184 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2147,8 +2147,6 @@ static struct fuse_chan *try_fuse_mount(char *parsed_options) } fc = fuse_mount(opts.mnt_point, &margs); - if (!fc) - ntfs_log_error("FUSE mount point creation failed\n"); free_args: fuse_opt_free_args(&margs); return fc; From 8796b5ee89b98c12bb96ab6f3b36aba8c4670e86 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 26 Sep 2007 19:20:10 +0000 Subject: [PATCH 002/328] ntfs_volume_check_hiberfile(): remove unused variable --- libntfs-3g/volume.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 1a147dbb..030640da 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -681,7 +681,7 @@ static int ntfs_volume_check_hiberfile(ntfs_volume *vol) { ntfs_inode *ni; ntfs_attr *na = NULL; - int i, bytes_read, err; + int bytes_read, err; char *buf = NULL; ni = ntfs_hiberfile_open(vol); From ffa355c0f8eebe26c9ba200d3bdc31a4e27793b1 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 26 Sep 2007 19:32:26 +0000 Subject: [PATCH 003/328] statfs() and free inode calculation is O(1) (David Fox, Szabolcs Szakacsits) --- include/ntfs-3g/volume.h | 1 + libntfs-3g/mft.c | 6 ++++- src/ntfs-3g.c | 54 +++++++++++----------------------------- 3 files changed, 20 insertions(+), 41 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 3020665f..b9f8023a 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -214,6 +214,7 @@ struct _ntfs_volume { s64 free_clusters; /* Track the number of free clusters which greatly improves statfs() performance */ + s64 free_mft_records; /* Same for free mft records (see above) */ /* Temp: for directory handling */ void *private_data; /* ntfs_dir for . */ diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 7171cf6f..251a667f 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -797,6 +797,7 @@ static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll); if (ll == 8) { ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); + vol->free_mft_records += (8 * 8); return 0; } ntfs_log_error("Failed to write to mft bitmap.\n"); @@ -1440,6 +1441,7 @@ found_free_rec: /* Return the opened, allocated inode of the allocated mft record. */ ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n", base_ni ? "extent " : "", (long long)bit); + vol->free_mft_records--; return ni; undo_mftbmp_alloc: @@ -1508,8 +1510,10 @@ int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) } /* Throw away the now freed inode. */ - if (!ntfs_inode_close(ni)) + if (!ntfs_inode_close(ni)) { + vol->free_mft_records++; return 0; + } err = errno; /* Rollback what we did... */ diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index ea961184..0a50ef92 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -105,8 +105,6 @@ typedef enum { typedef struct { ntfs_volume *vol; - int state; - long free_mft; unsigned int uid; unsigned int gid; unsigned int fmask; @@ -121,11 +119,6 @@ typedef struct { BOOL no_detach; } ntfs_fuse_context_t; -typedef enum { - NF_FreeMFTOutdate = (1 << 1), /* Information about amount of - free MFT records is outdated. */ -} ntfs_fuse_state_bits; - static struct options { char *mnt_point; /* Mount point */ char *options; /* Mount options */ @@ -162,11 +155,6 @@ static const char *usage_msg = "\n" "%s"; -static __inline__ void ntfs_fuse_mark_free_space_outdated(void) -{ - /* Mark information about free MFT records outdated. */ - ctx->state |= NF_FreeMFTOutdate; -} /** * ntfs_fuse_is_named_data_stream - check path to be to named data stream @@ -181,14 +169,12 @@ static __inline__ int ntfs_fuse_is_named_data_stream(const char *path) return 0; } -static long ntfs_fuse_get_nr_free_mft_records(ntfs_volume *vol, s64 numof_inode) +static long ntfs_get_nr_free_mft_records(ntfs_volume *vol) { u8 *buf; long nr_free = 0; s64 br, total = 0; - if (!(ctx->state & NF_FreeMFTOutdate)) - return ctx->free_mft; buf = ntfs_malloc(vol->cluster_size); if (!buf) return -errno; @@ -201,20 +187,15 @@ static long ntfs_fuse_get_nr_free_mft_records(ntfs_volume *vol, s64 numof_inode) break; total += br; for (i = 0; i < br; i++) - for (j = 0; j < 8; j++) { - - if (--numof_inode < 0) - break; - + for (j = 0; j < 8; j++) if (!((buf[i] >> j) & 1)) nr_free++; - } } free(buf); if (!total || br < 0) return -errno; - ctx->free_mft = nr_free; - ctx->state &= ~(NF_FreeMFTOutdate); + + nr_free += (vol->mftbmp_na->allocated_size - vol->mftbmp_na->data_size) << 3; return nr_free; } @@ -266,8 +247,8 @@ static long ntfs_get_nr_free_clusters(ntfs_volume *vol) static int ntfs_fuse_statfs(const char *path __attribute__((unused)), struct statvfs *sfs) { - long size, delta_bits; - u64 allocated_inodes; + s64 size; + int delta_bits; ntfs_volume *vol; vol = ctx->vol; @@ -300,11 +281,10 @@ static int ntfs_fuse_statfs(const char *path __attribute__((unused)), size >>= -delta_bits; /* Number of inodes in file system (at this point in time). */ - allocated_inodes = vol->mft_na->data_size >> vol->mft_record_size_bits; - sfs->f_files = allocated_inodes + size; + sfs->f_files = (vol->mftbmp_na->allocated_size << 3) + size; /* Free inodes in fs (based on current total count). */ - size = ntfs_fuse_get_nr_free_mft_records(vol, allocated_inodes) + size; + size += vol->free_mft_records; if (size < 0) size = 0; sfs->f_ffree = size; @@ -747,7 +727,6 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, } res = total; exit: - ntfs_fuse_mark_free_space_outdated(); if (na) ntfs_attr_close(na); if (ni && ntfs_inode_close(ni)) @@ -784,7 +763,6 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) if (ntfs_attr_truncate(na, size)) goto exit; - ntfs_fuse_mark_free_space_outdated(); ntfs_attr_close(na); errno = 0; exit: @@ -926,7 +904,6 @@ static int ntfs_fuse_create_file(const char *org_path, mode_t mode, else res = ntfs_fuse_create_stream(path, stream_name, stream_name_len); - ntfs_fuse_mark_free_space_outdated(); free(path); if (stream_name_len) free(stream_name); @@ -952,7 +929,6 @@ static int ntfs_fuse_mknod(const char *org_path, mode_t mode, dev_t dev) else res = ntfs_fuse_create_stream(path, stream_name, stream_name_len); - ntfs_fuse_mark_free_space_outdated(); exit: free(path); if (stream_name_len) @@ -964,7 +940,6 @@ static int ntfs_fuse_symlink(const char *to, const char *from) { if (ntfs_fuse_is_named_data_stream(from)) return -EINVAL; /* n/a for named data streams. */ - ntfs_fuse_mark_free_space_outdated(); return ntfs_fuse_create(from, S_IFLNK, 0, to); } @@ -1014,7 +989,6 @@ static int ntfs_fuse_ln(const char *old_path, const char *new_path, int mtime) res = -errno; goto exit; } - ntfs_fuse_mark_free_space_outdated(); /* Create hard link. */ if (ntfs_link(ni, dir_ni, uname, uname_len)) res = -errno; @@ -1115,7 +1089,6 @@ static int ntfs_fuse_unlink(const char *org_path) res = ntfs_fuse_rm(path); else res = ntfs_fuse_rm_stream(path, stream_name, stream_name_len); - ntfs_fuse_mark_free_space_outdated(); free(path); if (stream_name_len) free(stream_name); @@ -1239,7 +1212,6 @@ static int ntfs_fuse_mkdir(const char *path, { if (ntfs_fuse_is_named_data_stream(path)) return -EINVAL; /* n/a for named data streams. */ - ntfs_fuse_mark_free_space_outdated(); return ntfs_fuse_create(path, S_IFDIR, 0, NULL); } @@ -1247,7 +1219,6 @@ static int ntfs_fuse_rmdir(const char *path) { if (ntfs_fuse_is_named_data_stream(path)) return -EINVAL; /* n/a for named data streams. */ - ntfs_fuse_mark_free_space_outdated(); return ntfs_fuse_rm(path); } @@ -1543,7 +1514,6 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = -EEXIST; goto exit; } - ntfs_fuse_mark_free_space_outdated(); if (!na) { if (flags == XATTR_REPLACE) { res = -ENODATA; @@ -1603,7 +1573,6 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) res = -errno; } - ntfs_fuse_mark_free_space_outdated(); exit: free(lename); if (ntfs_inode_close(ni)) @@ -1672,7 +1641,6 @@ static int ntfs_fuse_init(void) return -1; *ctx = (ntfs_fuse_context_t) { - .state = NF_FreeMFTOutdate, .uid = getuid(), .gid = getgid(), .fmask = 0, @@ -2253,6 +2221,12 @@ int main(int argc, char *argv[]) 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 (use_blkdev) { set_fuseblk_options(parsed_options); From f557e5f6bac101360c4632b6a227cc5bae14699c Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 26 Sep 2007 20:40:18 +0000 Subject: [PATCH 004/328] optimization: find(1) is 20-200% faster for disk based and 300-600% faster for memory cache based directory traversals --- libntfs-3g/dir.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 52fcb66b..28d81efd 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -578,6 +578,8 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, return 0; if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) dt_type = NTFS_DT_DIR; + else if (fn->file_attributes & FILE_ATTR_SYSTEM) + dt_type = NTFS_DT_UNKNOWN; else dt_type = NTFS_DT_REG; return filldir(dirent, fn->file_name, fn->file_name_length, From 91604e7edd778ff7556e6020d9c5489afcd3fc98 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 27 Sep 2007 22:15:03 +0000 Subject: [PATCH 005/328] optimization: find(1) is 20-200% faster for disk based and 300-600% faster for memory cache based directory traversals --- src/ntfs-3g.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 0a50ef92..ab63eb31 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -568,7 +568,14 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || ctx->show_sys_files) { - struct stat st = { .st_ino = MREF(mref) }; + struct stat st = {}; + + 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); ret = fill_ctx->filler(fill_ctx->buf, filename, &st, 0); } From 2caeb1b4e4e5ea2ae9da4e28f5d3f44f0b4b078c Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 3 Oct 2007 21:04:53 +0000 Subject: [PATCH 006/328] max_empty_bit_range(): 30-50% speed up (David Fox) --- libntfs-3g/lcnalloc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index 073141d0..75f8b6ab 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -51,7 +51,7 @@ * the update of the mapping pairs which converges to the cubic Faulhaber's * formula as the function of the number of extents (fragments, runs). */ -#define NTFS_LCNALLOC_BSIZE 8192 +#define NTFS_LCNALLOC_BSIZE 4096 #define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) @@ -89,6 +89,11 @@ static s64 max_empty_bit_range(unsigned char *buf, int size) for (i = 0; i < size; i++, buf++) { + if (*buf == 0) { + run += 8; + continue; + } + for (j = 0; j < 8; j++) { int bit = *buf & (1 << j); From d4b4b41191656ae22b9ef9d65c679a3435ea3618 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 3 Oct 2007 21:09:30 +0000 Subject: [PATCH 007/328] fix: unwritten sparse file regions could get corrupted if the end of a write wasn't aligned to cluster boundary. --- libntfs-3g/attrib.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index feb28489..46a8bde2 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1098,7 +1098,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) ntfs_volume *vol; ntfs_attr_search_ctx *ctx = NULL; runlist_element *rl; - int eo; + s64 eo, hole; struct { unsigned int undo_initialized_size : 1; unsigned int undo_data_size : 1; @@ -1245,7 +1245,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * length. */ ofs = pos - (rl->vcn << vol->cluster_size_bits); - for (; count; rl++, ofs = 0) { + for (hole = 0; count; rl++, ofs = 0, hole = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { @@ -1265,6 +1265,8 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) goto rl_err_out; } if (rl->lcn < (LCN)0) { + + hole = rl->vcn + rl->length; if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; @@ -1284,20 +1286,26 @@ retry: if (!NVolReadOnly(vol)) { s64 pos = (rl->lcn << vol->cluster_size_bits) + ofs; - int bsize = vol->cluster_size; + u32 bsize = vol->cluster_size; + s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; + s64 rounded = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; /* - * Write cluster size blocks if it's possible. This will - * cause the kernel not to seek and read disk blocks for - * filling the end of the buffer which increases write - * speed at least by 2-11 fold typically. + * Zero fill to cluster boundary if we're writing to an + * ex-sparse cluster or we're at the end of the attribute. + * This will cause the kernel not to seek and read disk + * blocks during write(2) to fill the end of the buffer + * which increases write speed by 2-10 fold typically. */ - if (!(ofs % bsize) && (to_write % bsize) && - ((ofs + to_write) == na->initialized_size)) { + if ((rounded && (wend < (hole << vol->cluster_size_bits))) || + (((to_write % bsize) && + (ofs + to_write == na->initialized_size)))) { - s64 rounded = (to_write + bsize - 1) & ~(bsize - 1); - char *cb = ntfs_malloc(rounded); + char *cb; + rounded += to_write; + + cb = ntfs_malloc(rounded); if (!cb) goto err_out; From ecd3d2dd23dde685a4af1a0d1b9d1a8aa2243610 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 3 Oct 2007 21:10:38 +0000 Subject: [PATCH 008/328] release 1.1004 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4401d644..0391a114 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.913],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=12:0:0 +AC_INIT([ntfs-3g],[1.1004],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=13:0:0 AC_CANONICAL_HOST([]) AC_CANONICAL_TARGET([]) From 925c1cff30e08ae83281091e2e1627ab0852d3e4 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 8 Oct 2007 22:02:38 +0000 Subject: [PATCH 009/328] fix warnings --- include/ntfs-3g/logging.h | 3 ++- libntfs-3g/attrib.c | 10 +++++----- libntfs-3g/logging.c | 14 +++++++------- libntfs-3g/security.c | 2 +- src/ntfs-3g.c | 6 ++---- src/utils.c | 2 +- src/utils.h | 2 +- 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/include/ntfs-3g/logging.h b/include/ntfs-3g/logging.h index bc84734f..ae36210a 100644 --- a/include/ntfs-3g/logging.h +++ b/include/ntfs-3g/logging.h @@ -37,7 +37,8 @@ typedef int (ntfs_log_handler)(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args); /* Set the logging handler from one of the functions, below. */ -void ntfs_log_set_handler(ntfs_log_handler *handler); +void ntfs_log_set_handler(ntfs_log_handler *handler + __attribute__((format(printf, 6, 0)))); /* Logging handlers */ ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 46a8bde2..d7b574f6 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1285,10 +1285,10 @@ retry: "%lld.\n", to_write, rl->vcn, rl->lcn, ofs); if (!NVolReadOnly(vol)) { - s64 pos = (rl->lcn << vol->cluster_size_bits) + ofs; - u32 bsize = vol->cluster_size; + s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; - s64 rounded = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; + u32 bsize = vol->cluster_size; + s64 rounded = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; /* * Zero fill to cluster boundary if we're writing to an @@ -1312,13 +1312,13 @@ retry: memcpy(cb, b, to_write); memset(cb + to_write, 0, rounded - to_write); - written = ntfs_pwrite(vol->dev, pos, rounded, cb); + written = ntfs_pwrite(vol->dev, wpos, rounded, cb); if (written == rounded) written = to_write; free(cb); } else - written = ntfs_pwrite(vol->dev, pos, to_write, b); + written = ntfs_pwrite(vol->dev, wpos, to_write, b); } else written = to_write; /* If everything ok, update progress counters and continue. */ diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index 90a20cf1..7d3fe735 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -67,7 +67,7 @@ static const char *col_end = "\e[0m"; struct ntfs_logging { u32 levels; u32 flags; - ntfs_log_handler *handler; + ntfs_log_handler *handler __attribute__((format(printf, 6, 0))); }; /** @@ -355,14 +355,14 @@ int ntfs_log_handler_syslog(const char *function __attribute__((unused)), void *data __attribute__((unused)), const char *format, va_list args) { - char log[LOG_LINE_LEN]; + char logbuf[LOG_LINE_LEN]; int ret, olderr = errno; #ifndef DEBUG if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) return 1; #endif - ret = vsnprintf(log, LOG_LINE_LEN, format, args); + ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); if (ret < 0) { vsyslog(LOG_NOTICE, format, args); ret = 1; @@ -370,12 +370,12 @@ int ntfs_log_handler_syslog(const char *function __attribute__((unused)), } if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { - strncat(log, ": ", LOG_LINE_LEN - ret - 1); - strncat(log, strerror(olderr), LOG_LINE_LEN - (ret + 3)); - ret = strlen(log); + strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); + strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); + ret = strlen(logbuf); } - syslog(LOG_NOTICE, "%s", log); + syslog(LOG_NOTICE, "%s", logbuf); out: errno = olderr; return ret; diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index 2729ae08..489edc65 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -357,7 +357,7 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) */ le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) { - const le32 *pos = (le32 *)sd; + const le32 *pos = (const le32 *)sd; const le32 *end = pos + (len >> 2); u32 hash = 0; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index ab63eb31..a308009e 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -568,10 +568,8 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || ctx->show_sys_files) { - struct stat st = {}; + struct stat st = { .st_ino = MREF(mref) }; - 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) @@ -1670,7 +1668,7 @@ static ntfs_volume *ntfs_open(const char *device, char *mntpoint, int blkdev) if (ctx->force) flags |= MS_FORCE; - ctx->vol = utils_mount_volume(device, mntpoint, flags, ctx->force); + ctx->vol = utils_mount_volume(device, mntpoint, flags); return ctx->vol; } diff --git a/src/utils.c b/src/utils.c index d066a0d1..2b14f74d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -110,7 +110,7 @@ int utils_set_locale(void) } ntfs_volume *utils_mount_volume(const char *volume, const char *mntpoint, - unsigned long flags, BOOL force) + unsigned long flags) { ntfs_volume *vol; diff --git a/src/utils.h b/src/utils.h index eccc9a95..400d465f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -34,6 +34,6 @@ extern const char *ntfs_gpl; int utils_set_locale(void); ntfs_volume *utils_mount_volume(const char *device, const char *mntpoint, - unsigned long flags, BOOL force); + unsigned long flags); #endif /* _NTFS_UTILS_H_ */ From ff5d2522ccf91c4a013cc3f68e7e13078572ea29 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 10 Oct 2007 19:19:57 +0000 Subject: [PATCH 010/328] remove bogus format attribute which caused some gcc to crash --- libntfs-3g/logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index 7d3fe735..228e90e0 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -67,7 +67,7 @@ static const char *col_end = "\e[0m"; struct ntfs_logging { u32 levels; u32 flags; - ntfs_log_handler *handler __attribute__((format(printf, 6, 0))); + ntfs_log_handler *handler; }; /** From 358b9a8d90f1813ff59a4fc51ba8d70fbb1945d2 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 10 Oct 2007 19:56:15 +0000 Subject: [PATCH 011/328] try to better explain fuse installation errors --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0391a114..c3d93e1a 100644 --- a/configure.ac +++ b/configure.ac @@ -118,7 +118,7 @@ case "$target_os" in linux*|darwin*|netbsd*) PKG_CHECK_MODULES(FUSE_MODULE, fuse >= 2.6.0, [ compile_fuse_module=true ], [ - AC_MSG_ERROR([ntfs-3g requires FUSE >= 2.6.0. Please see http://fuse.sf.net/ or install __all__ FUSE packages (e.g. fuse, fuse-utils, libfuse, libfuse2, libfuse-dev) or remove already installed __older__ FUSE.]) + AC_MSG_ERROR([FUSE >= 2.6.0 was not found. Either it's not fully installed (e.g. fuse, fuse-utils, libfuse, libfuse2, libfuse-dev, etc packages) or files from an old version are still present. See FUSE at http://fuse.sf.net/]) ]);; freebsd*) AC_MSG_ERROR([Please see FreeBSD support at http://www.freshports.org/sysutils/fusefs-ntfs]) From 4e85b2465ddcf31c9e0b3596699726d0b07d06fc Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 10 Oct 2007 20:12:38 +0000 Subject: [PATCH 012/328] fix 64-bitness printf format string compiler warnings (Jean-Pierre Andre) --- libntfs-3g/lcnalloc.c | 11 +++++++---- libntfs-3g/mft.c | 15 +++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index 75f8b6ab..81d80cd5 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -131,7 +131,8 @@ static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, if (written != size) { if (!written) errno = EIO; - ntfs_log_perror("Bitmap write error (%lld, %lld)", pos, size); + ntfs_log_perror("Bitmap write error (%lld, %lld)", + (long long)pos, (long long)size); return -1; } @@ -312,7 +313,8 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, writeback = 1; if (vol->free_clusters <= 0) ntfs_log_error("Non-positive free clusters " - "(%lld)!\n", vol->free_clusters); + "(%lld)!\n", + (long long)vol->free_clusters); else vol->free_clusters--; @@ -514,8 +516,9 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, rl->length)) { ntfs_log_perror("Cluster deallocation failed " - "(%lld, %lld)", rl->lcn, - rl->length); + "(%lld, %lld)", + (long long)rl->lcn, + (long long)rl->length); goto out; } nr_freed += rl->length ; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 251a667f..b4bdd0c6 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -81,12 +81,12 @@ int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, s64 br; VCN m; - ntfs_log_trace("Entering for inode %lld\n", MREF(mref)); + ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref)); if (!vol || !vol->mft_na || !b || count < 0) { errno = EINVAL; - ntfs_log_perror("%s: b=%p count=%lld mft=%lld", __FUNCTION__, - b, count, MREF(mref)); + ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__, + b, (long long)count, (unsigned long long)MREF(mref)); return -1; } m = MREF(mref); @@ -265,16 +265,19 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, } err = EIO; if (!ntfs_is_file_record(m->magic)) { - ntfs_log_perror("Record %llu has no FILE magic", MREF(mref)); + ntfs_log_perror("Record %llu has no FILE magic", + (unsigned long long)MREF(mref)); goto err_out; } if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { - ntfs_log_perror("Record %llu has wrong SeqNo", MREF(mref)); + ntfs_log_perror("Record %llu has wrong SeqNo", + (unsigned long long)MREF(mref)); goto err_out; } a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size) { - ntfs_log_perror("Record %llu is corrupt", MREF(mref)); + ntfs_log_perror("Record %llu is corrupt", + (unsigned long long)MREF(mref)); goto err_out; } *mrec = m; From 42444cdd94432bd19e1964217fba73d7be1f48f8 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 10 Oct 2007 20:45:19 +0000 Subject: [PATCH 013/328] fix 64-bitness printf format string compiler warnings (Jean-Pierre Andre) --- libntfs-3g/attrib.c | 28 ++++++++++++++++++---------- libntfs-3g/inode.c | 5 +++-- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index d7b574f6..8fd8cd43 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -754,7 +754,8 @@ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { errno = EINVAL; ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", - __FUNCTION__, na, b, pos, count); + __FUNCTION__, na, b, (long long)pos, + (long long)count); return -1; } @@ -868,7 +869,8 @@ res_err_out: if (rl->lcn < (LCN)0) { if (rl->lcn != (LCN)LCN_HOLE) { ntfs_log_perror("%s: Bad run (%lld)", - __FUNCTION__, rl->lcn); + __FUNCTION__, + (long long)rl->lcn); goto rl_err_out; } /* It is a hole, just zero the matching @b range. */ @@ -1271,7 +1273,8 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; ntfs_log_perror("%s: Unexpected LCN (%lld)", - __FUNCTION__, rl->lcn); + __FUNCTION__, + (long long)rl->lcn); goto rl_err_out; } @@ -1530,7 +1533,8 @@ s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, /* Write the prepared data. */ written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); if (written <= 0) { - ntfs_log_perror("%s: written=%lld", __FUNCTION__, written); + ntfs_log_perror("%s: written=%lld", __FUNCTION__, + (long long)written); } /* Quickly deprotect the data again. */ for (i = 0; i < bk_cnt; ++i) @@ -2430,7 +2434,8 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, if (size < 0) { errno = EINVAL; - ntfs_log_perror("%s: size=%lld", __FUNCTION__, size); + ntfs_log_perror("%s: size=%lld", __FUNCTION__, + (long long)size); return -1; } @@ -2440,7 +2445,7 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, */ if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { errno = ERANGE; - ntfs_log_perror("Too large attrlist (%lld)", size); + ntfs_log_perror("Too large attrlist (%lld)", (long long)size); return -1; } @@ -2455,7 +2460,8 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, ((max_size > 0) && (size > max_size))) { errno = ERANGE; ntfs_log_perror("Attr type %d size check failed (min,size,max=" - "%lld,%lld,%lld)", type, min_size, size, max_size); + "%lld,%lld,%lld)", type, (long long)min_size, + (long long)size, (long long)max_size); return -1; } return 0; @@ -3016,7 +3022,8 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { errno = EINVAL; - ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, size); + ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, + (long long)size); return -1; } @@ -4728,8 +4735,9 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) DATA_ZONE); if (!rl) { ntfs_log_perror("Cluster allocation failed " - "(%lld)", first_free_vcn - - (na->allocated_size >> + "(%lld)", + (long long)first_free_vcn - + ((long long)na->allocated_size >> vol->cluster_size_bits)); return -1; } diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index d00789d9..9d30d899 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -409,7 +409,8 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) ni->mrec->sequence_number)) { errno = EIO; ntfs_log_perror("Found stale extent mft " - "reference mft=%lld", ni->mft_no); + "reference mft=%lld", + (long long)ni->mft_no); return NULL; } return ni; @@ -579,7 +580,7 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) if (!err) err = errno; ntfs_log_perror("Failed to open inode %lld with index", - le64_to_cpu(fn->parent_directory)); + (long long)le64_to_cpu(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); From 3502cdc3fbd338dbd17db0dc6389a991d2f1bdec Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 14 Oct 2007 10:12:41 +0000 Subject: [PATCH 014/328] ntfs_fuse_statfs(): fix free inodes available for non-privileged processes --- src/ntfs-3g.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a308009e..9adbc596 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -245,7 +245,7 @@ static long ntfs_get_nr_free_clusters(ntfs_volume *vol) * Returns 0 on success or -errno on error. */ static int ntfs_fuse_statfs(const char *path __attribute__((unused)), - struct statvfs *sfs) + struct statvfs *sfs) { s64 size; int delta_bits; @@ -255,22 +255,23 @@ static int ntfs_fuse_statfs(const char *path __attribute__((unused)), if (!vol) return -ENODEV; - /* Optimal transfer block size. */ + /* File system block size, used for 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 data blocks in file system in units of f_bsize and since - * inodes are also stored in data blocs ($MFT is a file) this is just - * the total clusters. + * 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 data blocks in file system in units of f_bsize. */ + /* Free blocks available for all and for non-privileged processes. */ size = vol->free_clusters; if (size < 0) size = 0; - - /* Free blocks avail to non-superuser, same as above on NTFS. */ sfs->f_bavail = sfs->f_bfree = size; /* Free inodes on the free space */ @@ -280,15 +281,14 @@ static int ntfs_fuse_statfs(const char *path __attribute__((unused)), else size >>= -delta_bits; - /* Number of inodes in file system (at this point in time). */ + /* Number of inodes at this point in time. */ sfs->f_files = (vol->mftbmp_na->allocated_size << 3) + size; - /* Free inodes in fs (based on current total count). */ + /* Free inodes available for all and for non-privileged processes. */ size += vol->free_mft_records; if (size < 0) size = 0; - sfs->f_ffree = size; - sfs->f_favail = 0; + sfs->f_ffree = sfs->f_favail = size; /* Maximum length of filenames. */ sfs->f_namemax = NTFS_MAX_NAME_LEN; From dd8bfdaa67cab2ba023d5d6b38528c9d013e0671 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 15 Oct 2007 19:21:55 +0000 Subject: [PATCH 015/328] fix bogus warning if FUSE is installed in /lib64 (Jean-Pierre Andre, Szaka) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c3d93e1a..7fa53b47 100644 --- a/configure.ac +++ b/configure.ac @@ -219,7 +219,7 @@ AC_CONFIG_FILES([ ]) AC_OUTPUT -if test "x$FUSE_LIB_PATH" != "x-L/lib"; then +if test "x$FUSE_LIB_PATH" != "x-L/lib" -a "x$FUSE_LIB_PATH" != "x-L/lib64"; then cat < Date: Mon, 22 Oct 2007 18:50:01 +0000 Subject: [PATCH 016/328] ntfs_security_hash(): big-endian fix (Erik Larsson) --- libntfs-3g/security.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index 489edc65..4b54b699 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -361,8 +361,10 @@ le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) const le32 *end = pos + (len >> 2); u32 hash = 0; - while (pos < end) - hash = le32_to_cpup(pos++) + ntfs_rol32(hash, 3); + while (pos < end) { + hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3); + pos++; + } return cpu_to_le32(hash); } From c48091cf9dbfc2d818eced4cd994e57c2bf852d1 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 18:57:50 +0000 Subject: [PATCH 017/328] ntfs_ir_truncate(): log only the real errors --- libntfs-3g/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 227ce53a..cb4918b3 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1218,7 +1218,7 @@ static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) icx->ir->index.allocated_size = cpu_to_le32(data_size); - } else + } else if (ret == STATUS_ERROR) ntfs_log_perror("Failed to truncate INDEX_ROOT"); ntfs_attr_close(na); From 1627dbeedacf86839bfa2248aa15f35aff9cf04f Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 19:26:26 +0000 Subject: [PATCH 018/328] ntfs_fuse_read(): don't flood log with EINVAL when using broken Thunderbirds (Julian Sikorski, Szabolcs Szakacsits) --- src/ntfs-3g.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 9adbc596..f464db9a 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -648,7 +648,8 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, ntfs_attr *na = NULL; char *path = NULL; ntfschar *stream_name; - int stream_name_len, res, total = 0; + int stream_name_len, res; + s64 total = 0; stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) @@ -664,20 +665,26 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, res = -errno; goto exit; } - if (offset + size > na->data_size) - size = na->data_size - offset; - while (size) { - res = ntfs_attr_pread(na, offset, size, buf); - if (res < (s64)size) - ntfs_log_perror("ntfs_attr_pread partial write (%lld: " - "%lld <> %d)", (s64)offset, (s64)size, res); - if (res <= 0) { - res = -errno; + if (offset + size > na->data_size) { + if (na->data_size < offset) { + res = -EINVAL; goto exit; } - size -= res; - offset += res; - total += res; + size = na->data_size - offset; + } + while (size > 0) { + s64 ret = ntfs_attr_pread(na, offset, size, buf); + if (ret != (s64)size) + ntfs_log_perror("ntfs_attr_pread error reading '%s' at " + "offset %lld: %lld <> %lld", org_path, + (long long)offset, (long long)size, (long long)ret); + if (ret <= 0 || ret > (s64)size) { + res = (ret <= 0) ? -errno : -EIO; + goto exit; + } + size -= ret; + offset += ret; + total += ret; } res = total; exit: From a6a0a088fa8bb657bb2dc5b4d5bd3338ad54a61f Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 19:31:36 +0000 Subject: [PATCH 019/328] use the FUSE debug only if the debug mount option is used --- src/ntfs-3g.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index f464db9a..be044412 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2257,11 +2257,11 @@ int main(int argc, char *argv[]) if (fuse_opt_add_arg(&margs, "") == -1 || fuse_opt_add_arg(&margs, "-o") == -1) fh = NULL; - if (!ctx->debug && !ctx->no_detach) { - if (fuse_opt_add_arg(&margs, "use_ino,kernel_cache") == -1) + if (ctx->debug) { + if (fuse_opt_add_arg(&margs, "use_ino,kernel_cache,debug") == -1) fh = NULL; } else { - if (fuse_opt_add_arg(&margs, "use_ino,kernel_cache,debug") == -1) + if (fuse_opt_add_arg(&margs, "use_ino,kernel_cache") == -1) fh = NULL; } if (fh) From e338f95879dd3ad1f89e7618b0ddf4f28e6245c7 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 20:03:10 +0000 Subject: [PATCH 020/328] ntfs_file_record_read(): logging cleanup --- libntfs-3g/mft.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index b4bdd0c6..d4d280f8 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -265,19 +265,20 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, } err = EIO; if (!ntfs_is_file_record(m->magic)) { - ntfs_log_perror("Record %llu has no FILE magic", - (unsigned long long)MREF(mref)); + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), *(le32 *)m); goto err_out; } if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { - ntfs_log_perror("Record %llu has wrong SeqNo", - (unsigned long long)MREF(mref)); + ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", + (unsigned long long)MREF(mref), MSEQNO(mref), + le16_to_cpu(m->sequence_number)); goto err_out; } a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size) { - ntfs_log_perror("Record %llu is corrupt", - (unsigned long long)MREF(mref)); + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); goto err_out; } *mrec = m; From 0c06669db6c4e363155dbd283cdff6989c14d76c Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 20:09:29 +0000 Subject: [PATCH 021/328] struct ntfs_logging: workaround for broken gcc format attribute handling --- libntfs-3g/logging.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index 228e90e0..6d85bcf2 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -58,6 +58,13 @@ static const char *col_red = "\e[01;31m"; static const char *col_redinv = "\e[01;07;31m"; static const char *col_end = "\e[0m"; +/* gcc 3.3.3 crashes with internal compiler error. 4.x seems to be ok. */ +#if __GNUC__ <= 3 +# define BROKEN_GCC_FORMAT_ATTRIBUTE +#else +# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0))) +#endif + /** * struct ntfs_logging - Control info for the logging system * @levels: Bitfield of logging levels @@ -67,7 +74,7 @@ static const char *col_end = "\e[0m"; struct ntfs_logging { u32 levels; u32 flags; - ntfs_log_handler *handler; + ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; }; /** From 63f23a8e7dee7bbad095e29d6ae97a13b263fe30 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 20:22:00 +0000 Subject: [PATCH 022/328] create the /dev/misc/fuse character device too if uclibc used --- src/ntfs-3g.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index be044412..b6da8e99 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -135,10 +135,6 @@ static const char *locale_msg = " be correct or visible. Please see the potential solution at\n" " http://ntfs-3g.org/support.html#locale\n"; -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) and make sure /dev/fuse exist.\n"; - static const char *usage_msg = "\n" "%s %s - Third Generation NTFS Driver\n" @@ -2030,6 +2026,12 @@ static int parse_options(int argc, char *argv[]) #ifdef linux +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" @@ -2041,19 +2043,39 @@ static const char *fuse26_kmod_msg = " http://ntfs-3g.org/support.html#fuse26\n" "\n"; -static void create_dev_fuse(void) +static void mknod_dev_fuse(const char *dev) { struct stat st; - if (stat("/dev/fuse", &st) && (errno == ENOENT)) { - if (mknod("/dev/fuse", S_IFCHR | 0666, makedev(10, 229))) { - ntfs_log_perror("Failed to create /dev/fuse"); + 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(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]; From fc8ef6669203b674a973c4f6ca638b593787a826 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 21:02:59 +0000 Subject: [PATCH 023/328] add preliminary uClinux support (huwenke, Szabolcs Szakacsits) --- src/ntfs-3g.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index b6da8e99..9fd25551 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2024,7 +2024,7 @@ static int parse_options(int argc, char *argv[]) return 0; } -#ifdef linux +#ifdef linux || __uClinux__ static const char *dev_fuse_msg = "HINT: You should be root, or make ntfs-3g setuid root, or load the FUSE\n" @@ -2231,7 +2231,7 @@ int main(int argc, char *argv[]) goto err_out; } -#ifdef linux +#ifdef linux || __uClinux__ fstype = get_fuse_fstype(); if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN) fstype = load_fuse_module(); @@ -2302,7 +2302,7 @@ int main(int argc, char *argv[]) goto err_out; } -#ifdef linux +#ifdef linux || __uClinux__ if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) ntfs_log_info(fuse26_kmod_msg); #endif From f24a38f0d66b84d4fd62fd2383bbc66b65bb1255 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 22:22:24 +0000 Subject: [PATCH 024/328] non-inline ntfs_fuse_is_named_data_stream & NAttr* functions: 500+ bytes saved --- include/ntfs-3g/attrib.h | 31 ++++++++----------------------- libntfs-3g/attrib.c | 31 +++++++++++++++++++++++++++++++ src/ntfs-3g.c | 2 +- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index 9e4dd31b..d87717ca 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -204,30 +204,15 @@ typedef enum { #define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) #define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) -#define GenNAttrIno(func_name,flag) \ -static inline int NAttr##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - return (na->ni->flags & FILE_ATTR_##flag); \ - return 0; \ -} \ -static inline void NAttrSet##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - na->ni->flags |= FILE_ATTR_##flag; \ - else \ - ntfs_log_trace("BUG! Should be called only for "\ - "unnamed data attribute.\n"); \ -} \ -static inline void NAttrClear##func_name(ntfs_attr *na) \ -{ \ - if (na->type == AT_DATA && na->name == AT_UNNAMED) \ - na->ni->flags &= ~FILE_ATTR_##flag; \ -} +#define GenNAttrIno(func_name, flag) \ +extern int NAttr##func_name(ntfs_attr *na); \ +extern void NAttrSet##func_name(ntfs_attr *na); \ +extern void NAttrClear##func_name(ntfs_attr *na); -GenNAttrIno(Compressed, COMPRESSED) -GenNAttrIno(Encrypted, ENCRYPTED) -GenNAttrIno(Sparse, SPARSE_FILE) +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) +#undef GenNAttrIno /** * union attr_val - Union of all known attribute values diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 8fd8cd43..6d2d7c83 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -60,6 +60,37 @@ ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; +static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + return (na->ni->flags & flag); + return 0; +} + +static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags |= flag; + else + ntfs_log_error("Denied setting flag %d for not unnamed data " + "attribute\n", flag); +} + +static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags &= ~flag; +} + +#define GenNAttrIno(func_name, flag) \ +int NAttr##func_name(ntfs_attr *na) { return NAttrFlag (na, flag); } \ +void NAttrSet##func_name(ntfs_attr *na) { NAttrSetFlag (na, flag); } \ +void NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); } + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) + /** * ntfs_get_attribute_value_length - Find the length of an attribute * @a: diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 9fd25551..38403a45 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -158,7 +158,7 @@ static const char *usage_msg = * * Returns 1 if path is to named data stream or 0 otherwise. */ -static __inline__ int ntfs_fuse_is_named_data_stream(const char *path) +static int ntfs_fuse_is_named_data_stream(const char *path) { if (strchr(path, ':') && ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) return 1; From 60a8be1bc55fed240e185d12b5d7fe603018a8b9 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 25 Oct 2007 22:26:02 +0000 Subject: [PATCH 025/328] check properly for the linux and __uClinux__ macros --- src/ntfs-3g.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 38403a45..9861378b 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2024,7 +2024,7 @@ static int parse_options(int argc, char *argv[]) return 0; } -#ifdef linux || __uClinux__ +#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" @@ -2231,7 +2231,7 @@ int main(int argc, char *argv[]) goto err_out; } -#ifdef linux || __uClinux__ +#if defined(linux) || defined(__uClinux__) fstype = get_fuse_fstype(); if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN) fstype = load_fuse_module(); @@ -2302,7 +2302,7 @@ int main(int argc, char *argv[]) goto err_out; } -#ifdef linux || __uClinux__ +#if defined(linux) || defined(__uClinux__) if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) ntfs_log_info(fuse26_kmod_msg); #endif From 4ac2b247ccdc00b7fb598f031e05c775208dd650 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 26 Oct 2007 00:46:59 +0000 Subject: [PATCH 026/328] ntfs_inode_attach_all_extents(): fix big-endianness (rsync may failed) --- libntfs-3g/inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 9d30d899..6a9889a8 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -487,8 +487,7 @@ int ntfs_inode_attach_all_extents(ntfs_inode *ni) while ((u8*)ale < ni->attr_list + ni->attr_list_size) { if (ni->mft_no != MREF_LE(ale->mft_reference) && prev_attached != MREF_LE(ale->mft_reference)) { - if (!ntfs_extent_inode_open(ni, - MREF_LE(ale->mft_reference))) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { ntfs_log_trace("Couldn't attach extent inode.\n"); return -1; } From 1200c1c4bc60b86e6087518080fe543b51ff4f1a Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 27 Oct 2007 19:27:33 +0000 Subject: [PATCH 027/328] factor out mount_fuse(); fix fuse mount error paths --- src/ntfs-3g.c | 105 +++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 9861378b..2bc81f42 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -117,6 +117,7 @@ typedef struct { BOOL debug; BOOL noatime; BOOL no_detach; + struct fuse_chan *fc; } ntfs_fuse_context_t; static struct options { @@ -1590,29 +1591,28 @@ exit: #endif /* HAVE_SETXATTR */ -static void ntfs_fuse_destroy(void) +static void ntfs_close(void) { if (!ctx) return; - if (ctx->vol) { - ntfs_log_info("Unmounting %s (%s)\n", opts.device, - ctx->vol->vol_name); - if (ntfs_umount(ctx->vol, FALSE)) - ntfs_log_perror("Failed to cleanly unmount volume %s", - opts.device); - } - free(ctx); - ctx = NULL; - free(opts.device); + if (!ctx->vol) + return; + + ntfs_log_info("Unmounting %s (%s)\n", opts.device, ctx->vol->vol_name); + + if (ntfs_umount(ctx->vol, FALSE)) + ntfs_log_perror("Failed to close volume %s", opts.device); + + ctx->vol = NULL; } static void ntfs_fuse_destroy2(void *unused __attribute__((unused))) { - ntfs_fuse_destroy(); + ntfs_close(); } -static struct fuse_operations ntfs_fuse_oper = { +static struct fuse_operations ntfs_3g_ops = { .getattr = ntfs_fuse_getattr, .readlink = ntfs_fuse_readlink, .readdir = ntfs_fuse_readdir, @@ -2194,12 +2194,42 @@ static void set_user_mount_option(char *parsed_options, uid_t uid) strcat(parsed_options, option); } +static struct fuse *mount_fuse(char *parsed_options) +{ + struct fuse *fh = NULL; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + + /* Libfuse can't always find fusermount, so let's help it. */ + if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) + ntfs_log_perror("WARNING: Failed to set $PATH\n"); + + ctx->fc = try_fuse_mount(parsed_options); + if (!ctx->fc) + return NULL; + + if (fuse_opt_add_arg(&args, "") == -1) + goto err; + if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache") == -1) + goto err; + if (ctx->debug) + if (fuse_opt_add_arg(&args, "-odebug") == -1) + goto err; + + fh = fuse_new(ctx->fc, &args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL); + if (!fh) + goto err; +out: + fuse_opt_free_args(&args); + return fh; +err: + fuse_unmount(opts.mnt_point, ctx->fc); + goto out; +} + int main(int argc, char *argv[]) { char *parsed_options = NULL; - struct fuse_args margs = FUSE_ARGS_INIT(0, NULL); struct fuse *fh; - struct fuse_chan *fc; fuse_fstype fstype = FSTYPE_UNKNOWN; struct stat sbuf; int use_blkdev = 0; @@ -2267,39 +2297,13 @@ int main(int argc, char *argv[]) set_user_mount_option(parsed_options, uid); } - /* Libfuse can't always find fusermount, so let's help it. */ - if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) - ntfs_log_perror("WARNING: Failed to set $PATH\n"); - - fc = try_fuse_mount(parsed_options); - if (!fc) + fh = mount_fuse(parsed_options); + if (!fh) goto err_out; - - fh = (struct fuse *)1; /* Cast anything except NULL to handle errors. */ - if (fuse_opt_add_arg(&margs, "") == -1 || - fuse_opt_add_arg(&margs, "-o") == -1) - fh = NULL; - if (ctx->debug) { - if (fuse_opt_add_arg(&margs, "use_ino,kernel_cache,debug") == -1) - fh = NULL; - } else { - if (fuse_opt_add_arg(&margs, "use_ino,kernel_cache") == -1) - fh = NULL; - } - if (fh) - fh = fuse_new(fc, &margs , &ntfs_fuse_oper, - sizeof(ntfs_fuse_oper), NULL); - fuse_opt_free_args(&margs); - if (!fh) { - ntfs_log_error("fuse_new failed.\n"); - fuse_unmount(opts.mnt_point, fc); - goto err_out; - } - + if (setuid(uid)) { ntfs_log_perror("Failed to set user ID to %d", uid); - fuse_unmount(opts.mnt_point, fc); - goto err_out; + goto err_umount; } #if defined(linux) || defined(__uClinux__) @@ -2328,12 +2332,15 @@ int main(int argc, char *argv[]) fuse_loop(fh); - fuse_unmount(opts.mnt_point, fc); - fuse_destroy(fh); err = 0; +err_umount: + fuse_unmount(opts.mnt_point, ctx->fc); + fuse_destroy(fh); err_out: - free(opts.options); + ntfs_close(); + free(ctx); free(parsed_options); - ntfs_fuse_destroy(); + free(opts.options); + free(opts.device); return err; } From c06caa535361406f2b0e9923b6464bd76523fbc9 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 27 Oct 2007 20:02:40 +0000 Subject: [PATCH 028/328] don't log "Unmounting ..." if volume isn't FUSE mounted yet (B. Kaindl, Szaka) --- src/ntfs-3g.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 2bc81f42..8445cf18 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -117,6 +117,7 @@ typedef struct { BOOL debug; BOOL noatime; BOOL no_detach; + BOOL mounted; struct fuse_chan *fc; } ntfs_fuse_context_t; @@ -1599,7 +1600,9 @@ static void ntfs_close(void) if (!ctx->vol) return; - ntfs_log_info("Unmounting %s (%s)\n", opts.device, ctx->vol->vol_name); + if (ctx->mounted) + ntfs_log_info("Unmounting %s (%s)\n", opts.device, + ctx->vol->vol_name); if (ntfs_umount(ctx->vol, FALSE)) ntfs_log_perror("Failed to close volume %s", opts.device); @@ -1644,15 +1647,13 @@ static struct fuse_operations ntfs_3g_ops = { static int ntfs_fuse_init(void) { - ctx = ntfs_malloc(sizeof(ntfs_fuse_context_t)); + ctx = ntfs_calloc(sizeof(ntfs_fuse_context_t)); if (!ctx) return -1; *ctx = (ntfs_fuse_context_t) { .uid = getuid(), .gid = getgid(), - .fmask = 0, - .dmask = 0, .streams = NF_STREAMS_INTERFACE_NONE, }; return 0; @@ -2218,6 +2219,8 @@ static struct fuse *mount_fuse(char *parsed_options) fh = fuse_new(ctx->fc, &args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL); if (!fh) goto err; + + ctx->mounted = TRUE; out: fuse_opt_free_args(&args); return fh; From 6be9b754b6b551882fe6742f96c13e4d2608c859 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 27 Oct 2007 20:10:34 +0000 Subject: [PATCH 029/328] factor out ntfs_attr_get_free_bits() --- include/ntfs-3g/attrib.h | 1 + libntfs-3g/attrib.c | 27 +++++++++++++++++++ src/ntfs-3g.c | 58 ++++------------------------------------ 3 files changed, 33 insertions(+), 53 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index d87717ca..6fc5bc8b 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -340,6 +340,7 @@ extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); +extern long ntfs_attr_get_free_bits(ntfs_attr *na); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 6d2d7c83..eafcdb55 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -5042,3 +5042,30 @@ int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, return ret; } +long ntfs_attr_get_free_bits(ntfs_attr *na) +{ + u8 *buf; + long nr_free = 0; + s64 br, total = 0; + + buf = ntfs_malloc(na->ni->vol->cluster_size); + if (!buf) + return -errno; + while (1) { + int i, j; + + br = ntfs_attr_pread(na, total, na->ni->vol->cluster_size, buf); + if (br <= 0) + break; + total += br; + for (i = 0; i < br; i++) + for (j = 0; j < 8; j++) + if (!((buf[i] >> j) & 1)) + nr_free++; + } + free(buf); + if (!total || br < 0) + return -errno; + return nr_free; +} + diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 8445cf18..aa75d8dc 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -169,59 +169,11 @@ static int ntfs_fuse_is_named_data_stream(const char *path) static long ntfs_get_nr_free_mft_records(ntfs_volume *vol) { - u8 *buf; - long nr_free = 0; - s64 br, total = 0; + ntfs_attr *na = vol->mftbmp_na; + long nr_free = ntfs_attr_get_free_bits(na); - buf = ntfs_malloc(vol->cluster_size); - if (!buf) - return -errno; - while (1) { - int i, j; - - br = ntfs_attr_pread(vol->mftbmp_na, total, - vol->cluster_size, buf); - if (br <= 0) - break; - total += br; - for (i = 0; i < br; i++) - for (j = 0; j < 8; j++) - if (!((buf[i] >> j) & 1)) - nr_free++; - } - free(buf); - if (!total || br < 0) - return -errno; - - nr_free += (vol->mftbmp_na->allocated_size - vol->mftbmp_na->data_size) << 3; - return nr_free; -} - -static long ntfs_get_nr_free_clusters(ntfs_volume *vol) -{ - u8 *buf; - long nr_free = 0; - s64 br, total = 0; - - buf = ntfs_malloc(vol->cluster_size); - if (!buf) - return -errno; - while (1) { - int i, j; - - br = ntfs_attr_pread(vol->lcnbmp_na, total, - vol->cluster_size, buf); - if (br <= 0) - break; - total += br; - for (i = 0; i < br; i++) - for (j = 0; j < 8; j++) - if (!((buf[i] >> j) & 1)) - nr_free++; - } - free(buf); - if (!total || br < 0) - return -errno; + if (nr_free >= 0) + nr_free += (na->allocated_size - na->data_size) << 3; return nr_free; } @@ -2283,7 +2235,7 @@ int main(int argc, char *argv[]) if (!ntfs_open(opts.device, opts.mnt_point, use_blkdev)) goto err_out; - ctx->vol->free_clusters = ntfs_get_nr_free_clusters(ctx->vol); + 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; From b032c0e68bbecc0a72b4319cac5cfe58b3765b32 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 27 Oct 2007 20:38:21 +0000 Subject: [PATCH 030/328] fix free space calculation for >1 TB volumes --- include/ntfs-3g/attrib.h | 2 +- libntfs-3g/attrib.c | 8 ++++---- src/ntfs-3g.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index 6fc5bc8b..3aeec198 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -340,7 +340,7 @@ extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len); -extern long ntfs_attr_get_free_bits(ntfs_attr *na); +extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index eafcdb55..7b78cfa4 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -5042,15 +5042,15 @@ int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, return ret; } -long ntfs_attr_get_free_bits(ntfs_attr *na) +s64 ntfs_attr_get_free_bits(ntfs_attr *na) { u8 *buf; - long nr_free = 0; + s64 nr_free = 0; s64 br, total = 0; buf = ntfs_malloc(na->ni->vol->cluster_size); if (!buf) - return -errno; + return -1; while (1) { int i, j; @@ -5065,7 +5065,7 @@ long ntfs_attr_get_free_bits(ntfs_attr *na) } free(buf); if (!total || br < 0) - return -errno; + return -1; return nr_free; } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index aa75d8dc..a307f2f2 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -167,10 +167,10 @@ static int ntfs_fuse_is_named_data_stream(const char *path) return 0; } -static long ntfs_get_nr_free_mft_records(ntfs_volume *vol) +static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol) { ntfs_attr *na = vol->mftbmp_na; - long nr_free = ntfs_attr_get_free_bits(na); + s64 nr_free = ntfs_attr_get_free_bits(na); if (nr_free >= 0) nr_free += (na->allocated_size - na->data_size) << 3; From af6370da9e0e2c366ed3a987d2518dc88851bfc0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 28 Oct 2007 00:07:33 +0000 Subject: [PATCH 031/328] ntfs_open() cleanup, refactoring --- src/ntfs-3g.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a307f2f2..04733394 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -117,6 +117,7 @@ typedef struct { BOOL debug; BOOL noatime; BOOL no_detach; + BOOL blkdev; BOOL mounted; struct fuse_chan *fc; } ntfs_fuse_context_t; @@ -1611,11 +1612,11 @@ static int ntfs_fuse_init(void) return 0; } -static ntfs_volume *ntfs_open(const char *device, char *mntpoint, int blkdev) +static int ntfs_open(const char *device, char *mntpoint) { unsigned long flags = 0; - if (!blkdev) + if (!ctx->blkdev) flags |= MS_EXCLUSIVE; if (ctx->ro) flags |= MS_RDONLY; @@ -1625,7 +1626,22 @@ static ntfs_volume *ntfs_open(const char *device, char *mntpoint, int blkdev) flags |= MS_FORCE; ctx->vol = utils_mount_volume(device, mntpoint, flags); - return ctx->vol; + if (!ctx->vol) + return -1; + + 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"); + return -1; + } + + 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"); + return -1; + } + + return 0; } static void signal_handler(int arg __attribute__((unused))) @@ -2187,7 +2203,6 @@ int main(int argc, char *argv[]) struct fuse *fh; fuse_fstype fstype = FSTYPE_UNKNOWN; struct stat sbuf; - int use_blkdev = 0; uid_t uid, euid; int err = 10; @@ -2230,26 +2245,15 @@ int main(int argc, char *argv[]) } /* Always use fuseblk for block devices unless it's surely missing. */ if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) - use_blkdev = 1; + ctx->blkdev = TRUE; - if (!ntfs_open(opts.device, opts.mnt_point, use_blkdev)) + if (ntfs_open(opts.device, opts.mnt_point)) 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 (use_blkdev) { - set_fuseblk_options(parsed_options); - set_user_mount_option(parsed_options, uid); + if (ctx->blkdev) { + /* Must do after ntfs_open() to set the right blksize. */ + set_fuseblk_options(parsed_options); + set_user_mount_option(parsed_options, uid); } fh = mount_fuse(parsed_options); From 03b4e15ab89986775f853b42a4c0863093b68e9d Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 28 Oct 2007 09:33:29 +0000 Subject: [PATCH 032/328] release 1.1028 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 7fa53b47..36e6c953 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.1004],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=13:0:0 +AC_INIT([ntfs-3g],[1.1028],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=14:0:0 AC_CANONICAL_HOST([]) AC_CANONICAL_TARGET([]) From 061c6dd9d853c29f7c2851135e61e24a41900155 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 28 Oct 2007 11:13:00 +0000 Subject: [PATCH 033/328] NAttrSetFlag(): don't log bogus errors --- libntfs-3g/attrib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 7b78cfa4..81fba8d7 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -72,7 +72,7 @@ static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) if (na->type == AT_DATA && na->name == AT_UNNAMED) na->ni->flags |= flag; else - ntfs_log_error("Denied setting flag %d for not unnamed data " + ntfs_log_trace("Denied setting flag %d for not unnamed data " "attribute\n", flag); } From 3e602f753ef1470efe5f15aa73bf43376879cb4f Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 28 Oct 2007 11:14:32 +0000 Subject: [PATCH 034/328] release 1.1030 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 36e6c953..0ea01149 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.1028],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[1.1030],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION=14:0:0 AC_CANONICAL_HOST([]) From 028f73740db53d2485304388bea6b2b0479c6362 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 31 Oct 2007 21:30:47 +0000 Subject: [PATCH 035/328] fix utimes() st_ctime if struct utimbuf buf wasn't NULL (Carmelo Viavattene, Szaka) --- src/ntfs-3g.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 04733394..3c4ac837 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1188,6 +1188,7 @@ static int ntfs_fuse_rmdir(const char *path) static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) { ntfs_inode *ni; + time_t now; int res = 0; if (ntfs_fuse_is_named_data_stream(path)) @@ -1198,17 +1199,15 @@ static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) NInoSetNoParentMtimeUpdate(ni); + now = time(NULL); + ni->last_mft_change_time = now; + if (buf) { ni->last_access_time = buf->actime; ni->last_data_change_time = buf->modtime; - ni->last_mft_change_time = buf->modtime; } else { - time_t now; - - now = time(NULL); ni->last_access_time = now; ni->last_data_change_time = now; - ni->last_mft_change_time = now; } NInoFileNameSetDirty(ni); NInoSetDirty(ni); From 729ee4333062b093d4bd05869dd1567e7173b76e Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 3 Nov 2007 23:09:11 +0000 Subject: [PATCH 036/328] fix read over file end (Tullio Andreatta, Julian Sikorski, Szabolcs Szakacsits) --- src/ntfs-3g.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 3c4ac837..1a9d268b 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -617,10 +617,8 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, goto exit; } if (offset + size > na->data_size) { - if (na->data_size < offset) { - res = -EINVAL; - goto exit; - } + if (na->data_size < offset) + goto ok; size = na->data_size - offset; } while (size > 0) { @@ -630,13 +628,14 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, "offset %lld: %lld <> %lld", org_path, (long long)offset, (long long)size, (long long)ret); if (ret <= 0 || ret > (s64)size) { - res = (ret <= 0) ? -errno : -EIO; + res = (ret < 0) ? -errno : -EIO; goto exit; } size -= ret; offset += ret; total += ret; } +ok: res = total; exit: if (na) From ac25bd2295b3430e54c93ed2b86431ef6bbb6c69 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 3 Nov 2007 23:19:18 +0000 Subject: [PATCH 037/328] workaround gcc 4.[01] crash (bsm, Loris Boillet, Kano, Szaka) --- libntfs-3g/logging.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index 6d85bcf2..c34bc2b0 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -58,8 +58,8 @@ static const char *col_red = "\e[01;31m"; static const char *col_redinv = "\e[01;07;31m"; static const char *col_end = "\e[0m"; -/* gcc 3.3.3 crashes with internal compiler error. 4.x seems to be ok. */ -#if __GNUC__ <= 3 +/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */ +#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1) # define BROKEN_GCC_FORMAT_ATTRIBUTE #else # define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0))) From 5d268c9a1753aafd950c8831e29293d7613c39a1 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 4 Nov 2007 11:27:29 +0000 Subject: [PATCH 038/328] ntfs_fuse_getxattr_windows(): separate stream names by '\0' (Daniel Borca) --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 1a9d268b..a442f682 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1372,7 +1372,7 @@ static int ntfs_fuse_getxattr_windows(const char *path, const char *name, if ((size_t)ret <= size) { /* Don't add space to the beginning of line. */ if (to != value) { - *to = ' '; + *to = '\0'; to++; } strncpy(to, tmp_name, tmp_name_len); From 698ab031c21d7da37c7d500a88cff9a528245651 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 4 Nov 2007 11:38:38 +0000 Subject: [PATCH 039/328] release 1.1104 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 0ea01149..bc334d6d 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.1030],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=14:0:0 +AC_INIT([ntfs-3g],[1.1104],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=14:1:0 AC_CANONICAL_HOST([]) AC_CANONICAL_TARGET([]) From d4b91d350c3039df65fdab8f8b3d2324dfe770cc Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 15 Nov 2007 07:33:40 +0000 Subject: [PATCH 040/328] posix file time updates (Yura Pakhuchiy, Szabolcs Szakacsits) --- include/ntfs-3g/inode.h | 24 ++++++------- libntfs-3g/attrib.c | 14 ++------ libntfs-3g/dir.c | 4 ++- libntfs-3g/inode.c | 51 ++++++++++++--------------- src/ntfs-3g.c | 76 +++++++++++++++++++++-------------------- 5 files changed, 76 insertions(+), 93 deletions(-) diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index 94c79f8e..90a3e654 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -48,8 +48,6 @@ typedef enum { mft record and then to disk. */ NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated in the index. */ - NI_NoMtimeUpdate, /* 1: Don't update modifiction time. */ - NI_NoParentMtimeUpdate, /* 1: Don't update parent dir's mtime. */ } ntfs_inode_state_bits; #define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) @@ -98,16 +96,6 @@ typedef enum { #define NInoFileNameTestAndClearDirty(ni) \ test_and_clear_nino_flag(ni, FileNameDirty) -#define NInoNoMtimeUpdate(ni) test_nino_flag(ni, NoMtimeUpdate) -#define NInoSetNoMtimeUpdate(ni) set_nino_flag(ni, NoMtimeUpdate) -#define NInoClearNoMtimeUpdate(ni) clear_nino_flag(ni, NoMtimeUpdate) -#define NInoMtimeUpdate(ni) (!NInoNoMtimeUpdate(ni)) - -#define NInoNoParentMtimeUpdate(ni) test_nino_flag(ni, NoMtimeUpdate) -#define NInoSetNoParentMtimeUpdate(ni) set_nino_flag(ni, NoMtimeUpdate) -#define NInoClearNoParentMtimeUpdate(ni) clear_nino_flag(ni, NoMtimeUpdate) -#define NInoParentMtimeUpdate(ni) (!NInoNoParentMtimeUpdate(ni)) - /** * struct _ntfs_inode - The NTFS in-memory inode structure. * @@ -161,6 +149,15 @@ struct _ntfs_inode { time_t last_access_time; }; +typedef enum { + NTFS_UPDATE_ATIME = 1 << 0, + NTFS_UPDATE_MTIME = 1 << 1, + NTFS_UPDATE_CTIME = 1 << 2, +} ntfs_time_update_flags; + +#define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME) +#define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME) + extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni); extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); @@ -176,8 +173,7 @@ extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); extern void ntfs_inode_mark_dirty(ntfs_inode *ni); -extern void ntfs_inode_update_atime(ntfs_inode *ni); -extern void ntfs_inode_update_time(ntfs_inode *ni); +extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask); extern int ntfs_inode_sync(ntfs_inode *ni); diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 81fba8d7..19b1b69f 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -809,10 +809,7 @@ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) return -1; } vol = na->ni->vol; - /* Update access time if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_atime(na->ni); + if (!count) return 0; /* Truncate reads beyond end of attribute. */ @@ -1162,10 +1159,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) errno = EOPNOTSUPP; goto errno_set; } - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); + if (!count) goto out; /* If the write reaches beyond the end, extend the attribute. */ @@ -4930,10 +4924,6 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) ret = ntfs_non_resident_attr_shrink(na, newsize); } else ret = ntfs_resident_attr_resize(na, newsize); - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); ntfs_log_trace("Return status %d\n", ret); return ret; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 28d81efd..65ac831f 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1485,8 +1485,10 @@ search: * case there are no reference to this inode left, so we should free all * non-resident attributes and mark all MFT record as not in use. */ - if (ni->mrec->link_count) + if (ni->mrec->link_count) { + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); goto out; + } ntfs_attr_reinit_search_ctx(actx); while (!ntfs_attrs_walk(actx)) { if (actx->attr->non_resident) { diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 6a9889a8..c88a626e 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -686,7 +686,6 @@ int ntfs_inode_sync(ntfs_inode *ni) /* Update FILE_NAME's in the index. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && NInoFileNameTestAndClearDirty(ni) && - NInoParentMtimeUpdate(ni) && ntfs_inode_sync_file_name(ni)) { if (!err || errno == EIO) { err = errno; @@ -1096,42 +1095,36 @@ put_err_out: } /** - * ntfs_inode_update_atime - update access time for ntfs inode - * @ni: ntfs inode for which update access time + * ntfs_inode_update_times - update selected time fields for ntfs inode + * @ni: ntfs inode for which update time fields + * @mask: select which time fields should be updated * - * This function usually get called when user read not metadata from inode. - * Do not update time for system files. + * This function updates time fields to current time. Fields to update are + * selected using @mask (see enum @ntfs_time_update_flags for posssible values). */ -void ntfs_inode_update_atime(ntfs_inode *ni) +void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - if (!NVolReadOnly(ni->vol) && !NVolNoATime(ni->vol) && (ni->mft_no >= - FILE_first_user || ni->mft_no == FILE_root)) { - ni->last_access_time = time(NULL); - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); + time_t now; + + if (!ni) { + ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); + return; } -} -/** - * ntfs_inode_update_time - update all times for ntfs inode - * @ni: ntfs inode for which update times - * - * This function updates last access, mft and data change times. Usually - * get called when user write not metadata to inode. Do not update time for - * system files. - */ -void ntfs_inode_update_time(ntfs_inode *ni) -{ - if (!NVolReadOnly(ni->vol) && NInoMtimeUpdate(ni) && - (ni->mft_no >= FILE_first_user || ni->mft_no == FILE_root)) { - time_t now; + if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || + NVolReadOnly(ni->vol) || !mask) + return; - now = time(NULL); + now = time(NULL); + if (mask & NTFS_UPDATE_ATIME) + ni->last_access_time = now; + if (mask & NTFS_UPDATE_MTIME) ni->last_data_change_time = now; + if (mask & NTFS_UPDATE_CTIME) ni->last_mft_change_time = now; - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); - } + + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); } /** diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a442f682..9be0b210 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -168,6 +168,13 @@ static int ntfs_fuse_is_named_data_stream(const char *path) return 0; } +static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) +{ + if (ctx->noatime) + mask &= ~NTFS_UPDATE_ATIME; + ntfs_inode_update_times(ni, mask); +} + static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol) { ntfs_attr *na = vol->mftbmp_na; @@ -552,6 +559,7 @@ static int ntfs_fuse_readdir(const char *path, void *buf, if (ntfs_readdir(ni, &pos, &fill_ctx, (ntfs_filldir_t)ntfs_fuse_filler)) err = -errno; + ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); if (ntfs_inode_close(ni)) set_fuse_error(&err); return err; @@ -594,7 +602,6 @@ static int ntfs_fuse_open(const char *org_path, static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi __attribute__((unused))) { - ntfs_volume *vol; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; char *path = NULL; @@ -602,11 +609,13 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, int stream_name_len, res; s64 total = 0; + if (!size) + return 0; + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) return stream_name_len; - vol = ctx->vol; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) { res = -errno; goto exit; @@ -636,6 +645,7 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, total += ret; } ok: + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME); res = total; exit: if (na) @@ -688,6 +698,8 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, total += res; } res = total; + if (res > 0) + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); exit: if (na) ntfs_attr_close(na); @@ -725,6 +737,7 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) if (ntfs_attr_truncate(na, size)) goto exit; + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); ntfs_attr_close(na); errno = 0; exit: @@ -808,6 +821,7 @@ static int ntfs_fuse_create(const char *org_path, dev_t type, dev_t dev, if (ni) { if (ntfs_inode_close(ni)) set_fuse_error(&res); + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); } else res = -errno; exit: @@ -905,13 +919,7 @@ static int ntfs_fuse_symlink(const char *to, const char *from) return ntfs_fuse_create(from, S_IFLNK, 0, to); } -/** - * NOTE: About the role of mtime: during rename(3), which is currently - * implemented by the help of link() operations, modification time mustn't - * be updated, so we NInoSetNoMtimeUpdate() such inodes after they are opened. - * This is not very nice itself but it may be eliminated, in time. - */ -static int ntfs_fuse_ln(const char *old_path, const char *new_path, int mtime) +static int ntfs_fuse_link(const char *old_path, const char *new_path) { char *name; ntfschar *uname = NULL; @@ -933,9 +941,6 @@ static int ntfs_fuse_ln(const char *old_path, const char *new_path, int mtime) goto exit; } - if (!mtime) - NInoSetNoMtimeUpdate(ni); - /* Generate unicode filename. */ name = strrchr(path, '/'); name++; @@ -951,9 +956,14 @@ static int ntfs_fuse_ln(const char *old_path, const char *new_path, int mtime) res = -errno; goto exit; } - /* Create hard link. */ - if (ntfs_link(ni, dir_ni, uname, uname_len)) + + if (ntfs_link(ni, dir_ni, uname, uname_len)) { res = -errno; + goto exit; + } + + 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) @@ -968,11 +978,6 @@ exit: return res; } -static int ntfs_fuse_link(const char *old_path, const char *new_path) -{ - return ntfs_fuse_ln(old_path, new_path, 1); -} - static int ntfs_fuse_rm(const char *org_path) { char *name; @@ -1006,8 +1011,13 @@ static int ntfs_fuse_rm(const char *org_path) goto exit; } /* Delete object. */ - if (ntfs_delete(ni, dir_ni, uname, uname_len)) + if (ntfs_delete(ni, dir_ni, uname, uname_len)) { res = -errno; + } else { + /* Inode ctime is updated in ntfs_delete() for hard links. */ + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); + } + /* ntfs_delete() always closes ni */ ni = NULL; exit: if (ni && ntfs_inode_close(ni)) @@ -1065,14 +1075,14 @@ static int ntfs_fuse_safe_rename(const char *old_path, ntfs_log_trace("Entering\n"); - ret = ntfs_fuse_ln(new_path, tmp, 0); + ret = ntfs_fuse_link(new_path, tmp); if (ret) return ret; ret = ntfs_fuse_unlink(new_path); if (!ret) { - ret = ntfs_fuse_ln(old_path, new_path, 0); + ret = ntfs_fuse_link(old_path, new_path); if (ret) goto restore; @@ -1086,7 +1096,7 @@ static int ntfs_fuse_safe_rename(const char *old_path, goto cleanup; restore: - if (ntfs_fuse_ln(tmp, new_path, 0)) { + if (ntfs_fuse_link(tmp, new_path)) { err: ntfs_log_perror("Rename failed. Existing file '%s' was renamed " "to '%s'", new_path, tmp); @@ -1155,7 +1165,7 @@ static int ntfs_fuse_rename(const char *old_path, const char *new_path) goto out; } - ret = ntfs_fuse_ln(old_path, new_path, 0); + ret = ntfs_fuse_link(old_path, new_path); if (ret) goto out; @@ -1187,7 +1197,6 @@ static int ntfs_fuse_rmdir(const char *path) static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) { ntfs_inode *ni; - time_t now; int res = 0; if (ntfs_fuse_is_named_data_stream(path)) @@ -1196,20 +1205,13 @@ static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) if (!ni) return -errno; - NInoSetNoParentMtimeUpdate(ni); - - now = time(NULL); - ni->last_mft_change_time = now; - if (buf) { ni->last_access_time = buf->actime; ni->last_data_change_time = buf->modtime; - } else { - ni->last_access_time = now; - ni->last_data_change_time = now; - } - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + } else + ntfs_fuse_update_times(ni, NTFS_UPDATE_AMCTIME); + if (ntfs_inode_close(ni)) set_fuse_error(&res); return res; From 78845d0898e19689fdded5af48e64138073494ad Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 15 Nov 2007 07:35:19 +0000 Subject: [PATCH 041/328] ntfs_readdir(): fix reading dirs with over 600,000 files (GeorgeK, Szaka) --- libntfs-3g/dir.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 65ac831f..b8a16877 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -883,6 +883,7 @@ find_next_index_buffer: if (bmp_buf_pos >> 3 < bmp_buf_size) continue; /* Read next chunk from the index bitmap. */ + bmp_buf_pos = 0; if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size) bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3); br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); From fc67849324bfde6223083238c7410159d92fb2d6 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 18 Nov 2007 13:01:42 +0000 Subject: [PATCH 042/328] ntfs_fuse_truncate: fix memleak if ntfs_attr_truncate failed (Jean-Pierre, Szaka) --- src/ntfs-3g.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 9be0b210..edb48d58 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -716,7 +716,7 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) { ntfs_volume *vol; ntfs_inode *ni = NULL; - ntfs_attr *na; + ntfs_attr *na = NULL; int res; char *path = NULL; ntfschar *stream_name; @@ -738,10 +738,10 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) goto exit; ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); - ntfs_attr_close(na); errno = 0; exit: res = -errno; + ntfs_attr_close(na); if (ni && ntfs_inode_close(ni)) set_fuse_error(&res); free(path); From 76a3a8963c96b1cd5b9bd8c56fdec0ffc15d992d Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 18 Nov 2007 13:14:49 +0000 Subject: [PATCH 043/328] cleanup: don't check ntfs_inode pointer since ntfs_inode_close() does it --- src/ntfs-3g.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index edb48d58..cdb94475 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -415,7 +415,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) stbuf->st_ctime = ni->last_mft_change_time; stbuf->st_mtime = ni->last_data_change_time; exit: - if (ni && ntfs_inode_close(ni)) + if (ntfs_inode_close(ni)) set_fuse_error(&res); free(path); if (stream_name_len) @@ -491,7 +491,7 @@ exit: free(intx_file); if (na) ntfs_attr_close(na); - if (ni && ntfs_inode_close(ni)) + if (ntfs_inode_close(ni)) set_fuse_error(&res); free(path); if (stream_name_len) @@ -650,7 +650,7 @@ ok: exit: if (na) ntfs_attr_close(na); - if (ni && ntfs_inode_close(ni)) + if (ntfs_inode_close(ni)) set_fuse_error(&res); free(path); if (stream_name_len) @@ -703,7 +703,7 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, exit: if (na) ntfs_attr_close(na); - if (ni && ntfs_inode_close(ni)) + if (ntfs_inode_close(ni)) set_fuse_error(&res); free(path); if (stream_name_len) @@ -742,7 +742,7 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) exit: res = -errno; ntfs_attr_close(na); - if (ni && ntfs_inode_close(ni)) + if (ntfs_inode_close(ni)) set_fuse_error(&res); free(path); if (stream_name_len) @@ -826,7 +826,7 @@ static int ntfs_fuse_create(const char *org_path, dev_t type, dev_t dev, res = -errno; exit: free(uname); - if (dir_ni && ntfs_inode_close(dir_ni)) + if (ntfs_inode_close(dir_ni)) set_fuse_error(&res); if (utarget) free(utarget); @@ -969,9 +969,9 @@ 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 (dir_ni && ntfs_inode_close(dir_ni)) + if (ntfs_inode_close(dir_ni)) set_fuse_error(&res); - if (ni && ntfs_inode_close(ni)) + if (ntfs_inode_close(ni)) set_fuse_error(&res); free(uname); free(path); @@ -1020,10 +1020,10 @@ static int ntfs_fuse_rm(const char *org_path) /* ntfs_delete() always closes ni */ ni = NULL; exit: - if (ni && ntfs_inode_close(ni)) + if (ntfs_inode_close(ni)) set_fuse_error(&res); free(uname); - if (dir_ni && ntfs_inode_close(dir_ni)) + if (ntfs_inode_close(dir_ni)) set_fuse_error(&res); free(path); return res; From 008cac84fb75ce3e6955d99faae35af155c96554 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 18 Nov 2007 14:08:44 +0000 Subject: [PATCH 044/328] atime update works (Yura Pakhuchiy, Szabolcs Szakacsits) --- include/ntfs-3g/volume.h | 18 +----------------- libntfs-3g/volume.c | 10 +++------- src/ntfs-3g.c | 13 +++---------- 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index b9f8023a..c4fb9df5 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -43,23 +43,12 @@ #endif /* - * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY and MS_NOATIME, + * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY, * so we define them ourselves. */ #ifndef MS_RDONLY #define MS_RDONLY 1 #endif -/* - * Solaris defines MS_RDONLY but not MS_NOATIME thus we need to carefully - * define MS_NOATIME. - */ -#ifndef MS_NOATIME -#if (MS_RDONLY != 1) -# define MS_NOATIME 1 -#else -# define MS_NOATIME 2 -#endif -#endif #define MS_EXCLUSIVE 0x08000000 @@ -98,7 +87,6 @@ typedef enum { NV_ReadOnly, /* 1: Volume is read-only. */ NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ NV_LogFileEmpty, /* 1: $logFile journal is empty. */ - NV_NoATime, /* 1: Do not update access time. */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -117,10 +105,6 @@ typedef enum { #define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) #define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) -#define NVolNoATime(nv) test_nvol_flag(nv, NoATime) -#define NVolSetNoATime(nv) set_nvol_flag(nv, NoATime) -#define NVolClearNoATime(nv) clear_nvol_flag(nv, NoATime) - /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 030640da..699c2dd2 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -457,8 +457,6 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) if (flags & MS_RDONLY) NVolSetReadOnly(vol); - if (flags & MS_NOATIME) - NVolSetNoATime(vol); /* ...->open needs bracketing to compile with glibc 2.7 */ if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { @@ -739,10 +737,9 @@ out: * to mount as the ntfs volume. * * @flags is an optional second parameter. The same flags are used as for - * the mount system call (man 2 mount). Currently only the following flags - * are implemented: + * the mount system call (man 2 mount). Currently only the following flag + * is implemented: * MS_RDONLY - mount volume read-only - * MS_NOATIME - do not update access time * * The function opens the device @dev and verifies that it contains a valid * bootsector. Then, it allocates an ntfs_volume structure and initializes @@ -1136,9 +1133,8 @@ error_exit: * * @flags is an optional second parameter. The same flags are used as for * the mount system call (man 2 mount). Currently only the following flags - * are implemented: + * is implemented: * MS_RDONLY - mount volume read-only - * MS_NOATIME - do not update access time * * The function opens the device or file @name and verifies that it contains a * valid bootsector. Then, it allocates an ntfs_volume structure and initializes diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index cdb94475..bd6a6ce7 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1210,7 +1210,7 @@ static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) ni->last_data_change_time = buf->modtime; ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); } else - ntfs_fuse_update_times(ni, NTFS_UPDATE_AMCTIME); + ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME); if (ntfs_inode_close(ni)) set_fuse_error(&res); @@ -1620,8 +1620,6 @@ static int ntfs_open(const char *device, char *mntpoint) flags |= MS_EXCLUSIVE; if (ctx->ro) flags |= MS_RDONLY; - if (ctx->noatime) - flags |= MS_NOATIME; if (ctx->force) flags |= MS_FORCE; @@ -1676,13 +1674,6 @@ static char *parse_mount_options(const char *orig_opts) return NULL; } - /* - * FIXME: Due to major performance hit and interference - * issues, always use the 'noatime' options for now. - */ - ctx->noatime = TRUE; - strcat(ret, "noatime,"); - ctx->silent = TRUE; s = options; @@ -1702,6 +1693,8 @@ static char *parse_mount_options(const char *orig_opts) "have value.\n"); goto err_exit; } + ctx->noatime = TRUE; + strcat(ret, "noatime,"); } else if (!strcmp(opt, "fake_rw")) { if (val) { ntfs_log_error("'fake_rw' option should not " From 08128a051fe37676650efdfb6cc7b2b59c2fb6d0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 18 Nov 2007 15:04:39 +0000 Subject: [PATCH 045/328] implement relatime and make it default (Yura Pakhuchiy, Valerie Henson, Szaka) --- src/ntfs-3g.8.in | 26 +++++++++++++++++++++++++- src/ntfs-3g.c | 41 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index c4824a9a..0dcf5510 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -99,7 +99,8 @@ present. The value is given in octal. The default value is 0 which means full access to everybody. .TP .B ro -Mount filesystem read\-only. +Mount filesystem read\-only. Useful if Windows is hibernated or the +NTFS journal file is unclean. .TP .BI locale= value This option can be useful if your language specific locale environment @@ -113,6 +114,29 @@ Force the mounting even if the NTFS logfile is unclean. The logfile will be unconditionally cleared. Use this option with caution and for your own responsibility. .TP +.B atime, noatime, relatime +The +.B atime +option updates inode access time for each access. + +The +.B noatime +option disables inode access time updates which can speed up +file operations and prevent sleeping (notebook) disks spinning +up too often thus saving energy and disk lifetime. + +The +.B relatime +option is very similar to +.B noatime. +It updates inode access times relative to modify or change time. +The access time is only updated if the previous access time was earlier +than the current modify or change time. Unlike +.B noatime +this option doesn't break applications that need to know +if a file has been read since the last time it was modified. +This is the default behaviour. +.TP .B show_sys_files Show the system files in directory listings. Otherwise the default behaviour is to hide the system files. diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index bd6a6ce7..f0be4709 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -92,6 +92,12 @@ typedef enum { FSTYPE_FUSEBLK } fuse_fstype; +typedef enum { + ATIME_ENABLED, + ATIME_DISABLED, + ATIME_RELATIVE +} ntfs_atime_t; + typedef struct { fuse_fill_dir_t filler; void *buf; @@ -110,12 +116,12 @@ typedef struct { unsigned int fmask; unsigned int dmask; ntfs_fuse_streams_interface streams; + ntfs_atime_t atime; BOOL ro; BOOL show_sys_files; BOOL silent; BOOL force; BOOL debug; - BOOL noatime; BOOL no_detach; BOOL blkdev; BOOL mounted; @@ -170,8 +176,12 @@ static int ntfs_fuse_is_named_data_stream(const char *path) static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - if (ctx->noatime) + 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); } @@ -1676,6 +1686,8 @@ static char *parse_mount_options(const char *orig_opts) ctx->silent = TRUE; + ctx->atime = ATIME_RELATIVE; + s = options; while (s && *s && (val = strsep(&s, ","))) { opt = strsep(&val, "="); @@ -1693,8 +1705,21 @@ static char *parse_mount_options(const char *orig_opts) "have value.\n"); goto err_exit; } - ctx->noatime = TRUE; - strcat(ret, "noatime,"); + ctx->atime = ATIME_DISABLED; + } else if (!strcmp(opt, "atime")) { + if (val) { + ntfs_log_error("'atime' option should not " + "have value.\n"); + goto err_exit; + } + ctx->atime = ATIME_ENABLED; + } else if (!strcmp(opt, "relatime")) { + if (val) { + ntfs_log_error("'relatime' option should not " + "have value.\n"); + goto err_exit; + } + ctx->atime = ATIME_RELATIVE; } else if (!strcmp(opt, "fake_rw")) { if (val) { ntfs_log_error("'fake_rw' option should not " @@ -1847,6 +1872,14 @@ static char *parse_mount_options(const char *orig_opts) strcat(ret, def_opts); if (default_permissions) strcat(ret, "default_permissions,"); + + if (ctx->atime == ATIME_RELATIVE) + strcat(ret, "relatime,"); + else if (ctx->atime == ATIME_ENABLED) + strcat(ret, "atime,"); + else + strcat(ret, "noatime,"); + strcat(ret, "fsname="); strcat(ret, opts.device); exit: From 90a845d1f44eca2012480fb12826356be5546267 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 18 Nov 2007 15:07:57 +0000 Subject: [PATCH 046/328] release 1.1119 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index bc334d6d..0e75db50 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.1104],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=14:1:0 +AC_INIT([ntfs-3g],[1.1119],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=15:0:0 AC_CANONICAL_HOST([]) AC_CANONICAL_TARGET([]) From 5d7af2f65363fe2d701ea13d145b54f9796a1a22 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 18 Nov 2007 21:58:34 +0000 Subject: [PATCH 047/328] update last edit date --- src/ntfs-3g.8.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 0dcf5510..31854dd4 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 2006-2007 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G 8 "August 2007" "ntfs-3g @VERSION@" +.TH NTFS-3G 8 "November 2007" "ntfs-3g @VERSION@" .SH NAME ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS From 4ab5ed8a492a469af189f4dbafa70674d3f2f8d7 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 18 Nov 2007 22:37:04 +0000 Subject: [PATCH 048/328] fix: successful file/directory removal may reported "Input/Output error" --- libntfs-3g/dir.c | 8 ++++++-- src/ntfs-3g.c | 16 ++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index b8a16877..b82885d3 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1488,7 +1488,7 @@ search: */ if (ni->mrec->link_count) { ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); - goto out; + goto ok; } ntfs_attr_reinit_search_ctx(actx); while (!ntfs_attrs_walk(actx)) { @@ -1530,12 +1530,16 @@ search: "Leaving inconsistent metadata.\n"); } ni = NULL; +ok: + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); out: if (actx) ntfs_attr_put_search_ctx(actx); if (ictx) ntfs_index_ctx_put(ictx); - if (ni && ntfs_inode_close(ni) && !err) + if (ntfs_inode_close(dir_ni) && !err) + err = errno; + if (ntfs_inode_close(ni) && !err) err = errno; if (err) { errno = err; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index f0be4709..1f4dc4f3 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1020,21 +1020,17 @@ static int ntfs_fuse_rm(const char *org_path) res = -errno; goto exit; } - /* Delete object. */ - if (ntfs_delete(ni, dir_ni, uname, uname_len)) { + + if (ntfs_delete(ni, dir_ni, uname, uname_len)) res = -errno; - } else { - /* Inode ctime is updated in ntfs_delete() for hard links. */ - ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); - } - /* ntfs_delete() always closes ni */ - ni = NULL; + /* ntfs_delete() always closes ni and dir_ni */ + ni = dir_ni = NULL; exit: + if (ntfs_inode_close(dir_ni)) + set_fuse_error(&res); if (ntfs_inode_close(ni)) set_fuse_error(&res); free(uname); - if (ntfs_inode_close(dir_ni)) - set_fuse_error(&res); free(path); return res; } From 6523139d60df39ebcde2fabb5cf097aece984876 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 19 Nov 2007 00:10:09 +0000 Subject: [PATCH 049/328] fix: ntfs_inode_sync_file_name() don't close root dir many times in error paths --- libntfs-3g/inode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index c88a626e..5bb2c4e1 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -588,7 +588,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); - ntfs_inode_close(index_ni); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; continue; } if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { @@ -601,7 +602,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) ntfs_log_perror("Index lookup failed, inode %lld", (long long)index_ni->mft_no); ntfs_index_ctx_put(ictx); - ntfs_inode_close(index_ni); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; continue; } /* Update flags and file size. */ From 1c509c2ed70b09c597826f9f32559afeeb74cde7 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 19 Nov 2007 00:24:24 +0000 Subject: [PATCH 050/328] release 1.1120 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 0e75db50..e9aa2014 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.1119],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=15:0:0 +AC_INIT([ntfs-3g],[1.1120],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=16:0:0 AC_CANONICAL_HOST([]) AC_CANONICAL_TARGET([]) From c7ec45eddd11e05cf0b322f8c65b2069c0612074 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 24 Nov 2007 19:58:21 +0000 Subject: [PATCH 051/328] stop 'configure' if pkg-config isn't installed (penpen, Szabolcs Szakacsits) --- configure.ac | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index e9aa2014..f2552c2f 100644 --- a/configure.ac +++ b/configure.ac @@ -116,9 +116,15 @@ AC_SYS_LARGEFILE case "$target_os" in linux*|darwin*|netbsd*) - PKG_CHECK_MODULES(FUSE_MODULE, fuse >= 2.6.0, [ compile_fuse_module=true ], + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + if test "x$PKG_CONFIG" = "xno" ; then + AC_MSG_ERROR([pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/]) + fi + PKG_CHECK_MODULES(FUSE_MODULE, fuse >= 2.6.0,, [ - AC_MSG_ERROR([FUSE >= 2.6.0 was not found. Either it's not fully installed (e.g. fuse, fuse-utils, libfuse, libfuse2, libfuse-dev, etc packages) or files from an old version are still present. See FUSE at http://fuse.sf.net/]) + AC_MSG_ERROR([FUSE >= 2.6.0 was not found. Either older FUSE is still present, or FUSE is not fully installed (e.g. fuse, libfuse, libfuse2, libfuse-dev, etc packages). Source code: http://fuse.sf.net]) ]);; freebsd*) AC_MSG_ERROR([Please see FreeBSD support at http://www.freshports.org/sysutils/fusefs-ntfs]) From 1731bcf5afaa803a3dfb90cf70b6b34e3bc5c4a7 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 6 Dec 2007 13:45:25 +0000 Subject: [PATCH 052/328] AUTHORS file update --- AUTHORS | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 16495644..cde9c0f2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,12 +1,20 @@ -Past and present authors of ntfs-3g in alphabetical order are: +Present authors of ntfs-3g in alphabetical order: + +Jean-Pierre Andre +Dominique L Bouix +Csaba Henk +Erik Larsson +Alejandro Pulver +Szabolcs Szakacsits +Miklos Szeredi + + +Past authors in alphabetical order: Anton Altaparmakov -Dominique L Bouix Mario Emmenlauer -Csaba Henk Yuval Fledel Yura Pakhuchiy Richard Russon -Szabolcs Szakacsits From 67997a4d88319ed6ff5898bdd4b486089a15e1e9 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 6 Dec 2007 20:44:10 +0000 Subject: [PATCH 053/328] New: built-in FUSE support by using a 50% stripped down, internal FUSE library which linked statically into libntfs-3g. Linux uses this one by default. New: the --with-fuse=external configure option makes ntfs-3g to be compiled with an external FUSE library. For non-Linux operating systems this is the default and the only option currently. New: rewritten, backwards compatible build system. New: README file is installed as documentation. New: the --enable-ldscript configure option uses ldscript instead of .so symlink New: the --disable-mtab configure option disables and ignores the usage of /etc/mtab New: added libntfs-3g.pc.in pkg-config and libntfs-3g.script.so.in ldconfig files Change: use 'make install-strip' instead of 'make strip' (Alon Bar-Lev, Miklos Szeredi, Szabolcs Szakacsits) --- Makefile.am | 28 +- autogen.sh | 4 +- configure.ac | 382 ++- include/Makefile.am | 3 +- include/fuse-lite/Makefile.am | 10 + include/fuse-lite/fuse.h | 657 +++++ include/fuse-lite/fuse_common.h | 209 ++ include/fuse-lite/fuse_kernel.h | 376 +++ include/fuse-lite/fuse_lowlevel.h | 1342 ++++++++++ include/fuse-lite/fuse_lowlevel_compat.h | 17 + include/fuse-lite/fuse_opt.h | 261 ++ include/ntfs-3g/Makefile.am | 61 +- libfuse-lite/Makefile.am | 32 + libfuse-lite/fuse.c | 2826 ++++++++++++++++++++++ libfuse-lite/fuse_i.h | 33 + libfuse-lite/fuse_kern_chan.c | 95 + libfuse-lite/fuse_loop.c | 39 + libfuse-lite/fuse_loop_mt.c | 221 ++ libfuse-lite/fuse_lowlevel.c | 1319 ++++++++++ libfuse-lite/fuse_misc.h | 45 + libfuse-lite/fuse_mt.c | 24 + libfuse-lite/fuse_opt.c | 367 +++ libfuse-lite/fuse_session.c | 182 ++ libfuse-lite/fuse_signals.c | 72 + libfuse-lite/helper.c | 337 +++ libfuse-lite/mount.c | 570 +++++ libfuse-lite/mount_util.c | 240 ++ libfuse-lite/mount_util.h | 17 + libntfs-3g/Makefile.am | 119 +- libntfs-3g/libntfs-3g.pc.in | 10 + libntfs-3g/libntfs-3g.script.so.in | 2 + src/Makefile.am | 79 +- 32 files changed, 9703 insertions(+), 276 deletions(-) create mode 100644 include/fuse-lite/Makefile.am create mode 100644 include/fuse-lite/fuse.h create mode 100644 include/fuse-lite/fuse_common.h create mode 100644 include/fuse-lite/fuse_kernel.h create mode 100644 include/fuse-lite/fuse_lowlevel.h create mode 100644 include/fuse-lite/fuse_lowlevel_compat.h create mode 100644 include/fuse-lite/fuse_opt.h create mode 100644 libfuse-lite/Makefile.am create mode 100644 libfuse-lite/fuse.c create mode 100644 libfuse-lite/fuse_i.h create mode 100644 libfuse-lite/fuse_kern_chan.c create mode 100644 libfuse-lite/fuse_loop.c create mode 100644 libfuse-lite/fuse_loop_mt.c create mode 100644 libfuse-lite/fuse_lowlevel.c create mode 100644 libfuse-lite/fuse_misc.h create mode 100644 libfuse-lite/fuse_mt.c create mode 100644 libfuse-lite/fuse_opt.c create mode 100644 libfuse-lite/fuse_session.c create mode 100644 libfuse-lite/fuse_signals.c create mode 100644 libfuse-lite/helper.c create mode 100644 libfuse-lite/mount.c create mode 100644 libfuse-lite/mount_util.c create mode 100644 libfuse-lite/mount_util.h create mode 100644 libntfs-3g/libntfs-3g.pc.in create mode 100644 libntfs-3g/libntfs-3g.script.so.in diff --git a/Makefile.am b/Makefile.am index 44fb1f6c..3cfc6525 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,17 +1,23 @@ -SUBDIRS = include libntfs-3g src - -EXTRA_DIST = AUTHORS CREDITS COPYING INSTALL NEWS README autogen.sh AUTOMAKE_OPTIONS = gnu -MAINTAINERCLEANFILES = configure Makefile.in aclocal.m4 compile depcomp \ - install-sh ltmain.sh missing config.guess config.sub config.h.in INSTALL +EXTRA_DIST = AUTHORS CREDITS COPYING NEWS autogen.sh -libtool: $(LIBTOOL_DEPS) - $(SHELL) ./config.status --recheck +MAINTAINERCLEANFILES=\ + configure \ + Makefile.in \ + aclocal.m4 \ + compile \ + depcomp \ + install-sh \ + ltmain.sh \ + missing \ + config.guess \ + config.sub \ + config.h.in \ + config.h.in~ \ + INSTALL -strip: - (cd src && $(MAKE) strip) || exit 1; +SUBDIRS= include libfuse-lite libntfs-3g src -libs: - (cd libntfs-3g && $(MAKE) libs) || exit 1; +doc_DATA = README diff --git a/autogen.sh b/autogen.sh index f6813c3f..62e5439b 100755 --- a/autogen.sh +++ b/autogen.sh @@ -18,5 +18,5 @@ exit 1 } -echo Running autoreconf --verbose --install -autoreconf --force --verbose --install +echo Running autoreconf --verbose --install --force +autoreconf --verbose --install --force diff --git a/configure.ac b/configure.ac index f2552c2f..b657fd7e 100644 --- a/configure.ac +++ b/configure.ac @@ -3,8 +3,8 @@ # compilation. # # Copyright (c) 2000-2006 Anton Altaparmakov -# Copyright (c) 2003 Jan Kratochvil # Copyright (c) 2005-2007 Szabolcs Szakacsits +# Copyright (C) 2007 Alon Bar-Lev # # This program/include file is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as published @@ -20,112 +20,137 @@ # 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 -# -AC_PREREQ(2.59) +# Autoconf +AC_PREREQ([2.59]) +AC_INIT([ntfs-3g],[1.1206-BETA],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=18 +AC_CONFIG_SRCDIR([src/ntfs-3g.c]) -AC_INIT([ntfs-3g],[1.1120],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=16:0:0 +# Environment +AC_CANONICAL_HOST +AC_CANONICAL_TARGET -AC_CANONICAL_HOST([]) -AC_CANONICAL_TARGET([]) -AC_CONFIG_SRCDIR([config.h.in]) -AC_CONFIG_HEADERS([config.h]) -AM_INIT_AUTOMAKE +# Automake +AM_INIT_AUTOMAKE([${PACKAGE_NAME}], [${PACKAGE_VERSION}]) +AM_CONFIG_HEADER([config.h]) AM_MAINTAINER_MODE -AM_ENABLE_SHARED -AM_ENABLE_STATIC -AC_PREFIX_DEFAULT(/usr/local) -if test "x$prefix" = "xNONE"; then - prefix=$ac_default_prefix - ac_configure_args="$ac_configure_args --prefix $prefix" -fi - -# Executables should be installed to the root filesystem, otherwise -# automounting NTFS volumes can fail during boot if the driver binaries -# (ntfs-3g, fuse) are on an unmounted partition. -if test "x$exec_prefix" = "xNONE"; then - exec_prefix=/ - ac_configure_args="$ac_configure_args --exec-prefix $exec_prefix" -fi - -AC_MSG_NOTICE([Setting exec_prefix to $exec_prefix]) - -# Command-line options. -AC_ARG_ENABLE(debug, - AS_HELP_STRING(--enable-debug,enable additional debugging code and - output), , - enable_debug=no +# Options +AC_ARG_ENABLE( + [debug], + [AC_HELP_STRING([--enable-debug], [enable debugging code and output])], + , + [enable_debug="no"] ) -AC_ARG_ENABLE(really-static, - AS_HELP_STRING(--enable-really-static,create static binaries - for the utilities), , - enable_really_static=no -) -AM_CONDITIONAL(REALLYSTATIC, test "$enable_really_static" = yes) - -AC_ARG_ENABLE(warnings, - AS_HELP_STRING(--enable-warnings,enable additional compiler warnings), , - enable_warnings=no +AC_ARG_ENABLE( + [warnings], + [AC_HELP_STRING([--enable-warnings], [enable lots of compiler warnings])], + , + [enable_warnings="no"] ) -AC_ARG_ENABLE(ldconfig, - AS_HELP_STRING(--disable-ldconfig,do not update dynamic linker cache - using ldconfig), , - enable_ldconfig=yes +AC_ARG_ENABLE( + [pedantic], + [AC_HELP_STRING([--enable-pedantic], [enable compile pedantic mode])], + , + [enable_pedantic="no"] ) -AC_ARG_ENABLE(library, - AS_HELP_STRING(--disable-library,do not install libntfs-3g but link - it into ntfs-3g), , - enable_library=yes +AC_ARG_ENABLE( + [really-static], + [AC_HELP_STRING([--enable-really-static], [create fully static binaries])], + , + [enable_really_static="no"] ) -if test "$enable_library" != yes; then - enable_shared=no - enable_ldconfig=no -fi +AM_CONDITIONAL([REALLYSTATIC], [test "${enable_really_static}" = "yes"]) + +AC_ARG_ENABLE( + [library], + [AC_HELP_STRING([--disable-library], [do not install libntfs-3g but link it into ntfs-3g])], + , + [enable_library="yes"] +) +test "${enable_really_static}" = "yes" && enable_library="no" AM_CONDITIONAL(INSTALL_LIBRARY, test "$enable_library" = yes) +AC_ARG_ENABLE( + [ldconfig], + [AC_HELP_STRING([--disable-ldconfig], [do not update dynamic linker cache using ldconfig])], + , + [enable_ldconfig="yes"] +) +test "${enable_library}" = "no" && enable_ldconfig="no" # --disable-library will disable to run ldconfig since no point to do so. AM_CONDITIONAL(RUN_LDCONFIG, test "$enable_ldconfig" = yes) -# Use GNU extensions if available. -AC_GNU_SOURCE +AC_ARG_ENABLE( + [ldscript], + [AC_HELP_STRING([--enable-ldscript], [use ldscript instead of .so symlink])], + , + [enable_ldscript="no"] +) -# Checks for programs. +AC_ARG_ENABLE( + [mtab], + [AC_HELP_STRING([--disable-mtab], [disable and ignore usage of /etc/mtab])], + , + [enable_mtab="yes"] +) + +AC_ARG_ENABLE( + [device-default-io-ops], + [AC_HELP_STRING([--disable-device-default-io-ops], [Install default IO ops])], + , + [enable_device_default_io_ops="yes"] +) + +# Programs +AC_GNU_SOURCE AC_PROG_CC -AC_PROG_GCC_TRADITIONAL -AC_PROG_INSTALL -AC_PATH_PROG(RM, rm, rm) +AC_PROG_LIBTOOL +AC_PROG_LN_S +AM_PROG_CC_C_O # No need to check for ldconfig if --disable-ldconfig was given if test "$enable_ldconfig" = yes; then AC_PATH_PROG(LDCONFIG, ldconfig, true, [/sbin /usr/sbin $PATH]) fi -AC_PROG_LN_S -AC_PROG_MAKE_SET -AC_PROG_LIBTOOL +AC_PATH_PROG([MV], [mv]) +AC_PATH_PROG([RM], [rm]) +AC_PATH_PROG([SED], [sed]) -# Libraries often install their metadata .pc files in directories not searched -# by pkg-config. Let's workaround this. -export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/lib/pkgconfig:/usr/lib/pkgconfig:/opt/gnome/lib/pkgconfig:/usr/share/pkgconfig:/usr/local/lib/pkgconfig:$prefix/lib/pkgconfig:/opt/gnome/share/pkgconfig:/usr/local/share/pkgconfig +# Environment +AC_MSG_CHECKING([Windows OS]) +case "${target}" in +*-mingw32*|*-winnt*|*-cygwin*) + AC_MSG_RESULT([yes]) + WINDOWS="yes" + AC_DEFINE( + [WINDOWS], + [1], + [Define to 1 if this is a Windows OS] + ) + ;; +*) + AC_MSG_RESULT([no]) + WINDOWS="no" + ;; +esac -# Enable large file support. -AC_SYS_LARGEFILE - -case "$target_os" in -linux*|darwin*|netbsd*) - if test -z "$PKG_CONFIG"; then - AC_PATH_PROG(PKG_CONFIG, pkg-config, no) - fi - if test "x$PKG_CONFIG" = "xno" ; then - AC_MSG_ERROR([pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/]) - fi - PKG_CHECK_MODULES(FUSE_MODULE, fuse >= 2.6.0,, - [ - AC_MSG_ERROR([FUSE >= 2.6.0 was not found. Either older FUSE is still present, or FUSE is not fully installed (e.g. fuse, libfuse, libfuse2, libfuse-dev, etc packages). Source code: http://fuse.sf.net]) - ]);; +AC_MSG_CHECKING([fuse compatibility]) +case "${target_os}" in +linux*) + AC_ARG_WITH( + [fuse], + [AC_HELP_STRING([--with-fuse=], [Select FUSE library: internal or external @<:@default=internal@:>@])], + , + [with_fuse="internal"] + ) + ;; +darwin*|netbsd*) + with_fuse="external" + ;; freebsd*) AC_MSG_ERROR([Please see FreeBSD support at http://www.freshports.org/sysutils/fusefs-ntfs]) ;; @@ -133,51 +158,56 @@ freebsd*) AC_MSG_ERROR([ntfs-3g can be built only under Linux, FreeBSD, Mac OS X, and NetBSD.]) ;; esac +AC_MSG_RESULT([${with_fuse}]) -# Static linking failed because FUSE 2.6.[01] forgot to include -lrt. -# However FreeBSD doesn't have one. So, here we go with the code from FUSE. -libfuse_libs= -LIBS= -AC_SEARCH_LIBS(clock_gettime, [rt]) -libfuse_libs="$libfuse_libs $LIBS" -LIBS= -AC_SUBST(libfuse_libs) - -FUSE_LIB_PATH=`$PKG_CONFIG --libs-only-L fuse | sed -e 's,/[/]*,/,g' -e 's,[ ]*$,,'` - -# add --with-extra-includes and --with-extra-libs switch to ./configure -all_libraries="$all_libraries $USER_LDFLAGS" -all_includes="$all_includes $USER_INCLUDES" -AC_SUBST(all_includes) -AC_SUBST(all_libraries) - -# Add our compiler switches not discarding 'CFLAGS' as they may have been -# passed to us by rpmbuild(8). -# We add -Wall to enable some compiler warnings. -CFLAGS="$CFLAGS -Wall" - -# Add lots of extra warnings if --enable-warnings was specified. -if test "$enable_warnings" = "yes"; then - CFLAGS="$CFLAGS -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings" +if test "${enable_ldscript}" = "yes"; then + AC_MSG_CHECKING([Output format]) + OUTPUT_FORMAT="$(${CC} ${CFLAGS} ${LDFLAGS} -Wl,--verbose 2>&1 | ${SED} -n 's/^OUTPUT_FORMAT("\([[^"]]*\)",.*/\1/p')" + if test -z "${OUTPUT_FORMAT}"; then + AC_MSG_RESULT([None]) + else + AC_MSG_RESULT([${OUTPUT_FORMAT}]) + OUTPUT_FORMAT="OUTPUT_FORMAT ( ${OUTPUT_FORMAT} )" + fi fi -# Add debugging switches if --enable-debug was specified. -if test "$enable_debug" = "yes"; then - CFLAGS="$CFLAGS -ggdb3 -DDEBUG" +# Libraries +if test "${with_fuse}" = "internal"; then + AC_CHECK_LIB( + [pthread], + [pthread_create], + [LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lpthread"], + [AC_MSG_ERROR([Cannot find pthread library])] + ) + AC_CHECK_LIB( + [rt], + [clock_gettime], + [LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lrt"], + [AC_MSG_ERROR([Cannot find rt library])] + ) + # required so that we re-compile anything + AC_DEFINE( + [FUSE_INTERNAL], + [1], + [Define to 1 if using internal fuse] + ) +else + PKG_PROG_PKG_CONFIG + test -z "${PKG_CONFIG}" && AC_MSG_ERROR([pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/]) + # Libraries often install their metadata .pc files in directories + # not searched by pkg-config. Let's workaround this. + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/lib/pkgconfig:/usr/lib/pkgconfig:/opt/gnome/lib/pkgconfig:/usr/share/pkgconfig:/usr/local/lib/pkgconfig:$prefix/lib/pkgconfig:/opt/gnome/share/pkgconfig:/usr/local/share/pkgconfig + PKG_CHECK_MODULES( + [FUSE_MODULE], + [fuse >= 2.6.0], + , + [ + AC_MSG_ERROR([FUSE >= 2.6.0 was not found. Either older FUSE is still present, or FUSE is not fully installed (e.g. fuse, libfuse, libfuse2, libfuse-dev, etc packages). Source code: http://fuse.sf.net]) + ] + ) + FUSE_LIB_PATH=`$PKG_CONFIG --libs-only-L fuse | sed -e 's,/[/]*,/,g' -e 's,[ ]*$,,'` fi -AC_SUBST(CFLAGS) -AC_SUBST(CPPFLAGS) -AC_SUBST(LDFLAGS) -AC_SUBST(LIBS) - -AC_SUBST(LIBNTFS_3G_VERSION) -AC_SUBST(LIBNTFS_3G_CFLAGS) - -AC_SUBST(AUTODIRS) - -# Checks for libraries. - # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ @@ -190,17 +220,26 @@ AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL -AC_C_BIGENDIAN(, - [AC_DEFINE([WORDS_LITTLEENDIAN], 1, - [Define to 1 if your processor stores words with the least significant - byte first (like Intel and VAX, unlike Motorola and SPARC).])] - ,) +AC_C_BIGENDIAN( + , + [ + AC_DEFINE( + [WORDS_LITTLEENDIAN], + [1], + [Define to 1 if your processor stores words with the least significant + byte first (like Intel and VAX, unlike Motorola and SPARC).] + ) + ] + , +) AC_C_CONST AC_C_INLINE AC_TYPE_OFF_T AC_TYPE_SIZE_T AC_STRUCT_ST_BLOCKS AC_CHECK_MEMBERS([struct stat.st_rdev]) +AC_CHECK_MEMBERS([struct stat.st_atim]) +AC_CHECK_MEMBERS([struct stat.st_atimespec]) # Checks for library functions. AC_FUNC_GETMNTENT @@ -210,23 +249,97 @@ AC_FUNC_STAT AC_FUNC_STRFTIME AC_FUNC_UTIME_NULL AC_FUNC_VPRINTF -AC_CHECK_FUNCS([atexit basename dup2 fdatasync getopt_long hasmntopt mbsinit \ +AC_CHECK_FUNCS([ \ + atexit basename dup2 fdatasync getopt_long hasmntopt mbsinit \ memmove memset realpath regcomp setlocale setxattr strcasecmp strchr \ - strdup strerror strnlen strtol strtoul sysconf utime]) + strdup strerror strnlen strtol strtoul sysconf utime fork \ +]) +AC_SYS_LARGEFILE -# Makefiles to be created by configure. +# We add -Wall to enable some compiler warnings. +CFLAGS="${CFLAGS} -Wall -fno-strict-aliasing" + +if test "${enable_pedantic}" = "yes"; then + enable_warnings="yes" + CFLAGS="${CFLAGS} -pedantic" +fi + +if test "${enable_warnings}" = "yes"; then + CFLAGS="${CFLAGS} -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings" +fi + +if test "${enable_debug}" = "yes"; then + CFLAGS="${CFLAGS} -ggdb3 -DDEBUG" + AC_DEFINE( + [ENABLE_DEBUG], + [1], + [Define to 1 if debug should be enabled] + ) +fi + +test "${enable_device_default_io_ops}" = "no" && AC_DEFINE( + [NO_NTFS_DEVICE_DEFAULT_IO_OPS], + [1], + [Don't use default IO ops] +) + +if test "${enable_mtab}" = "no"; then + AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab]) +fi + +# Settings +pkgconfigdir="\$(libdir)/pkgconfig" +ntfs3gincludedir="\$(includedir)/ntfs-3g" +# Executables should be installed to the root filesystem, otherwise +# automounting NTFS volumes can fail during boot if the driver binaries +# and their dependencies are on an unmounted partition. Use --exec-prefix +# to override this. +if test "x${exec_prefix}" = "xNONE"; then + rootbindir="/bin" + rootsbindir="/sbin" + rootlibdir="/lib${libdir##*/lib}" +else + rootbindir="\$(bindir)" + rootsbindir="\$(sbindir)" + rootlibdir="\$(libdir)" +fi +AC_SUBST([pkgconfigdir]) +AC_SUBST([ntfs3gincludedir]) +AC_SUBST([rootbindir]) +AC_SUBST([rootsbindir]) +AC_SUBST([rootlibdir]) +AC_SUBST([LIBNTFS_3G_VERSION]) +AC_SUBST([LIBFUSE_LITE_LIBS]) +AC_SUBST([OUTPUT_FORMAT]) +AM_CONDITIONAL([FUSE_INTERNAL], [test "${with_fuse}" = "internal"]) +AM_CONDITIONAL([GENERATE_LDSCRIPT], [test "${enable_ldscript}" = "yes"]) +AM_CONDITIONAL([WINDOWS], [test "${WINDOWS}" = "yes"]) +AM_CONDITIONAL([NTFS_DEVICE_DEFAULT_IO_OPS], [test "${enable_device_default_io_ops}" = "yes"]) + +# workaround for /dev/null; then + cat < + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#ifndef _FUSE_H_ +#define _FUSE_H_ + +/** @file + * + * This file defines the library interface of FUSE + */ + +#include "fuse_common.h" + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------- * + * Basic FUSE API * + * ----------------------------------------------------------- */ + +/** Handle for a FUSE filesystem */ +struct fuse; + +/** Structure containing a raw command */ +struct fuse_cmd; + +/** Function to add an entry in a readdir() operation + * + * @param buf the buffer passed to the readdir() operation + * @param name the file name of the directory entry + * @param stat file attributes, can be NULL + * @param off offset of the next entry or zero + * @return 1 if buffer is full, zero otherwise + */ +typedef int (*fuse_fill_dir_t) (void *buf, const char *name, + const struct stat *stbuf, off_t off); + +/** + * The file system operations: + * + * Most of these should work very similarly to the well known UNIX + * file system operations. A major exception is that instead of + * returning an error in 'errno', the operation should return the + * negated error value (-errno) directly. + * + * All methods are optional, but some are essential for a useful + * filesystem (e.g. getattr). Open, flush, release, fsync, opendir, + * releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock, + * init and destroy are special purpose methods, without which a full + * featured filesystem can still be implemented. + */ +struct fuse_operations { + /** Get file attributes. + * + * Similar to stat(). The 'st_dev' and 'st_blksize' fields are + * ignored. The 'st_ino' field is ignored except if the 'use_ino' + * mount option is given. + */ + int (*getattr) (const char *, struct stat *); + + /** Read the target of a symbolic link + * + * The buffer should be filled with a null terminated string. The + * buffer size argument includes the space for the terminating + * null character. If the linkname is too long to fit in the + * buffer, it should be truncated. The return value should be 0 + * for success. + */ + int (*readlink) (const char *, char *, size_t); + + /** Create a file node + * + * This is called for creation of all non-directory, non-symlink + * nodes. If the filesystem defines a create() method, then for + * regular files that will be called instead. + */ + int (*mknod) (const char *, mode_t, dev_t); + + /** Create a directory */ + int (*mkdir) (const char *, mode_t); + + /** Remove a file */ + int (*unlink) (const char *); + + /** Remove a directory */ + int (*rmdir) (const char *); + + /** Create a symbolic link */ + int (*symlink) (const char *, const char *); + + /** Rename a file */ + int (*rename) (const char *, const char *); + + /** Create a hard link to a file */ + int (*link) (const char *, const char *); + + /** Change the permission bits of a file */ + int (*chmod) (const char *, mode_t); + + /** Change the owner and group of a file */ + int (*chown) (const char *, uid_t, gid_t); + + /** Change the size of a file */ + int (*truncate) (const char *, off_t); + + /** Change the access and/or modification times of a file + * + * Deprecated, use utimens() instead. + */ + int (*utime) (const char *, struct utimbuf *); + + /** File open operation + * + * No creation, or truncation flags (O_CREAT, O_EXCL, O_TRUNC) + * will be passed to open(). Open should check if the operation + * is permitted for the given flags. Optionally open may also + * return an arbitrary filehandle in the fuse_file_info structure, + * which will be passed to all file operations. + * + * Changed in version 2.2 + */ + int (*open) (const char *, struct fuse_file_info *); + + /** Read data from an open file + * + * Read should return exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the + * 'direct_io' mount option is specified, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * Changed in version 2.2 + */ + int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *); + + /** Write data to an open file + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the 'direct_io' + * mount option is specified (see read operation). + * + * Changed in version 2.2 + */ + int (*write) (const char *, const char *, size_t, off_t, + struct fuse_file_info *); + + /** Get file system statistics + * + * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored + * + * Replaced 'struct statfs' parameter with 'struct statvfs' in + * version 2.5 + */ + int (*statfs) (const char *, struct statvfs *); + + /** Possibly flush cached data + * + * BIG NOTE: This is not equivalent to fsync(). It's not a + * request to sync dirty data. + * + * Flush is called on each close() of a file descriptor. So if a + * filesystem wants to return write errors in close() and the file + * has cached dirty data, this is a good place to write back data + * and return any errors. Since many applications ignore close() + * errors this is not always useful. + * + * NOTE: The flush() method may be called more than once for each + * open(). This happens if more than one file descriptor refers + * to an opened file due to dup(), dup2() or fork() calls. It is + * not possible to determine if a flush is final, so each flush + * should be treated equally. Multiple write-flush sequences are + * relatively rare, so this shouldn't be a problem. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * Changed in version 2.2 + */ + int (*flush) (const char *, struct fuse_file_info *); + + /** Release an open file + * + * Release is called when there are no more references to an open + * file: all file descriptors are closed and all memory mappings + * are unmapped. + * + * For every open() call there will be exactly one release() call + * with the same flags and file descriptor. It is possible to + * have a file opened more than once, in which case only the last + * release will mean, that no more reads/writes will happen on the + * file. The return value of release is ignored. + * + * Changed in version 2.2 + */ + int (*release) (const char *, struct fuse_file_info *); + + /** Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * Changed in version 2.2 + */ + int (*fsync) (const char *, int, struct fuse_file_info *); + + /** Set extended attributes */ + int (*setxattr) (const char *, const char *, const char *, size_t, int); + + /** Get extended attributes */ + int (*getxattr) (const char *, const char *, char *, size_t); + + /** List extended attributes */ + int (*listxattr) (const char *, char *, size_t); + + /** Remove extended attributes */ + int (*removexattr) (const char *, const char *); + + /** Open directory + * + * This method should check if the open operation is permitted for + * this directory + * + * Introduced in version 2.3 + */ + int (*opendir) (const char *, struct fuse_file_info *); + + /** Read directory + * + * The filesystem may choose between two modes of operation: + * + * 1) The readdir implementation ignores the offset parameter, and + * passes zero to the filler function's offset. The filler + * function will not return '1' (unless an error happens), so the + * whole directory is read in a single readdir operation. + * + * 2) The readdir implementation keeps track of the offsets of the + * directory entries. It uses the offset parameter and always + * passes non-zero offset to the filler function. When the buffer + * is full (or an error happens) the filler function will return + * '1'. + * + * Introduced in version 2.3 + */ + int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, + struct fuse_file_info *); + + /** Release directory + * + * Introduced in version 2.3 + */ + int (*releasedir) (const char *, struct fuse_file_info *); + + /** Synchronize directory contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data + * + * Introduced in version 2.3 + */ + int (*fsyncdir) (const char *, int, struct fuse_file_info *); + + /** + * Initialize filesystem + * + * The return value will passed in the private_data field of + * fuse_context to all file operations and as a parameter to the + * destroy() method. + * + * Introduced in version 2.3 + * Changed in version 2.6 + */ + void *(*init) (struct fuse_conn_info *conn); + + /** + * Clean up filesystem + * + * Called on filesystem exit. + * + * Introduced in version 2.3 + */ + void (*destroy) (void *); + + /** + * Check file access permissions + * + * This will be called for the access() system call. If the + * 'default_permissions' mount option is given, this method is not + * called. + * + * This method is not called under Linux kernel versions 2.4.x + * + * Introduced in version 2.5 + */ + int (*access) (const char *, int); + + /** + * Create and open a file + * + * If the file does not exist, first create it with the specified + * mode, and then open it. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the mknod() and open() methods + * will be called instead. + * + * Introduced in version 2.5 + */ + int (*create) (const char *, mode_t, struct fuse_file_info *); + + /** + * Change the size of an open file + * + * This method is called instead of the truncate() method if the + * truncation was invoked from an ftruncate() system call. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the truncate() method will be + * called instead. + * + * Introduced in version 2.5 + */ + int (*ftruncate) (const char *, off_t, struct fuse_file_info *); + + /** + * Get attributes from an open file + * + * This method is called instead of the getattr() method if the + * file information is available. + * + * Currently this is only called after the create() method if that + * is implemented (see above). Later it may be called for + * invocations of fstat() too. + * + * Introduced in version 2.5 + */ + int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *); + + /** + * Perform POSIX file locking operation + * + * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. + * + * For the meaning of fields in 'struct flock' see the man page + * for fcntl(2). The l_whence field will always be set to + * SEEK_SET. + * + * For checking lock ownership, the 'fuse_file_info->owner' + * argument must be used. + * + * For F_GETLK operation, the library will first check currently + * held locks, and if a conflicting lock is found it will return + * information without calling this method. This ensures, that + * for local locks the l_pid field is correctly filled in. The + * results may not be accurate in case of race conditions and in + * the presence of hard links, but it's unlikly that an + * application would rely on accurate GETLK results in these + * cases. If a conflicting lock is not found, this method will be + * called, and the filesystem may fill out l_pid by a meaningful + * value, or it may leave this field zero. + * + * For F_SETLK and F_SETLKW the l_pid field will be set to the pid + * of the process performing the locking operation. + * + * Note: if this method is not implemented, the kernel will still + * allow file locking to work locally. Hence it is only + * interesting for network filesystems and similar. + * + * Introduced in version 2.6 + */ + int (*lock) (const char *, struct fuse_file_info *, int cmd, + struct flock *); + + /** + * Change the access and modification times of a file with + * nanosecond resolution + * + * Introduced in version 2.6 + */ + int (*utimens) (const char *, const struct timespec tv[2]); + + /** + * Map block index within file to block index within device + * + * Note: This makes sense only for block device backed filesystems + * mounted with the 'blkdev' option + * + * Introduced in version 2.6 + */ + int (*bmap) (const char *, size_t blocksize, uint64_t *idx); +}; + +/** Extra context that may be needed by some filesystems + * + * The uid, gid and pid fields are not filled in case of a writepage + * operation. + */ +struct fuse_context { + /** Pointer to the fuse object */ + struct fuse *fuse; + + /** User ID of the calling process */ + uid_t uid; + + /** Group ID of the calling process */ + gid_t gid; + + /** Thread ID of the calling process */ + pid_t pid; + + /** Private filesystem data */ + void *private_data; +}; + +/** + * Main function of FUSE. + * + * This is for the lazy. This is all that has to be called from the + * main() function. + * + * This function does the following: + * - parses command line options (-d -s and -h) + * - passes relevant mount options to the fuse_mount() + * - installs signal handlers for INT, HUP, TERM and PIPE + * - registers an exit handler to unmount the filesystem on program exit + * - creates a fuse handle + * - registers the operations + * - calls either the single-threaded or the multi-threaded event loop + * + * Note: this is currently implemented as a macro. + * + * @param argc the argument counter passed to the main() function + * @param argv the argument vector passed to the main() function + * @param op the file system operation + * @param user_data user data supplied in the context during the init() method + * @return 0 on success, nonzero on failure + */ +/* +int fuse_main(int argc, char *argv[], const struct fuse_operations *op, + void *user_data); +*/ +#define fuse_main(argc, argv, op, user_data) \ + fuse_main_real(argc, argv, op, sizeof(*(op)), user_data) + +/* ----------------------------------------------------------- * + * More detailed API * + * ----------------------------------------------------------- */ + +/** + * Create a new FUSE filesystem. + * + * @param ch the communication channel + * @param args argument vector + * @param op the filesystem operations + * @param op_size the size of the fuse_operations structure + * @param user_data user data supplied in the context during the init() method + * @return the created FUSE handle + */ +struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *op, size_t op_size, + void *user_data); + +/** + * Destroy the FUSE handle. + * + * The communication channel attached to the handle is also destroyed. + * + * NOTE: This function does not unmount the filesystem. If this is + * needed, call fuse_unmount() before calling this function. + * + * @param f the FUSE handle + */ +void fuse_destroy(struct fuse *f); + +/** + * FUSE event loop. + * + * Requests from the kernel are processed, and the appropriate + * operations are called. + * + * @param f the FUSE handle + * @return 0 if no error occurred, -1 otherwise + */ +int fuse_loop(struct fuse *f); + +/** + * Exit from event loop + * + * @param f the FUSE handle + */ +void fuse_exit(struct fuse *f); + +/** + * FUSE event loop with multiple threads + * + * Requests from the kernel are processed, and the appropriate + * operations are called. Request are processed in parallel by + * distributing them between multiple threads. + * + * Calling this function requires the pthreads library to be linked to + * the application. + * + * @param f the FUSE handle + * @return 0 if no error occurred, -1 otherwise + */ +int fuse_loop_mt(struct fuse *f); + +/** + * Get the current context + * + * The context is only valid for the duration of a filesystem + * operation, and thus must not be stored and used later. + * + * @return the context + */ +struct fuse_context *fuse_get_context(void); + +/** + * Check if a request has already been interrupted + * + * @param req request handle + * @return 1 if the request has been interrupted, 0 otherwise + */ +int fuse_interrupted(void); + +/** + * The real main function + * + * Do not call this directly, use fuse_main() + */ +int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, + size_t op_size, void *user_data); + +/* + * Stacking API + */ + +/** + * Fuse filesystem object + * + * This is opaque object represents a filesystem layer + */ +struct fuse_fs; + +/* + * These functions call the relevant filesystem operation, and return + * the result. + * + * If the operation is not defined, they return -ENOSYS, with the + * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir, + * fuse_fs_releasedir and fuse_fs_statfs, which return 0. + */ + +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf); +int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi); +int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, + const char *newpath); +int fuse_fs_unlink(struct fuse_fs *fs, const char *path); +int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); +int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, + const char *path); +int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); +int fuse_fs_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_open(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, + off_t off, struct fuse_file_info *fi); +int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi); +int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi); +int fuse_fs_flush(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); +int fuse_fs_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, + fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi); +int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi); +int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi); +int fuse_fs_lock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int cmd, struct flock *lock); +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode); +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid); +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size); +int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, + struct fuse_file_info *fi); +int fuse_fs_utimens(struct fuse_fs *fs, const char *path, + const struct timespec tv[2]); +int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); +int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, + size_t len); +int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, + dev_t rdev); +int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); +int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, + const char *value, size_t size, int flags); +int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, + char *value, size_t size); +int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, + size_t size); +int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, + const char *name); +int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, + uint64_t *idx); +void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); +void fuse_fs_destroy(struct fuse_fs *fs); + +/** + * Create a new fuse filesystem object + * + * This is usually called from the factory of a fuse module to create + * a new instance of a filesystem. + * + * @param op the filesystem operations + * @param op_size the size of the fuse_operations structure + * @param user_data user data supplied in the context during the init() method + * @return a new filesystem object + */ +struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, + void *user_data); + +/* ----------------------------------------------------------- * + * Advanced API for event handling, don't worry about this... * + * ----------------------------------------------------------- */ + +/* NOTE: the following functions are deprecated, and will be removed + from the 3.0 API. Use the lowlevel session functions instead */ + +/** Get session from fuse object */ +struct fuse_session *fuse_get_session(struct fuse *f); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_H_ */ diff --git a/include/fuse-lite/fuse_common.h b/include/fuse-lite/fuse_common.h new file mode 100644 index 00000000..b7e5791b --- /dev/null +++ b/include/fuse-lite/fuse_common.h @@ -0,0 +1,209 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +/** @file */ + +#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_) +#error "Never include directly; use or + +/** Major version of FUSE library interface */ +#define FUSE_MAJOR_VERSION 2 + +/** Minor version of FUSE library interface */ +#define FUSE_MINOR_VERSION 7 + +#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) +#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) + +/* This interface uses 64 bit off_t */ +#if _FILE_OFFSET_BITS != 64 +#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Information about open files + * + * Changed in version 2.5 + */ +struct fuse_file_info { + /** Open flags. Available in open() and release() */ + int flags; + + /** Old file handle, don't use */ + unsigned long fh_old; + + /** In case of a write operation indicates if this was caused by a + writepage */ + int writepage; + + /** Can be filled in by open, to use direct I/O on this file. + Introduced in version 2.4 */ + unsigned int direct_io : 1; + + /** Can be filled in by open, to indicate, that cached file data + need not be invalidated. Introduced in version 2.4 */ + unsigned int keep_cache : 1; + + /** Indicates a flush operation. Set in flush operation, also + maybe set in highlevel lock operation and lowlevel release + operation. Introduced in version 2.6 */ + unsigned int flush : 1; + + /** Padding. Do not use*/ + unsigned int padding : 29; + + /** File handle. May be filled in by filesystem in open(). + Available in all other file operations */ + uint64_t fh; + + /** Lock owner id. Available in locking operations and flush */ + uint64_t lock_owner; +}; + +/** + * Connection information, passed to the ->init() method + * + * Some of the elements are read-write, these can be changed to + * indicate the value requested by the filesystem. The requested + * value must usually be smaller than the indicated value. + */ +struct fuse_conn_info { + /** + * Major version of the protocol (read-only) + */ + unsigned proto_major; + + /** + * Minor version of the protocol (read-only) + */ + unsigned proto_minor; + + /** + * Is asynchronous read supported (read-write) + */ + unsigned async_read; + + /** + * Maximum size of the write buffer + */ + unsigned max_write; + + /** + * Maximum readahead + */ + unsigned max_readahead; + + /** + * For future use. + */ + unsigned reserved[27]; +}; + +struct fuse_session; +struct fuse_chan; + +/** + * Create a FUSE mountpoint + * + * Returns a control file descriptor suitable for passing to + * fuse_new() + * + * @param mountpoint the mount point path + * @param args argument vector + * @return the communication channel on success, NULL on failure + */ +struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args); + +/** + * Umount a FUSE mountpoint + * + * @param mountpoint the mount point path + * @param ch the communication channel + */ +void fuse_unmount(const char *mountpoint, struct fuse_chan *ch); + +/** + * Parse common options + * + * The following options are parsed: + * + * '-f' foreground + * '-d' '-odebug' foreground, but keep the debug option + * '-s' single threaded + * '-h' '--help' help + * '-ho' help without header + * '-ofsname=..' file system name, if not present, then set to the program + * name + * + * All parameters may be NULL + * + * @param args argument vector + * @param mountpoint the returned mountpoint, should be freed after use + * @param multithreaded set to 1 unless the '-s' option is present + * @param foreground set to 1 if one of the relevant options is present + * @return 0 on success, -1 on failure + */ +int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, + int *multithreaded, int *foreground); + +/** + * Go into the background + * + * @param foreground if true, stay in the foreground + * @return 0 on success, -1 on failure + */ +int fuse_daemonize(int foreground); + +/** + * Get the version of the library + * + * @return the version + */ +int fuse_version(void); + +/* ----------------------------------------------------------- * + * Signal handling * + * ----------------------------------------------------------- */ + +/** + * Exit session on HUP, TERM and INT signals and ignore PIPE signal + * + * Stores session in a global variable. May only be called once per + * process until fuse_remove_signal_handlers() is called. + * + * @param se the session to exit + * @return 0 on success, -1 on failure + */ +int fuse_set_signal_handlers(struct fuse_session *se); + +/** + * Restore default signal handlers + * + * Resets global session. After this fuse_set_signal_handlers() may + * be called again. + * + * @param se the same session as given in fuse_set_signal_handlers() + */ +void fuse_remove_signal_handlers(struct fuse_session *se); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_COMMON_H_ */ diff --git a/include/fuse-lite/fuse_kernel.h b/include/fuse-lite/fuse_kernel.h new file mode 100644 index 00000000..be124ee9 --- /dev/null +++ b/include/fuse-lite/fuse_kernel.h @@ -0,0 +1,376 @@ +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#ifndef linux +#include +#define __u64 uint64_t +#define __u32 uint32_t +#define __s32 int32_t +#else +#include +#include +#endif + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 8 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** The major number of the fuse character device */ +#define FUSE_MAJOR MISC_MAJOR + +/** The minor number of the fuse character device */ +#define FUSE_MINOR 229 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + __u64 ino; + __u64 size; + __u64 blocks; + __u64 atime; + __u64 mtime; + __u64 ctime; + __u32 atimensec; + __u32 mtimensec; + __u32 ctimensec; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 rdev; +}; + +struct fuse_kstatfs { + __u64 blocks; + __u64 bfree; + __u64 bavail; + __u64 files; + __u64 ffree; + __u32 bsize; + __u32 namelen; + __u32 frsize; + __u32 padding; + __u32 spare[6]; +}; + +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) + +/** + * INIT request/reply flags + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +struct fuse_entry_out { + __u64 nodeid; /* Inode ID */ + __u64 generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + __u64 entry_valid; /* Cache timeout for the name */ + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 entry_valid_nsec; + __u32 attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + __u64 nlookup; +}; + +struct fuse_attr_out { + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 attr_valid_nsec; + __u32 dummy; + struct fuse_attr attr; +}; + +struct fuse_mknod_in { + __u32 mode; + __u32 rdev; +}; + +struct fuse_mkdir_in { + __u32 mode; + __u32 padding; +}; + +struct fuse_rename_in { + __u64 newdir; +}; + +struct fuse_link_in { + __u64 oldnodeid; +}; + +struct fuse_setattr_in { + __u32 valid; + __u32 padding; + __u64 fh; + __u64 size; + __u64 unused1; + __u64 atime; + __u64 mtime; + __u64 unused2; + __u32 atimensec; + __u32 mtimensec; + __u32 unused3; + __u32 mode; + __u32 unused4; + __u32 uid; + __u32 gid; + __u32 unused5; +}; + +struct fuse_open_in { + __u32 flags; + __u32 mode; +}; + +struct fuse_open_out { + __u64 fh; + __u32 open_flags; + __u32 padding; +}; + +struct fuse_release_in { + __u64 fh; + __u32 flags; + __u32 release_flags; + __u64 lock_owner; +}; + +struct fuse_flush_in { + __u64 fh; + __u32 unused; + __u32 padding; + __u64 lock_owner; +}; + +struct fuse_read_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 padding; +}; + +struct fuse_write_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 write_flags; +}; + +struct fuse_write_out { + __u32 size; + __u32 padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + __u64 fh; + __u32 fsync_flags; + __u32 padding; +}; + +struct fuse_setxattr_in { + __u32 size; + __u32 flags; +}; + +struct fuse_getxattr_in { + __u32 size; + __u32 padding; +}; + +struct fuse_getxattr_out { + __u32 size; + __u32 padding; +}; + +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + __u32 mask; + __u32 padding; +}; + +struct fuse_init_in { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; +}; + +struct fuse_init_out { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; + __u32 unused; + __u32 max_write; +}; + +struct fuse_interrupt_in { + __u64 unique; +}; + +struct fuse_bmap_in { + __u64 block; + __u32 blocksize; + __u32 padding; +}; + +struct fuse_bmap_out { + __u64 block; +}; + +struct fuse_in_header { + __u32 len; + __u32 opcode; + __u64 unique; + __u64 nodeid; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 padding; +}; + +struct fuse_out_header { + __u32 len; + __s32 error; + __u64 unique; +}; + +struct fuse_dirent { + __u64 ino; + __u64 off; + __u32 namelen; + __u32 type; + char name[0]; +}; + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/include/fuse-lite/fuse_lowlevel.h b/include/fuse-lite/fuse_lowlevel.h new file mode 100644 index 00000000..662aca0d --- /dev/null +++ b/include/fuse-lite/fuse_lowlevel.h @@ -0,0 +1,1342 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#ifndef _FUSE_LOWLEVEL_H_ +#define _FUSE_LOWLEVEL_H_ + +/** @file + * + * Low level API + */ + +#include "fuse_common.h" + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------- * + * Miscellaneous definitions * + * ----------------------------------------------------------- */ + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** Inode number type */ +typedef unsigned long fuse_ino_t; + +/** Request pointer type */ +typedef struct fuse_req *fuse_req_t; + +/** + * Session + * + * This provides hooks for processing requests, and exiting + */ +struct fuse_session; + +/** + * Channel + * + * A communication channel, providing hooks for sending and receiving + * messages + */ +struct fuse_chan; + +/** Directory entry parameters supplied to fuse_reply_entry() */ +struct fuse_entry_param { + /** Unique inode number + * + * In lookup, zero means negative entry (from version 2.5) + * Returning ENOENT also means negative entry, but by setting zero + * ino the kernel may cache negative entries for entry_timeout + * seconds. + */ + fuse_ino_t ino; + + /** Generation number for this entry. The ino/generation pair + should be unique for the filesystem's lifetime. It must be + non-zero, otherwise FUSE will treat it as an error. */ + unsigned long generation; + + /** Inode attributes. Even if attr_timeout == 0, attr must be + correct. For example, for open(), FUSE uses attr.st_size from + lookup() to determine how many bytes to request. If this + value is not correct, incorrect data will be returned. */ + struct stat attr; + + /** Validity timeout (in seconds) for the attributes */ + double attr_timeout; + + /** Validity timeout (in seconds) for the name */ + double entry_timeout; +}; + +/** Additional context associated with requests */ +struct fuse_ctx { + /** User ID of the calling process */ + uid_t uid; + + /** Group ID of the calling process */ + gid_t gid; + + /** Thread ID of the calling process */ + pid_t pid; +}; + +/* 'to_set' flags in setattr */ +#define FUSE_SET_ATTR_MODE (1 << 0) +#define FUSE_SET_ATTR_UID (1 << 1) +#define FUSE_SET_ATTR_GID (1 << 2) +#define FUSE_SET_ATTR_SIZE (1 << 3) +#define FUSE_SET_ATTR_ATIME (1 << 4) +#define FUSE_SET_ATTR_MTIME (1 << 5) + +/* ----------------------------------------------------------- * + * Request methods and replies * + * ----------------------------------------------------------- */ + +/** + * Low level filesystem operations + * + * Most of the methods (with the exception of init and destroy) + * receive a request handle (fuse_req_t) as their first argument. + * This handle must be passed to one of the specified reply functions. + * + * This may be done inside the method invocation, or after the call + * has returned. The request handle is valid until one of the reply + * functions is called. + * + * Other pointer arguments (name, fuse_file_info, etc) are not valid + * after the call has returned, so if they are needed later, their + * contents have to be copied. + * + * The filesystem sometimes needs to handle a return value of -ENOENT + * from the reply function, which means, that the request was + * interrupted, and the reply discarded. For example if + * fuse_reply_open() return -ENOENT means, that the release method for + * this file will not be called. + */ +struct fuse_lowlevel_ops { + /** + * Initialize filesystem + * + * Called before any other filesystem method + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_lowlevel_new() + */ + void (*init) (void *userdata, struct fuse_conn_info *conn); + + /** + * Clean up filesystem + * + * Called on filesystem exit + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_lowlevel_new() + */ + void (*destroy) (void *userdata); + + /** + * Look up a directory entry by name and get its attributes. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name the name to look up + */ + void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Forget about an inode + * + * The nlookup parameter indicates the number of lookups + * previously performed on this inode. + * + * If the filesystem implements inode lifetimes, it is recommended + * that inodes acquire a single reference on each lookup, and lose + * nlookup references on each forget. + * + * The filesystem may ignore forget calls, if the inodes don't + * need to have a limited lifetime. + * + * On unmount it is not guaranteed, that all referenced inodes + * will receive a forget message. + * + * Valid replies: + * fuse_reply_none + * + * @param req request handle + * @param ino the inode number + * @param nlookup the number of lookups to forget + */ + void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); + + /** + * Get file attributes + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi for future use, currently always NULL + */ + void (*getattr) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Set file attributes + * + * In the 'attr' argument only members indicated by the 'to_set' + * bitmask contain valid values. Other members contain undefined + * values. + * + * If the setattr was invoked from the ftruncate() system call + * under Linux kernel versions 2.6.15 or later, the fi->fh will + * contain the value set by the open method or will be undefined + * if the open method didn't set any value. Otherwise (not + * ftruncate call, or kernel version earlier than 2.6.15) the fi + * parameter will be NULL. + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param attr the attributes + * @param to_set bit mask of attributes which should be set + * @param fi file information, or NULL + * + * Changed in version 2.5: + * file information filled in for ftruncate + */ + void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi); + + /** + * Read symbolic link + * + * Valid replies: + * fuse_reply_readlink + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + */ + void (*readlink) (fuse_req_t req, fuse_ino_t ino); + + /** + * Create file node + * + * Create a regular file, character device, block device, fifo or + * socket node. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param rdev the device number (only valid if created file is a device) + */ + void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev); + + /** + * Create a directory + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode with which to create the new file + */ + void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode); + + /** + * Remove a file + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Remove a directory + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Create a symbolic link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param link the contents of the symbolic link + * @param parent inode number of the parent directory + * @param name to create + */ + void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, + const char *name); + + /** Rename a file + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the old parent directory + * @param name old name + * @param newparent inode number of the new parent directory + * @param newname new name + */ + void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname); + + /** + * Create a hard link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param ino the old inode number + * @param newparent inode number of the new parent directory + * @param newname new name to create + */ + void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname); + + /** + * Open a file + * + * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and + * O_TRUNC) are available in fi->flags. + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other file operations + * (read, write, flush, release, fsync). + * + * Filesystem may also implement stateless file I/O and not store + * anything in fi->fh. + * + * There are also some flags (direct_io, keep_cache) which the + * filesystem may set in fi, to change the way the file is opened. + * See fuse_file_info structure in for more details. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*open) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Read data + * + * Read should send exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the file + * has been opened in 'direct_io' mode, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size number of bytes to read + * @param off offset to read from + * @param fi file information + */ + void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + + /** + * Write data + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the file has + * been opened in 'direct_io' mode, in which case the return value + * of the write system call will reflect the return value of this + * operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_write + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param buf data to write + * @param size number of bytes to write + * @param off offset to write to + * @param fi file information + */ + void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi); + + /** + * Flush method + * + * This is called on each close() of the opened file. + * + * Since file descriptors can be duplicated (dup, dup2, fork), for + * one open call there may be many flush calls. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * NOTE: the name of the method is misleading, since (unlike + * fsync) the filesystem is not forced to flush pending writes. + * One reason to flush data, is if the filesystem wants to return + * write errors. + * + * If the filesystem supports file locking operations (setlk, + * getlk) it should remove all locks belonging to 'fi->owner'. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Release an open file + * + * Release is called when there are no more references to an open + * file: all file descriptors are closed and all memory mappings + * are unmapped. + * + * For every open call there will be exactly one release call. + * + * The filesystem may reply with an error, but error values are + * not returned to close() or munmap() which triggered the + * release. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * fi->flags will contain the same flags as for open. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*release) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); + + /** + * Open a directory + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other directory + * stream operations (readdir, releasedir, fsyncdir). + * + * Filesystem may also implement stateless directory I/O and not + * store anything in fi->fh, though that makes it impossible to + * implement standard conforming directory stream operations in + * case the contents of the directory can change between opendir + * and releasedir. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*opendir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Read directory + * + * Send a buffer filled using fuse_add_direntry(), with size not + * exceeding the requested size. Send an empty buffer on end of + * stream. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum number of bytes to send + * @param off offset to continue reading the directory stream + * @param fi file information + */ + void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + + /** + * Release an open directory + * + * For every opendir call there will be exactly one releasedir + * call. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*releasedir) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Synchronize directory contents + * + * If the datasync parameter is non-zero, then only the directory + * contents should be flushed, not the meta data. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); + + /** + * Get file system statistics + * + * Valid replies: + * fuse_reply_statfs + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number, zero means "undefined" + */ + void (*statfs) (fuse_req_t req, fuse_ino_t ino); + + /** + * Set an extended attribute + * + * Valid replies: + * fuse_reply_err + */ + void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags); + + /** + * Get an extended attribute + * + * If size is zero, the size of the value should be sent with + * fuse_reply_xattr. + * + * If the size is non-zero, and the value fits in the buffer, the + * value should be sent with fuse_reply_buf. + * + * If the size is too small for the value, the ERANGE error should + * be sent. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + * @param size maximum size of the value to send + */ + void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size); + + /** + * List extended attribute names + * + * If size is zero, the total size of the attribute list should be + * sent with fuse_reply_xattr. + * + * If the size is non-zero, and the null character separated + * attribute list fits in the buffer, the list should be sent with + * fuse_reply_buf. + * + * If the size is too small for the list, the ERANGE error should + * be sent. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum size of the list to send + */ + void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); + + /** + * Remove an extended attribute + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + */ + void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); + + /** + * Check file access permissions + * + * This will be called for the access() system call. If the + * 'default_permissions' mount option is given, this method is not + * called. + * + * This method is not called under Linux kernel versions 2.4.x + * + * Introduced in version 2.5 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param mask requested access mode + */ + void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); + + /** + * Create and open a file + * + * If the file does not exist, first create it with the specified + * mode, and then open it. + * + * Open flags (with the exception of O_NOCTTY) are available in + * fi->flags. + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other file operations + * (read, write, flush, release, fsync). + * + * There are also some flags (direct_io, keep_cache) which the + * filesystem may set in fi, to change the way the file is opened. + * See fuse_file_info structure in for more details. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the mknod() and open() methods + * will be called instead. + * + * Introduced in version 2.5 + * + * Valid replies: + * fuse_reply_create + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param fi file information + */ + void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi); + + /** + * Test for a POSIX file lock + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_lock + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + */ + void (*getlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock); + + /** + * Acquire, modify or release a POSIX file lock + * + * For POSIX threads (NPTL) there's a 1-1 relation between pid and + * owner, but otherwise this is not always the case. For checking + * lock ownership, 'fi->owner' must be used. The l_pid field in + * 'struct flock' should only be used to fill in this field in + * getlk(). + * + * Note: if the locking methods are not implemented, the kernel + * will still allow file locking to work locally. Hence these are + * only interesting for network filesystems and similar. + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + * @param sleep locking operation may sleep + */ + void (*setlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock, int sleep); + + /** + * Map block index within file to block index within device + * + * Note: This makes sense only for block device backed filesystems + * mounted with the 'blkdev' option + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_bmap + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param blocksize unit of block index + * @param idx block index within file + */ + void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t idx); +}; + +/** + * Reply with an error code or success + * + * Possible requests: + * all except forget + * + * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, + * removexattr and setlk may send a zero code + * + * @param req request handle + * @param err the positive error value, or zero for success + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_err(fuse_req_t req, int err); + +/** + * Don't send reply + * + * Possible requests: + * forget + * + * @param req request handle + */ +void fuse_reply_none(fuse_req_t req); + +/** + * Reply with a directory entry + * + * Possible requests: + * lookup, mknod, mkdir, symlink, link + * + * @param req request handle + * @param e the entry parameters + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); + +/** + * Reply with a directory entry and open parameters + * + * currently the following members of 'fi' are used: + * fh, direct_io, keep_cache + * + * Possible requests: + * create + * + * @param req request handle + * @param e the entry parameters + * @param fi file information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *fi); + +/** + * Reply with attributes + * + * Possible requests: + * getattr, setattr + * + * @param req request handle + * @param the attributes + * @param attr_timeout validity timeout (in seconds) for the attributes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_attr(fuse_req_t req, const struct stat *attr, + double attr_timeout); + +/** + * Reply with the contents of a symbolic link + * + * Possible requests: + * readlink + * + * @param req request handle + * @param link symbolic link contents + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_readlink(fuse_req_t req, const char *link); + +/** + * Reply with open parameters + * + * currently the following members of 'fi' are used: + * fh, direct_io, keep_cache + * + * Possible requests: + * open, opendir + * + * @param req request handle + * @param fi file information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi); + +/** + * Reply with number of bytes written + * + * Possible requests: + * write + * + * @param req request handle + * @param count the number of bytes written + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_write(fuse_req_t req, size_t count); + +/** + * Reply with data + * + * Possible requests: + * read, readdir, getxattr, listxattr + * + * @param req request handle + * @param buf buffer containing data + * @param size the size of data in bytes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); + +/** + * Reply with data vector + * + * Possible requests: + * read, readdir, getxattr, listxattr + * + * @param req request handle + * @param iov the vector containing the data + * @param count the size of vector + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count); + +/** + * Reply with filesystem statistics + * + * Possible requests: + * statfs + * + * @param req request handle + * @param stbuf filesystem statistics + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf); + +/** + * Reply with needed buffer size + * + * Possible requests: + * getxattr, listxattr + * + * @param req request handle + * @param count the buffer size needed in bytes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_xattr(fuse_req_t req, size_t count); + +/** + * Reply with file lock information + * + * Possible requests: + * getlk + * + * @param req request handle + * @param lock the lock information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_lock(fuse_req_t req, struct flock *lock); + +/** + * Reply with block index + * + * Possible requests: + * bmap + * + * @param req request handle + * @param idx block index within device + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_bmap(fuse_req_t req, uint64_t idx); + +/* ----------------------------------------------------------- * + * Filling a buffer in readdir * + * ----------------------------------------------------------- */ + +/** + * Add a directory entry to the buffer + * + * Buffer needs to be large enough to hold the entry. Of it's not, + * then the entry is not filled in but the size of the entry is still + * returned. The caller can check this by comparing the bufsize + * parameter with the returned entry size. If the entry size is + * larger than the buffer size, the operation failed. + * + * From the 'stbuf' argument the st_ino field and bits 12-15 of the + * st_mode field are used. The other fields are ignored. + * + * Note: offsets do not necessarily represent physical offsets, and + * could be any marker, that enables the implementation to find a + * specific point in the directory stream. + * + * @param req request handle + * @param buf the point where the new entry will be added to the buffer + * @param bufsize remaining size of the buffer + * @param the name of the entry + * @param stbuf the file attributes + * @param off the offset of the next entry + * @return the space needed for the entry + */ +size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + const char *name, const struct stat *stbuf, + off_t off); + +/* ----------------------------------------------------------- * + * Utility functions * + * ----------------------------------------------------------- */ + +/** + * Get the userdata from the request + * + * @param req request handle + * @return the user data passed to fuse_lowlevel_new() + */ +void *fuse_req_userdata(fuse_req_t req); + +/** + * Get the context from the request + * + * The pointer returned by this function will only be valid for the + * request's lifetime + * + * @param req request handle + * @return the context structure + */ +const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); + +/** + * Callback function for an interrupt + * + * @param req interrupted request + * @param data user data + */ +typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); + +/** + * Register/unregister callback for an interrupt + * + * If an interrupt has already happened, then the callback function is + * called from within this function, hence it's not possible for + * interrupts to be lost. + * + * @param req request handle + * @param func the callback function or NULL for unregister + * @parm data user data passed to the callback function + */ +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + void *data); + +/** + * Check if a request has already been interrupted + * + * @param req request handle + * @return 1 if the request has been interrupted, 0 otherwise + */ +int fuse_req_interrupted(fuse_req_t req); + +/* ----------------------------------------------------------- * + * Filesystem setup * + * ----------------------------------------------------------- */ + +/** + * Create a low level session + * + * @param args argument vector + * @param op the low level filesystem operations + * @param op_size sizeof(struct fuse_lowlevel_ops) + * @param userdata user data + * @return the created session object, or NULL on failure + */ +struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata); + +/* ----------------------------------------------------------- * + * Session interface * + * ----------------------------------------------------------- */ + +/** + * Session operations + * + * This is used in session creation + */ +struct fuse_session_ops { + /** + * Hook to process a request (mandatory) + * + * @param data user data passed to fuse_session_new() + * @param buf buffer containing the raw request + * @param len request length + * @param ch channel on which the request was received + */ + void (*process) (void *data, const char *buf, size_t len, + struct fuse_chan *ch); + + /** + * Hook for session exit and reset (optional) + * + * @param data user data passed to fuse_session_new() + * @param val exited status (1 - exited, 0 - not exited) + */ + void (*exit) (void *data, int val); + + /** + * Hook for querying the current exited status (optional) + * + * @param data user data passed to fuse_session_new() + * @return 1 if exited, 0 if not exited + */ + int (*exited) (void *data); + + /** + * Hook for cleaning up the channel on destroy (optional) + * + * @param data user data passed to fuse_session_new() + */ + void (*destroy) (void *data); +}; + +/** + * Create a new session + * + * @param op session operations + * @param data user data + * @return new session object, or NULL on failure + */ +struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data); + +/** + * Assign a channel to a session + * + * Note: currently only a single channel may be assigned. This may + * change in the future + * + * If a session is destroyed, the assigned channel is also destroyed + * + * @param se the session + * @param ch the channel + */ +void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch); + +/** + * Remove a channel from a session + * + * If the channel is not assigned to a session, then this is a no-op + * + * @param ch the channel to remove + */ +void fuse_session_remove_chan(struct fuse_chan *ch); + +/** + * Iterate over the channels assigned to a session + * + * The iterating function needs to start with a NULL channel, and + * after that needs to pass the previously returned channel to the + * function. + * + * @param se the session + * @param ch the previous channel, or NULL + * @return the next channel, or NULL if no more channels exist + */ +struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, + struct fuse_chan *ch); + +/** + * Process a raw request + * + * @param se the session + * @param buf buffer containing the raw request + * @param len request length + * @param ch channel on which the request was received + */ +void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, + struct fuse_chan *ch); + +/** + * Destroy a session + * + * @param se the session + */ +void fuse_session_destroy(struct fuse_session *se); + +/** + * Exit a session + * + * @param se the session + */ +void fuse_session_exit(struct fuse_session *se); + +/** + * Reset the exited status of a session + * + * @param se the session + */ +void fuse_session_reset(struct fuse_session *se); + +/** + * Query the exited status of a session + * + * @param se the session + * @return 1 if exited, 0 if not exited + */ +int fuse_session_exited(struct fuse_session *se); + +/** + * Enter a single threaded event loop + * + * @param se the session + * @return 0 on success, -1 on error + */ +int fuse_session_loop(struct fuse_session *se); + +/** + * Enter a multi-threaded event loop + * + * @param se the session + * @return 0 on success, -1 on error + */ +int fuse_session_loop_mt(struct fuse_session *se); + +/* ----------------------------------------------------------- * + * Channel interface * + * ----------------------------------------------------------- */ + +/** + * Channel operations + * + * This is used in channel creation + */ +struct fuse_chan_ops { + /** + * Hook for receiving a raw request + * + * @param ch pointer to the channel + * @param buf the buffer to store the request in + * @param size the size of the buffer + * @return the actual size of the raw request, or -1 on error + */ + int (*receive)(struct fuse_chan **chp, char *buf, size_t size); + + /** + * Hook for sending a raw reply + * + * A return value of -ENOENT means, that the request was + * interrupted, and the reply was discarded + * + * @param ch the channel + * @param iov vector of blocks + * @param count the number of blocks in vector + * @return zero on success, -errno on failure + */ + int (*send)(struct fuse_chan *ch, const struct iovec iov[], size_t count); + + /** + * Destroy the channel + * + * @param ch the channel + */ + void (*destroy)(struct fuse_chan *ch); +}; + +/** + * Create a new channel + * + * @param op channel operations + * @param fd file descriptor of the channel + * @param bufsize the minimal receive buffer size + * @param data user data + * @return the new channel object, or NULL on failure + */ +struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, + size_t bufsize, void *data); + +/** + * Query the file descriptor of the channel + * + * @param ch the channel + * @return the file descriptor passed to fuse_chan_new() + */ +int fuse_chan_fd(struct fuse_chan *ch); + +/** + * Query the minimal receive buffer size + * + * @param ch the channel + * @return the buffer size passed to fuse_chan_new() + */ +size_t fuse_chan_bufsize(struct fuse_chan *ch); + +/** + * Query the user data + * + * @param ch the channel + * @return the user data passed to fuse_chan_new() + */ +void *fuse_chan_data(struct fuse_chan *ch); + +/** + * Query the session to which this channel is assigned + * + * @param ch the channel + * @return the session, or NULL if the channel is not assigned + */ +struct fuse_session *fuse_chan_session(struct fuse_chan *ch); + +/** + * Receive a raw request + * + * A return value of -ENODEV means, that the filesystem was unmounted + * + * @param ch pointer to the channel + * @param buf the buffer to store the request in + * @param size the size of the buffer + * @return the actual size of the raw request, or -errno on error + */ +int fuse_chan_recv(struct fuse_chan **ch, char *buf, size_t size); + +/** + * Send a raw reply + * + * A return value of -ENOENT means, that the request was + * interrupted, and the reply was discarded + * + * @param ch the channel + * @param iov vector of blocks + * @param count the number of blocks in vector + * @return zero on success, -errno on failure + */ +int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], + size_t count); + +/** + * Destroy a channel + * + * @param ch the channel + */ +void fuse_chan_destroy(struct fuse_chan *ch); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_LOWLEVEL_H_ */ diff --git a/include/fuse-lite/fuse_lowlevel_compat.h b/include/fuse-lite/fuse_lowlevel_compat.h new file mode 100644 index 00000000..87be491b --- /dev/null +++ b/include/fuse-lite/fuse_lowlevel_compat.h @@ -0,0 +1,17 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +/* these definitions provide source compatibility to prior versions. + Do not include this file directly! */ + +size_t fuse_dirent_size(size_t namelen); + +char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, + off_t off); + +struct fuse_chan *fuse_kern_chan_new(int fd); diff --git a/include/fuse-lite/fuse_opt.h b/include/fuse-lite/fuse_opt.h new file mode 100644 index 00000000..6123d0b4 --- /dev/null +++ b/include/fuse-lite/fuse_opt.h @@ -0,0 +1,261 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#ifndef _FUSE_OPT_H_ +#define _FUSE_OPT_H_ + +/** @file + * + * This file defines the option parsing interface of FUSE + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Option description + * + * This structure describes a single option, and and action associated + * with it, in case it matches. + * + * More than one such match may occur, in which case the action for + * each match is executed. + * + * There are three possible actions in case of a match: + * + * i) An integer (int or unsigned) variable determined by 'offset' is + * set to 'value' + * + * ii) The processing function is called, with 'value' as the key + * + * iii) An integer (any) or string (char *) variable determined by + * 'offset' is set to the value of an option parameter + * + * 'offset' should normally be either set to + * + * - 'offsetof(struct foo, member)' actions i) and iii) + * + * - -1 action ii) + * + * The 'offsetof()' macro is defined in the header. + * + * The template determines which options match, and also have an + * effect on the action. Normally the action is either i) or ii), but + * if a format is present in the template, then action iii) is + * performed. + * + * The types of templates are: + * + * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only + * themselves. Invalid values are "--" and anything beginning + * with "-o" + * + * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or + * the relevant option in a comma separated option list + * + * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) + * which have a parameter + * + * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform + * action iii). + * + * 5) "-x ", etc. Matches either "-xparam" or "-x param" as + * two separate arguments + * + * 6) "-x %s", etc. Combination of 4) and 5) + * + * If the format is "%s", memory is allocated for the string unlike + * with scanf(). + */ +struct fuse_opt { + /** Matching template and optional parameter formatting */ + const char *templ; + + /** + * Offset of variable within 'data' parameter of fuse_opt_parse() + * or -1 + */ + unsigned long offset; + + /** + * Value to set the variable to, or to be passed as 'key' to the + * processing function. Ignored if template has a format + */ + int value; +}; + +/** + * Key option. In case of a match, the processing function will be + * called with the specified key. + */ +#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } + +/** + * Last option. An array of 'struct fuse_opt' must end with a NULL + * template value + */ +#define FUSE_OPT_END { .templ = NULL } + +/** + * Argument list + */ +struct fuse_args { + /** Argument count */ + int argc; + + /** Argument vector. NULL terminated */ + char **argv; + + /** Is 'argv' allocated? */ + int allocated; +}; + +/** + * Initializer for 'struct fuse_args' + */ +#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } + +/** + * Key value passed to the processing function if an option did not + * match any template + */ +#define FUSE_OPT_KEY_OPT -1 + +/** + * Key value passed to the processing function for all non-options + * + * Non-options are the arguments beginning with a charater other than + * '-' or all arguments after the special '--' option + */ +#define FUSE_OPT_KEY_NONOPT -2 + +/** + * Special key value for options to keep + * + * Argument is not passed to processing function, but behave as if the + * processing function returned 1 + */ +#define FUSE_OPT_KEY_KEEP -3 + +/** + * Special key value for options to discard + * + * Argument is not passed to processing function, but behave as if the + * processing function returned zero + */ +#define FUSE_OPT_KEY_DISCARD -4 + +/** + * Processing function + * + * This function is called if + * - option did not match any 'struct fuse_opt' + * - argument is a non-option + * - option did match and offset was set to -1 + * + * The 'arg' parameter will always contain the whole argument or + * option including the parameter if exists. A two-argument option + * ("-x foo") is always converted to single arguemnt option of the + * form "-xfoo" before this function is called. + * + * Options of the form '-ofoo' are passed to this function without the + * '-o' prefix. + * + * The return value of this function determines whether this argument + * is to be inserted into the output argument vector, or discarded. + * + * @param data is the user data passed to the fuse_opt_parse() function + * @param arg is the whole argument or option + * @param key determines why the processing function was called + * @param outargs the current output argument list + * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept + */ +typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, + struct fuse_args *outargs); + +/** + * Option parsing function + * + * If 'args' was returned from a previous call to fuse_opt_parse() or + * it was constructed from + * + * A NULL 'args' is equivalent to an empty argument vector + * + * A NULL 'opts' is equivalent to an 'opts' array containing a single + * end marker + * + * A NULL 'proc' is equivalent to a processing function always + * returning '1' + * + * @param args is the input and output argument list + * @param data is the user data + * @param opts is the option description array + * @param proc is the processing function + * @return -1 on error, 0 on success + */ +int fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc); + +/** + * Add an option to a comma separated option list + * + * @param opts is a pointer to an option list, may point to a NULL value + * @param opt is the option to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_opt(char **opts, const char *opt); + +/** + * Add an argument to a NULL terminated argument vector + * + * @param args is the structure containing the current argument list + * @param arg is the new argument to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_arg(struct fuse_args *args, const char *arg); + +/** + * Add an argument at the specified position in a NULL terminated + * argument vector + * + * Adds the argument to the N-th position. This is useful for adding + * options at the beggining of the array which must not come after the + * special '--' option. + * + * @param args is the structure containing the current argument list + * @param pos is the position at which to add the argument + * @param arg is the new argument to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); + +/** + * Free the contents of argument list + * + * The structure itself is not freed + * + * @param args is the structure containing the argument list + */ +void fuse_opt_free_args(struct fuse_args *args); + + +/** + * Check if an option matches + * + * @param opts is the option description array + * @param opt is the option to match + * @return 1 if a match is found, 0 if not + */ +int fuse_opt_match(const struct fuse_opt opts[], const char *opt); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_OPT_H_ */ diff --git a/include/ntfs-3g/Makefile.am b/include/ntfs-3g/Makefile.am index c8f9f9d1..870ec2a5 100644 --- a/include/ntfs-3g/Makefile.am +++ b/include/ntfs-3g/Makefile.am @@ -1,35 +1,36 @@ +MAINTAINERCLEANFILES = Makefile.in + if INSTALL_LIBRARY -linux_ntfsincludedir = $(includedir)/ntfs-3g -linux_ntfsinclude_HEADERS = \ - attrib.h \ - attrlist.h \ - bitmap.h \ - bootsect.h \ - collate.h \ - compat.h \ - compress.h \ - debug.h \ - device.h \ - device_io.h \ - dir.h \ - endians.h \ - index.h \ - inode.h \ - layout.h \ - lcnalloc.h \ - logfile.h \ - logging.h \ - mft.h \ - mst.h \ - ntfstime.h \ - runlist.h \ - security.h \ - support.h \ - types.h \ - unistr.h \ - version.h \ +ntfs3ginclude_HEADERS = \ + attrib.h \ + attrlist.h \ + bitmap.h \ + bootsect.h \ + collate.h \ + compat.h \ + compress.h \ + debug.h \ + device.h \ + device_io.h \ + dir.h \ + endians.h \ + index.h \ + inode.h \ + layout.h \ + lcnalloc.h \ + logfile.h \ + logging.h \ + mft.h \ + misc.h \ + mst.h \ + ntfstime.h \ + runlist.h \ + security.h \ + support.h \ + types.h \ + unistr.h \ + version.h \ volume.h endif -MAINTAINERCLEANFILES = Makefile.in diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am new file mode 100644 index 00000000..f1455544 --- /dev/null +++ b/libfuse-lite/Makefile.am @@ -0,0 +1,32 @@ + +MAINTAINERCLEANFILES = Makefile.in + +if FUSE_INTERNAL +noinst_LTLIBRARIES = libfuse-lite.la +endif + +libfuse_lite_la_CFLAGS= \ + $(AM_CFLAGS) \ + -I$(top_srcdir)/include/fuse-lite \ + -DFUSERMOUNT_DIR=\"$(bindir)\" \ + -D_REENTRANT \ + -D_FILE_OFFSET_BITS=64 + +libfuse_lite_la_LIBADD = $(LIBFUSE_LITE_LIBS) + +libfuse_lite_la_SOURCES = \ + fuse.c \ + fuse_i.h \ + fuse_kern_chan.c \ + fuse_loop.c \ + fuse_loop_mt.c \ + fuse_lowlevel.c \ + fuse_misc.h \ + fuse_mt.c \ + fuse_opt.c \ + fuse_session.c \ + fuse_signals.c \ + helper.c \ + mount.c \ + mount_util.c \ + mount_util.h diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c new file mode 100644 index 00000000..6c5deced --- /dev/null +++ b/libfuse-lite/fuse.c @@ -0,0 +1,2826 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + + +/* For pthread_rwlock_t */ +#define _GNU_SOURCE + +#include "fuse_i.h" +#include "fuse_lowlevel.h" +#include "fuse_opt.h" +#include "fuse_misc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUSE_MAX_PATH 4096 +#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1 + +#define FUSE_UNKNOWN_INO 0xffffffff +#define OFFSET_MAX 0x7fffffffffffffffLL + +struct fuse_config { + unsigned int uid; + unsigned int gid; + unsigned int umask; + double entry_timeout; + double negative_timeout; + double attr_timeout; + double ac_attr_timeout; + int ac_attr_timeout_set; + int debug; + int hard_remove; + int use_ino; + int readdir_ino; + int set_mode; + int set_uid; + int set_gid; + int direct_io; + int kernel_cache; + int auto_cache; + int intr; + int intr_signal; + int help; +}; + +struct fuse_fs { + struct fuse_operations op; + void *user_data; +}; + +struct fuse { + struct fuse_session *se; + struct node **name_table; + size_t name_table_size; + struct node **id_table; + size_t id_table_size; + fuse_ino_t ctr; + unsigned int generation; + unsigned int hidectr; + pthread_mutex_t lock; + pthread_rwlock_t tree_lock; + struct fuse_config conf; + int intr_installed; + struct fuse_fs *fs; +}; + +struct lock { + int type; + off_t start; + off_t end; + pid_t pid; + uint64_t owner; + struct lock *next; +}; + +struct node { + struct node *name_next; + struct node *id_next; + fuse_ino_t nodeid; + unsigned int generation; + int refctr; + struct node *parent; + char *name; + uint64_t nlookup; + int open_count; + int is_hidden; + struct timespec stat_updated; + struct timespec mtime; + off_t size; + int cache_valid; + struct lock *locks; +}; + +struct fuse_dh { + pthread_mutex_t lock; + struct fuse *fuse; + fuse_req_t req; + char *contents; + int allocated; + unsigned len; + unsigned size; + unsigned needlen; + int filled; + uint64_t fh; + int error; + fuse_ino_t nodeid; +}; + +struct fuse_context_i { + struct fuse_context ctx; + fuse_req_t req; +}; + +static pthread_key_t fuse_context_key; +static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER; +static int fuse_context_ref; + +static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid) +{ + size_t hash = nodeid % f->id_table_size; + struct node *node; + + for (node = f->id_table[hash]; node != NULL; node = node->id_next) + if (node->nodeid == nodeid) + return node; + + return NULL; +} + +static struct node *get_node(struct fuse *f, fuse_ino_t nodeid) +{ + struct node *node = get_node_nocheck(f, nodeid); + if (!node) { + fprintf(stderr, "fuse internal error: node %llu not found\n", + (unsigned long long) nodeid); + abort(); + } + return node; +} + +static void free_node(struct node *node) +{ + free(node->name); + free(node); +} + +static void unhash_id(struct fuse *f, struct node *node) +{ + size_t hash = node->nodeid % f->id_table_size; + struct node **nodep = &f->id_table[hash]; + + for (; *nodep != NULL; nodep = &(*nodep)->id_next) + if (*nodep == node) { + *nodep = node->id_next; + return; + } +} + +static void hash_id(struct fuse *f, struct node *node) +{ + size_t hash = node->nodeid % f->id_table_size; + node->id_next = f->id_table[hash]; + f->id_table[hash] = node; +} + +static unsigned int name_hash(struct fuse *f, fuse_ino_t parent, + const char *name) +{ + unsigned int hash = *name; + + if (hash) + for (name += 1; *name != '\0'; name++) + hash = (hash << 5) - hash + *name; + + return (hash + parent) % f->name_table_size; +} + +static void unref_node(struct fuse *f, struct node *node); + +static void unhash_name(struct fuse *f, struct node *node) +{ + if (node->name) { + size_t hash = name_hash(f, node->parent->nodeid, node->name); + struct node **nodep = &f->name_table[hash]; + + for (; *nodep != NULL; nodep = &(*nodep)->name_next) + if (*nodep == node) { + *nodep = node->name_next; + node->name_next = NULL; + unref_node(f, node->parent); + free(node->name); + node->name = NULL; + node->parent = NULL; + return; + } + fprintf(stderr, "fuse internal error: unable to unhash node: %llu\n", + (unsigned long long) node->nodeid); + abort(); + } +} + +static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid, + const char *name) +{ + size_t hash = name_hash(f, parentid, name); + struct node *parent = get_node(f, parentid); + node->name = strdup(name); + if (node->name == NULL) + return -1; + + parent->refctr ++; + node->parent = parent; + node->name_next = f->name_table[hash]; + f->name_table[hash] = node; + return 0; +} + +static void delete_node(struct fuse *f, struct node *node) +{ + if (f->conf.debug) + fprintf(stderr, "delete: %llu\n", (unsigned long long) node->nodeid); + + assert(!node->name); + unhash_id(f, node); + free_node(node); +} + +static void unref_node(struct fuse *f, struct node *node) +{ + assert(node->refctr > 0); + node->refctr --; + if (!node->refctr) + delete_node(f, node); +} + +static fuse_ino_t next_id(struct fuse *f) +{ + do { + f->ctr = (f->ctr + 1) & 0xffffffff; + if (!f->ctr) + f->generation ++; + } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO || + get_node_nocheck(f, f->ctr) != NULL); + return f->ctr; +} + +static struct node *lookup_node(struct fuse *f, fuse_ino_t parent, + const char *name) +{ + size_t hash = name_hash(f, parent, name); + struct node *node; + + for (node = f->name_table[hash]; node != NULL; node = node->name_next) + if (node->parent->nodeid == parent && strcmp(node->name, name) == 0) + return node; + + return NULL; +} + +static struct node *find_node(struct fuse *f, fuse_ino_t parent, + const char *name) +{ + struct node *node; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, parent, name); + if (node == NULL) { + node = (struct node *) calloc(1, sizeof(struct node)); + if (node == NULL) + goto out_err; + + node->refctr = 1; + node->nodeid = next_id(f); + node->open_count = 0; + node->is_hidden = 0; + node->generation = f->generation; + if (hash_name(f, node, parent, name) == -1) { + free(node); + node = NULL; + goto out_err; + } + hash_id(f, node); + } + node->nlookup ++; + out_err: + pthread_mutex_unlock(&f->lock); + return node; +} + +static char *add_name(char *buf, char *s, const char *name) +{ + size_t len = strlen(name); + s -= len; + if (s <= buf) { + fprintf(stderr, "fuse: path too long: ...%s\n", s + len); + return NULL; + } + strncpy(s, name, len); + s--; + *s = '/'; + + return s; +} + +static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name) +{ + char buf[FUSE_MAX_PATH]; + char *s = buf + FUSE_MAX_PATH - 1; + struct node *node; + + *s = '\0'; + + if (name != NULL) { + s = add_name(buf, s, name); + if (s == NULL) + return NULL; + } + + pthread_mutex_lock(&f->lock); + for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID; + node = node->parent) { + if (node->name == NULL) { + s = NULL; + break; + } + + s = add_name(buf, s, node->name); + if (s == NULL) + break; + } + pthread_mutex_unlock(&f->lock); + + if (node == NULL || s == NULL) + return NULL; + else if (*s == '\0') + return strdup("/"); + else + return strdup(s); +} + +static char *get_path(struct fuse *f, fuse_ino_t nodeid) +{ + return get_path_name(f, nodeid, NULL); +} + +static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup) +{ + struct node *node; + if (nodeid == FUSE_ROOT_ID) + return; + pthread_mutex_lock(&f->lock); + node = get_node(f, nodeid); + assert(node->nlookup >= nlookup); + node->nlookup -= nlookup; + if (!node->nlookup) { + unhash_name(f, node); + unref_node(f, node); + } + pthread_mutex_unlock(&f->lock); +} + +static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name) +{ + struct node *node; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, name); + if (node != NULL) + unhash_name(f, node); + pthread_mutex_unlock(&f->lock); +} + +static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname, + fuse_ino_t newdir, const char *newname, int hide) +{ + struct node *node; + struct node *newnode; + int err = 0; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, olddir, oldname); + newnode = lookup_node(f, newdir, newname); + if (node == NULL) + goto out; + + if (newnode != NULL) { + if (hide) { + fprintf(stderr, "fuse: hidden file got created during hiding\n"); + err = -EBUSY; + goto out; + } + unhash_name(f, newnode); + } + + unhash_name(f, node); + if (hash_name(f, node, newdir, newname) == -1) { + err = -ENOMEM; + goto out; + } + + if (hide) + node->is_hidden = 1; + + out: + pthread_mutex_unlock(&f->lock); + return err; +} + +static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf) +{ + if (!f->conf.use_ino) + stbuf->st_ino = nodeid; + if (f->conf.set_mode) + stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.umask); + if (f->conf.set_uid) + stbuf->st_uid = f->conf.uid; + if (f->conf.set_gid) + stbuf->st_gid = f->conf.gid; +} + +static struct fuse *req_fuse(fuse_req_t req) +{ + return (struct fuse *) fuse_req_userdata(req); +} + +static void fuse_intr_sighandler(int sig) +{ + (void) sig; + /* Nothing to do */ +} + +struct fuse_intr_data { + pthread_t id; + pthread_cond_t cond; + int finished; +}; + +static void fuse_interrupt(fuse_req_t req, void *d_) +{ + struct fuse_intr_data *d = d_; + struct fuse *f = req_fuse(req); + + if (d->id == pthread_self()) + return; + + pthread_mutex_lock(&f->lock); + while (!d->finished) { + struct timeval now; + struct timespec timeout; + + pthread_kill(d->id, f->conf.intr_signal); + gettimeofday(&now, NULL); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + pthread_cond_timedwait(&d->cond, &f->lock, &timeout); + } + pthread_mutex_unlock(&f->lock); +} + +static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + pthread_mutex_lock(&f->lock); + d->finished = 1; + pthread_cond_broadcast(&d->cond); + pthread_mutex_unlock(&f->lock); + fuse_req_interrupt_func(req, NULL, NULL); + pthread_cond_destroy(&d->cond); +} + +static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d) +{ + d->id = pthread_self(); + pthread_cond_init(&d->cond, NULL); + d->finished = 0; + fuse_req_interrupt_func(req, fuse_interrupt, d); +} + +static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + if (f->conf.intr) + fuse_do_finish_interrupt(f, req, d); +} + +static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + if (f->conf.intr) + fuse_do_prepare_interrupt(req, d); +} + +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.getattr) + return fs->op.getattr(path, buf); + else + return -ENOSYS; +} + +int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fgetattr) + return fs->op.fgetattr(path, buf, fi); + else if (fs->op.getattr) + return fs->op.getattr(path, buf); + else + return -ENOSYS; +} + +int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, + const char *newpath) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.rename) + return fs->op.rename(oldpath, newpath); + else + return -ENOSYS; +} + +int fuse_fs_unlink(struct fuse_fs *fs, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.unlink) + return fs->op.unlink(path); + else + return -ENOSYS; +} + +int fuse_fs_rmdir(struct fuse_fs *fs, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.rmdir) + return fs->op.rmdir(path); + else + return -ENOSYS; +} + +int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.symlink) + return fs->op.symlink(linkname, path); + else + return -ENOSYS; +} + +int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.link) + return fs->op.link(oldpath, newpath); + else + return -ENOSYS; +} + +int fuse_fs_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.release) + return fs->op.release(path, fi); + else + return 0; +} + +int fuse_fs_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.opendir) + return fs->op.opendir(path, fi); + else + return 0; +} + +int fuse_fs_open(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.open) + return fs->op.open(path, fi); + else + return 0; +} + +int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, + off_t off, struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.read) + return fs->op.read(path, buf, size, off, fi); + else + return -ENOSYS; +} + +int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.write) + return fs->op.write(path, buf, size, off, fi); + else + return -ENOSYS; +} + +int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fsync) + return fs->op.fsync(path, datasync, fi); + else + return -ENOSYS; +} + +int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fsyncdir) + return fs->op.fsyncdir(path, datasync, fi); + else + return -ENOSYS; +} + +int fuse_fs_flush(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.flush) + return fs->op.flush(path, fi); + else + return -ENOSYS; +} + +int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.statfs) + return fs->op.statfs(path, buf); + else { + buf->f_namemax = 255; + buf->f_bsize = 512; + return 0; + } +} + +int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.releasedir) + return fs->op.releasedir(path, fi); + else + return 0; +} + +int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, + fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.readdir) + return fs->op.readdir(path, buf, filler, off, fi); + else + return -ENOSYS; +} + +int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.create) + return fs->op.create(path, mode, fi); + else + return -ENOSYS; +} + +int fuse_fs_lock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int cmd, struct flock *lock) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.lock) + return fs->op.lock(path, fi, cmd, lock); + else + return -ENOSYS; +} + +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.chown) + return fs->op.chown(path, uid, gid); + else + return -ENOSYS; +} + +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.truncate) + return fs->op.truncate(path, size); + else + return -ENOSYS; +} + +int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.ftruncate) + return fs->op.ftruncate(path, size, fi); + else if (fs->op.truncate) + return fs->op.truncate(path, size); + else + return -ENOSYS; +} + +int fuse_fs_utimens(struct fuse_fs *fs, const char *path, + const struct timespec tv[2]) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.utimens) + return fs->op.utimens(path, tv); + else if(fs->op.utime) { + struct utimbuf buf; + buf.actime = tv[0].tv_sec; + buf.modtime = tv[1].tv_sec; + return fs->op.utime(path, &buf); + } else + return -ENOSYS; +} + +int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.access) + return fs->op.access(path, mask); + else + return -ENOSYS; +} + +int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, + size_t len) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.readlink) + return fs->op.readlink(path, buf, len); + else + return -ENOSYS; +} + +int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, + dev_t rdev) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.mknod) + return fs->op.mknod(path, mode, rdev); + else + return -ENOSYS; +} + +int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.mkdir) + return fs->op.mkdir(path, mode); + else + return -ENOSYS; +} + +int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, + const char *value, size_t size, int flags) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.setxattr) + return fs->op.setxattr(path, name, value, size, flags); + else + return -ENOSYS; +} + +int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, + char *value, size_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.getxattr) + return fs->op.getxattr(path, name, value, size); + else + return -ENOSYS; +} + +int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, + size_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.listxattr) + return fs->op.listxattr(path, list, size); + else + return -ENOSYS; +} + +int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, + uint64_t *idx) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.bmap) + return fs->op.bmap(path, blocksize, idx); + else + return -ENOSYS; +} + +int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.removexattr) + return fs->op.removexattr(path, name); + else + return -ENOSYS; +} + +static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) +{ + struct node *node; + int isopen = 0; + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, name); + if (node && node->open_count > 0) + isopen = 1; + pthread_mutex_unlock(&f->lock); + return isopen; +} + +static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, + char *newname, size_t bufsize) +{ + struct stat buf; + struct node *node; + struct node *newnode; + char *newpath; + int res; + int failctr = 10; + + do { + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, oldname); + if (node == NULL) { + pthread_mutex_unlock(&f->lock); + return NULL; + } + do { + f->hidectr ++; + snprintf(newname, bufsize, ".fuse_hidden%08x%08x", + (unsigned int) node->nodeid, f->hidectr); + newnode = lookup_node(f, dir, newname); + } while(newnode); + pthread_mutex_unlock(&f->lock); + + newpath = get_path_name(f, dir, newname); + if (!newpath) + break; + + res = fuse_fs_getattr(f->fs, newpath, &buf); + if (res == -ENOENT) + break; + free(newpath); + newpath = NULL; + } while(res == 0 && --failctr); + + return newpath; +} + +static int hide_node(struct fuse *f, const char *oldpath, + fuse_ino_t dir, const char *oldname) +{ + char newname[64]; + char *newpath; + int err = -EBUSY; + + newpath = hidden_name(f, dir, oldname, newname, sizeof(newname)); + if (newpath) { + err = fuse_fs_rename(f->fs, oldpath, newpath); + if (!err) + err = rename_node(f, dir, oldname, dir, newname, 1); + free(newpath); + } + return err; +} + +static int mtime_eq(const struct stat *stbuf, const struct timespec *ts) +{ + return stbuf->st_mtime == ts->tv_sec && ST_MTIM_NSEC(stbuf) == ts->tv_nsec; +} + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +static void curr_time(struct timespec *now) +{ + static clockid_t clockid = CLOCK_MONOTONIC; + int res = clock_gettime(clockid, now); + if (res == -1 && errno == EINVAL) { + clockid = CLOCK_REALTIME; + res = clock_gettime(clockid, now); + } + if (res == -1) { + perror("fuse: clock_gettime"); + abort(); + } +} + +static void update_stat(struct node *node, const struct stat *stbuf) +{ + if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) || + stbuf->st_size != node->size)) + node->cache_valid = 0; + node->mtime.tv_sec = stbuf->st_mtime; + node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf); + node->size = stbuf->st_size; + curr_time(&node->stat_updated); +} + +static int lookup_path(struct fuse *f, fuse_ino_t nodeid, + const char *name, const char *path, + struct fuse_entry_param *e, struct fuse_file_info *fi) +{ + int res; + + memset(e, 0, sizeof(struct fuse_entry_param)); + if (fi) + res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi); + else + res = fuse_fs_getattr(f->fs, path, &e->attr); + if (res == 0) { + struct node *node; + + node = find_node(f, nodeid, name); + if (node == NULL) + res = -ENOMEM; + else { + e->ino = node->nodeid; + e->generation = node->generation; + e->entry_timeout = f->conf.entry_timeout; + e->attr_timeout = f->conf.attr_timeout; + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(node, &e->attr); + pthread_mutex_unlock(&f->lock); + } + set_stat(f, e->ino, &e->attr); + if (f->conf.debug) + fprintf(stderr, " NODEID: %lu\n", (unsigned long) e->ino); + } + } + return res; +} + +static struct fuse_context_i *fuse_get_context_internal(void) +{ + struct fuse_context_i *c; + + c = (struct fuse_context_i *) pthread_getspecific(fuse_context_key); + if (c == NULL) { + c = (struct fuse_context_i *) malloc(sizeof(struct fuse_context_i)); + if (c == NULL) { + /* This is hard to deal with properly, so just abort. If + memory is so low that the context cannot be allocated, + there's not much hope for the filesystem anyway */ + fprintf(stderr, "fuse: failed to allocate thread specific data\n"); + abort(); + } + pthread_setspecific(fuse_context_key, c); + } + return c; +} + +static void fuse_freecontext(void *data) +{ + free(data); +} + +static int fuse_create_context_key(void) +{ + int err = 0; + pthread_mutex_lock(&fuse_context_lock); + if (!fuse_context_ref) { + err = pthread_key_create(&fuse_context_key, fuse_freecontext); + if (err) { + fprintf(stderr, "fuse: failed to create thread specific key: %s\n", + strerror(err)); + pthread_mutex_unlock(&fuse_context_lock); + return -1; + } + } + fuse_context_ref++; + pthread_mutex_unlock(&fuse_context_lock); + return 0; +} + +static void fuse_delete_context_key(void) +{ + pthread_mutex_lock(&fuse_context_lock); + fuse_context_ref--; + if (!fuse_context_ref) { + free(pthread_getspecific(fuse_context_key)); + pthread_key_delete(fuse_context_key); + } + pthread_mutex_unlock(&fuse_context_lock); +} + +static struct fuse *req_fuse_prepare(fuse_req_t req) +{ + struct fuse_context_i *c = fuse_get_context_internal(); + const struct fuse_ctx *ctx = fuse_req_ctx(req); + c->req = req; + c->ctx.fuse = req_fuse(req); + c->ctx.uid = ctx->uid; + c->ctx.gid = ctx->gid; + c->ctx.pid = ctx->pid; + return c->ctx.fuse; +} + +static inline void reply_err(fuse_req_t req, int err) +{ + /* fuse_reply_err() uses non-negated errno values */ + fuse_reply_err(req, -err); +} + +static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e, + int err) +{ + if (!err) { + struct fuse *f = req_fuse(req); + if (fuse_reply_entry(req, e) == -ENOENT) + forget_node(f, e->ino, 1); + } else + reply_err(req, err); +} + +void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.init) + fs->user_data = fs->op.init(conn); +} + +static void fuse_lib_init(void *data, struct fuse_conn_info *conn) +{ + struct fuse *f = (struct fuse *) data; + struct fuse_context_i *c = fuse_get_context_internal(); + + memset(c, 0, sizeof(*c)); + c->ctx.fuse = f; + fuse_fs_init(f->fs, conn); +} + +void fuse_fs_destroy(struct fuse_fs *fs) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.destroy) + fs->op.destroy(fs->user_data); + free(fs); +} + +static void fuse_lib_destroy(void *data) +{ + struct fuse *f = (struct fuse *) data; + struct fuse_context_i *c = fuse_get_context_internal(); + + memset(c, 0, sizeof(*c)); + c->ctx.fuse = f; + fuse_fs_destroy(f->fs); + f->fs = NULL; +} + +static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "LOOKUP %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = lookup_path(f, parent, name, path, &e, NULL); + if (err == -ENOENT && f->conf.negative_timeout != 0.0) { + e.ino = 0; + e.entry_timeout = f->conf.negative_timeout; + err = 0; + } + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, + unsigned long nlookup) +{ + struct fuse *f = req_fuse(req); + if (f->conf.debug) + fprintf(stderr, "FORGET %llu/%lu\n", (unsigned long long)ino, nlookup); + forget_node(f, ino, nlookup); + fuse_reply_none(req); +} + +static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct stat buf; + char *path; + int err; + + (void) fi; + memset(&buf, 0, sizeof(buf)); + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_getattr(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) { + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(get_node(f, ino), &buf); + pthread_mutex_unlock(&f->lock); + } + set_stat(f, ino, &buf); + fuse_reply_attr(req, &buf, f->conf.attr_timeout); + } else + reply_err(req, err); +} + +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.chmod) + return fs->op.chmod(path, mode); + else + return -ENOSYS; +} + +static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int valid, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct stat buf; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = 0; + if (!err && (valid & FUSE_SET_ATTR_MODE)) + err = fuse_fs_chmod(f->fs, path, attr->st_mode); + if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) { + uid_t uid = + (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; + gid_t gid = + (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; + err = fuse_fs_chown(f->fs, path, uid, gid); + } + if (!err && (valid & FUSE_SET_ATTR_SIZE)) { + if (fi) + err = fuse_fs_ftruncate(f->fs, path, attr->st_size, fi); + else + err = fuse_fs_truncate(f->fs, path, attr->st_size); + } + if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == + (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { + struct timespec tv[2]; + tv[0].tv_sec = attr->st_atime; + tv[0].tv_nsec = ST_ATIM_NSEC(attr); + tv[1].tv_sec = attr->st_mtime; + tv[1].tv_nsec = ST_MTIM_NSEC(attr); + err = fuse_fs_utimens(f->fs, path, tv); + } + if (!err) + err = fuse_fs_getattr(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) { + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(get_node(f, ino), &buf); + pthread_mutex_unlock(&f->lock); + } + set_stat(f, ino, &buf); + fuse_reply_attr(req, &buf, f->conf.attr_timeout); + } else + reply_err(req, err); +} + +static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "ACCESS %s 0%o\n", path, mask); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_access(f->fs, path, mask); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino) +{ + struct fuse *f = req_fuse_prepare(req); + char linkname[PATH_MAX + 1]; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname)); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) { + linkname[PATH_MAX] = '\0'; + fuse_reply_readlink(req, linkname); + } else + reply_err(req, err); +} + +static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "MKNOD %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = -ENOSYS; + if (S_ISREG(mode)) { + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = O_CREAT | O_EXCL | O_WRONLY; + err = fuse_fs_create(f->fs, path, mode, &fi); + if (!err) { + err = lookup_path(f, parent, name, path, &e, &fi); + fuse_fs_release(f->fs, path, &fi); + } + } + if (err == -ENOSYS) { + err = fuse_fs_mknod(f->fs, path, mode, rdev); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + } + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "MKDIR %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_mkdir(f->fs, path, mode); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_wrlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "UNLINK %s\n", path); + fuse_prepare_interrupt(f, req, &d); + if (!f->conf.hard_remove && is_open(f, parent, name)) + err = hide_node(f, path, parent, name); + else { + err = fuse_fs_unlink(f->fs, path); + if (!err) + remove_node(f, parent, name); + } + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_wrlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "RMDIR %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_rmdir(f->fs, path); + fuse_finish_interrupt(f, req, &d); + if (!err) + remove_node(f, parent, name); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_symlink(fuse_req_t req, const char *linkname, + fuse_ino_t parent, const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "SYMLINK %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_symlink(f->fs, linkname, path); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir, + const char *oldname, fuse_ino_t newdir, + const char *newname) +{ + struct fuse *f = req_fuse_prepare(req); + char *oldpath; + char *newpath; + int err; + + err = -ENOENT; + pthread_rwlock_wrlock(&f->tree_lock); + oldpath = get_path_name(f, olddir, oldname); + if (oldpath != NULL) { + newpath = get_path_name(f, newdir, newname); + if (newpath != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "RENAME %s -> %s\n", oldpath, newpath); + err = 0; + fuse_prepare_interrupt(f, req, &d); + if (!f->conf.hard_remove && is_open(f, newdir, newname)) + err = hide_node(f, newpath, newdir, newname); + if (!err) { + err = fuse_fs_rename(f->fs, oldpath, newpath); + if (!err) + err = rename_node(f, olddir, oldname, newdir, newname, 0); + } + fuse_finish_interrupt(f, req, &d); + free(newpath); + } + free(oldpath); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *oldpath; + char *newpath; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + oldpath = get_path(f, ino); + if (oldpath != NULL) { + newpath = get_path_name(f, newparent, newname); + if (newpath != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "LINK %s\n", newpath); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_link(f->fs, oldpath, newpath); + if (!err) + err = lookup_path(f, newparent, newname, newpath, &e, NULL); + fuse_finish_interrupt(f, req, &d); + free(newpath); + } + free(oldpath); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path, + struct fuse_file_info *fi) +{ + struct node *node; + int unlink_hidden = 0; + + fuse_fs_release(f->fs, path ? path : "-", fi); + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); + assert(node->open_count > 0); + --node->open_count; + if (node->is_hidden && !node->open_count) { + unlink_hidden = 1; + node->is_hidden = 0; + } + pthread_mutex_unlock(&f->lock); + + if(unlink_hidden && path) + fuse_fs_unlink(f->fs, path); +} + +static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_create(f->fs, path, mode, fi); + if (!err) { + err = lookup_path(f, parent, name, path, &e, fi); + if (err) + fuse_fs_release(f->fs, path, fi); + else if (!S_ISREG(e.attr.st_mode)) { + err = -EIO; + fuse_fs_release(f->fs, path, fi); + forget_node(f, e.ino, 1); + } else { + if (f->conf.direct_io) + fi->direct_io = 1; + if (f->conf.kernel_cache) + fi->keep_cache = 1; + + } + } + fuse_finish_interrupt(f, req, &d); + } + if (!err) { + pthread_mutex_lock(&f->lock); + get_node(f, e.ino)->open_count++; + pthread_mutex_unlock(&f->lock); + if (fuse_reply_create(req, &e, fi) == -ENOENT) { + /* The open syscall was interrupted, so it must be cancelled */ + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, e.ino, path, fi); + fuse_finish_interrupt(f, req, &d); + forget_node(f, e.ino, 1); + } else if (f->conf.debug) { + fprintf(stderr, " CREATE[%llu] flags: 0x%x %s\n", + (unsigned long long) fi->fh, fi->flags, path); + } + } else + reply_err(req, err); + + if (path) + free(path); + + pthread_rwlock_unlock(&f->tree_lock); +} + +static double diff_timespec(const struct timespec *t1, + const struct timespec *t2) +{ + return (t1->tv_sec - t2->tv_sec) + + ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0; +} + +static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, + struct fuse_file_info *fi) +{ + struct node *node; + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); + if (node->cache_valid) { + struct timespec now; + + curr_time(&now); + if (diff_timespec(&now, &node->stat_updated) > f->conf.ac_attr_timeout) { + struct stat stbuf; + int err; + pthread_mutex_unlock(&f->lock); + err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi); + pthread_mutex_lock(&f->lock); + if (!err) + update_stat(node, &stbuf); + else + node->cache_valid = 0; + } + } + if (node->cache_valid) + fi->keep_cache = 1; + + node->cache_valid = 1; + pthread_mutex_unlock(&f->lock); +} + +static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path = NULL; + int err = 0; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_open(f->fs, path, fi); + if (!err) { + if (f->conf.direct_io) + fi->direct_io = 1; + if (f->conf.kernel_cache) + fi->keep_cache = 1; + + if (f->conf.auto_cache) + open_auto_cache(f, ino, path, fi); + } + fuse_finish_interrupt(f, req, &d); + } + if (!err) { + pthread_mutex_lock(&f->lock); + get_node(f, ino)->open_count++; + pthread_mutex_unlock(&f->lock); + if (fuse_reply_open(req, fi) == -ENOENT) { + /* The open syscall was interrupted, so it must be cancelled */ + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, ino, path, fi); + fuse_finish_interrupt(f, req, &d); + } else if (f->conf.debug) { + fprintf(stderr, "OPEN[%llu] flags: 0x%x %s\n", + (unsigned long long) fi->fh, fi->flags, path); + } + } else + reply_err(req, err); + + if (path) + free(path); + pthread_rwlock_unlock(&f->tree_lock); +} + +static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + char *buf; + int res; + + buf = (char *) malloc(size); + if (buf == NULL) { + reply_err(req, -ENOMEM); + return; + } + + res = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "READ[%llu] %lu bytes from %llu\n", + (unsigned long long) fi->fh, (unsigned long) size, + (unsigned long long) off); + + fuse_prepare_interrupt(f, req, &d); + res = fuse_fs_read(f->fs, path, buf, size, off, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + + if (res >= 0) { + if (f->conf.debug) + fprintf(stderr, " READ[%llu] %u bytes\n", + (unsigned long long)fi->fh, res); + if ((size_t) res > size) + fprintf(stderr, "fuse: read too many bytes"); + fuse_reply_buf(req, buf, res); + } else + reply_err(req, res); + + free(buf); +} + +static void fuse_lib_write(fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int res; + + res = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "WRITE%s[%llu] %lu bytes to %llu\n", + fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh, + (unsigned long) size, (unsigned long long) off); + + fuse_prepare_interrupt(f, req, &d); + res = fuse_fs_write(f->fs, path, buf, size, off, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + + if (res >= 0) { + if (f->conf.debug) + fprintf(stderr, " WRITE%s[%llu] %u bytes\n", + fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh, + res); + if ((size_t) res > size) + fprintf(stderr, "fuse: wrote too many bytes"); + fuse_reply_write(req, res); + } else + reply_err(req, res); +} + +static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "FSYNC[%llu]\n", (unsigned long long) fi->fh); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fsync(f->fs, path, datasync, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi, + struct fuse_file_info *fi) +{ + struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh; + memset(fi, 0, sizeof(struct fuse_file_info)); + fi->fh = dh->fh; + fi->fh_old = dh->fh; + return dh; +} + +static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_dh *dh; + struct fuse_file_info fi; + char *path; + int err; + + dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh)); + if (dh == NULL) { + reply_err(req, -ENOMEM); + return; + } + memset(dh, 0, sizeof(struct fuse_dh)); + dh->fuse = f; + dh->contents = NULL; + dh->len = 0; + dh->filled = 0; + dh->nodeid = ino; + fuse_mutex_init(&dh->lock); + + llfi->fh = (uintptr_t) dh; + + memset(&fi, 0, sizeof(fi)); + fi.flags = llfi->flags; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_opendir(f->fs, path, &fi); + fuse_finish_interrupt(f, req, &d); + dh->fh = fi.fh; + } + if (!err) { + if (fuse_reply_open(req, llfi) == -ENOENT) { + /* The opendir syscall was interrupted, so it must be cancelled */ + fuse_prepare_interrupt(f, req, &d); + fuse_fs_releasedir(f->fs, path, &fi); + fuse_finish_interrupt(f, req, &d); + pthread_mutex_destroy(&dh->lock); + free(dh); + } + } else { + reply_err(req, err); + free(dh); + } + free(path); + pthread_rwlock_unlock(&f->tree_lock); +} + +static int extend_contents(struct fuse_dh *dh, unsigned minsize) +{ + if (minsize > dh->size) { + char *newptr; + unsigned newsize = dh->size; + if (!newsize) + newsize = 1024; + while (newsize < minsize) + newsize *= 2; + + newptr = (char *) realloc(dh->contents, newsize); + if (!newptr) { + dh->error = -ENOMEM; + return -1; + } + dh->contents = newptr; + dh->size = newsize; + } + return 0; +} + +static int fill_dir(void *dh_, const char *name, const struct stat *statp, + off_t off) +{ + struct fuse_dh *dh = (struct fuse_dh *) dh_; + struct stat stbuf; + size_t newlen; + + if (statp) + stbuf = *statp; + else { + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_ino = FUSE_UNKNOWN_INO; + } + + if (!dh->fuse->conf.use_ino) { + stbuf.st_ino = FUSE_UNKNOWN_INO; + if (dh->fuse->conf.readdir_ino) { + struct node *node; + pthread_mutex_lock(&dh->fuse->lock); + node = lookup_node(dh->fuse, dh->nodeid, name); + if (node) + stbuf.st_ino = (ino_t) node->nodeid; + pthread_mutex_unlock(&dh->fuse->lock); + } + } + + if (off) { + if (extend_contents(dh, dh->needlen) == -1) + return 1; + + dh->filled = 0; + newlen = dh->len + fuse_add_direntry(dh->req, dh->contents + dh->len, + dh->needlen - dh->len, name, + &stbuf, off); + if (newlen > dh->needlen) + return 1; + } else { + newlen = dh->len + fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0); + if (extend_contents(dh, newlen) == -1) + return 1; + + fuse_add_direntry(dh->req, dh->contents + dh->len, dh->size - dh->len, + name, &stbuf, newlen); + } + dh->len = newlen; + return 0; +} + +static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + size_t size, off_t off, struct fuse_dh *dh, + struct fuse_file_info *fi) +{ + int err = -ENOENT; + char *path; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + + dh->len = 0; + dh->error = 0; + dh->needlen = size; + dh->filled = 1; + dh->req = req; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi); + fuse_finish_interrupt(f, req, &d); + dh->req = NULL; + if (!err) + err = dh->error; + if (err) + dh->filled = 0; + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_file_info fi; + struct fuse_dh *dh = get_dirhandle(llfi, &fi); + + pthread_mutex_lock(&dh->lock); + /* According to SUS, directory contents need to be refreshed on + rewinddir() */ + if (!off) + dh->filled = 0; + + if (!dh->filled) { + int err = readdir_fill(f, req, ino, size, off, dh, &fi); + if (err) { + reply_err(req, err); + goto out; + } + } + if (dh->filled) { + if (off < dh->len) { + if (off + size > dh->len) + size = dh->len - off; + } else + size = 0; + } else { + size = dh->len; + off = 0; + } + fuse_reply_buf(req, dh->contents + off, size); + out: + pthread_mutex_unlock(&dh->lock); +} + +static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_file_info fi; + struct fuse_dh *dh = get_dirhandle(llfi, &fi); + char *path; + + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + fuse_prepare_interrupt(f, req, &d); + fuse_fs_releasedir(f->fs, path ? path : "-", &fi); + fuse_finish_interrupt(f, req, &d); + if (path) + free(path); + pthread_rwlock_unlock(&f->tree_lock); + pthread_mutex_lock(&dh->lock); + pthread_mutex_unlock(&dh->lock); + pthread_mutex_destroy(&dh->lock); + free(dh->contents); + free(dh); + reply_err(req, 0); +} + +static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_file_info fi; + char *path; + int err; + + get_dirhandle(llfi, &fi); + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino) +{ + struct fuse *f = req_fuse_prepare(req); + struct statvfs buf; + char *path; + int err; + + memset(&buf, 0, sizeof(buf)); + pthread_rwlock_rdlock(&f->tree_lock); + if (!ino) { + err = -ENOMEM; + path = strdup("/"); + } else { + err = -ENOENT; + path = get_path(f, ino); + } + if (path) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_statfs(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + + if (!err) + fuse_reply_statfs(req, &buf); + else + reply_err(req, err); +} + +static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_setxattr(f->fs, path, name, value, size, flags); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + const char *name, char *value, size_t size) +{ + int err; + char *path; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_getxattr(f->fs, path, name, value, size); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) +{ + struct fuse *f = req_fuse_prepare(req); + int res; + + if (size) { + char *value = (char *) malloc(size); + if (value == NULL) { + reply_err(req, -ENOMEM); + return; + } + res = common_getxattr(f, req, ino, name, value, size); + if (res > 0) + fuse_reply_buf(req, value, res); + else + reply_err(req, res); + free(value); + } else { + res = common_getxattr(f, req, ino, name, NULL, 0); + if (res >= 0) + fuse_reply_xattr(req, res); + else + reply_err(req, res); + } +} + +static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + char *list, size_t size) +{ + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_listxattr(f->fs, path, list, size); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) +{ + struct fuse *f = req_fuse_prepare(req); + int res; + + if (size) { + char *list = (char *) malloc(size); + if (list == NULL) { + reply_err(req, -ENOMEM); + return; + } + res = common_listxattr(f, req, ino, list, size); + if (res > 0) + fuse_reply_buf(req, list, res); + else + reply_err(req, res); + free(list); + } else { + res = common_listxattr(f, req, ino, NULL, 0); + if (res >= 0) + fuse_reply_xattr(req, res); + else + reply_err(req, res); + } +} + +static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino, + const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_removexattr(f->fs, path, name); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static struct lock *locks_conflict(struct node *node, const struct lock *lock) +{ + struct lock *l; + + for (l = node->locks; l; l = l->next) + if (l->owner != lock->owner && + lock->start <= l->end && l->start <= lock->end && + (l->type == F_WRLCK || lock->type == F_WRLCK)) + break; + + return l; +} + +static void delete_lock(struct lock **lockp) +{ + struct lock *l = *lockp; + *lockp = l->next; + free(l); +} + +static void insert_lock(struct lock **pos, struct lock *lock) +{ + lock->next = *pos; + *pos = lock; +} + +static int locks_insert(struct node *node, struct lock *lock) +{ + struct lock **lp; + struct lock *newl1 = NULL; + struct lock *newl2 = NULL; + + if (lock->type != F_UNLCK || lock->start != 0 || lock->end != OFFSET_MAX) { + newl1 = malloc(sizeof(struct lock)); + newl2 = malloc(sizeof(struct lock)); + + if (!newl1 || !newl2) { + free(newl1); + free(newl2); + return -ENOLCK; + } + } + + for (lp = &node->locks; *lp;) { + struct lock *l = *lp; + if (l->owner != lock->owner) + goto skip; + + if (lock->type == l->type) { + if (l->end < lock->start - 1) + goto skip; + if (lock->end < l->start - 1) + break; + if (l->start <= lock->start && lock->end <= l->end) + goto out; + if (l->start < lock->start) + lock->start = l->start; + if (lock->end < l->end) + lock->end = l->end; + goto delete; + } else { + if (l->end < lock->start) + goto skip; + if (lock->end < l->start) + break; + if (lock->start <= l->start && l->end <= lock->end) + goto delete; + if (l->end <= lock->end) { + l->end = lock->start - 1; + goto skip; + } + if (lock->start <= l->start) { + l->start = lock->end + 1; + break; + } + *newl2 = *l; + newl2->start = lock->end + 1; + l->end = lock->start - 1; + insert_lock(&l->next, newl2); + newl2 = NULL; + } + skip: + lp = &l->next; + continue; + + delete: + delete_lock(lp); + } + if (lock->type != F_UNLCK) { + *newl1 = *lock; + insert_lock(lp, newl1); + newl1 = NULL; + } +out: + free(newl1); + free(newl2); + return 0; +} + +static void flock_to_lock(struct flock *flock, struct lock *lock) +{ + memset(lock, 0, sizeof(struct lock)); + lock->type = flock->l_type; + lock->start = flock->l_start; + lock->end = flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX; + lock->pid = flock->l_pid; +} + +static void lock_to_flock(struct lock *lock, struct flock *flock) +{ + flock->l_type = lock->type; + flock->l_start = lock->start; + flock->l_len = (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1; + flock->l_pid = lock->pid; +} + +static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + const char *path, struct fuse_file_info *fi) +{ + struct fuse_intr_data d; + struct flock lock; + struct lock l; + int err; + int errlock; + + fuse_prepare_interrupt(f, req, &d); + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + err = fuse_fs_flush(f->fs, path, fi); + errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock); + fuse_finish_interrupt(f, req, &d); + + if (errlock != -ENOSYS) { + flock_to_lock(&lock, &l); + l.owner = fi->lock_owner; + pthread_mutex_lock(&f->lock); + locks_insert(get_node(f, ino), &l); + pthread_mutex_unlock(&f->lock); + + /* if op.lock() is defined FLUSH is needed regardless of op.flush() */ + if (err == -ENOSYS) + err = 0; + } + return err; +} + +static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path; + int err = 0; + + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (f->conf.debug) + fprintf(stderr, "RELEASE%s[%llu] flags: 0x%x\n", + fi->flush ? "+FLUSH" : "", + (unsigned long long) fi->fh, fi->flags); + + if (fi->flush) { + err = fuse_flush_common(f, req, ino, path, fi); + if (err == -ENOSYS) + err = 0; + } + + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, ino, path, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + pthread_rwlock_unlock(&f->tree_lock); + + reply_err(req, err); +} + +static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path && f->conf.debug) + fprintf(stderr, "FLUSH[%llu]\n", (unsigned long long) fi->fh); + err = fuse_flush_common(f, req, ino, path, fi); + free(path); + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock, + int cmd) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_lock(f->fs, path, fi, cmd, lock); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock) +{ + int err; + struct lock l; + struct lock *conflict; + struct fuse *f = req_fuse(req); + + flock_to_lock(lock, &l); + l.owner = fi->lock_owner; + pthread_mutex_lock(&f->lock); + conflict = locks_conflict(get_node(f, ino), &l); + if (conflict) + lock_to_flock(conflict, lock); + pthread_mutex_unlock(&f->lock); + if (!conflict) + err = fuse_lock_common(req, ino, fi, lock, F_GETLK); + else + err = 0; + + if (!err) + fuse_reply_lock(req, lock); + else + reply_err(req, err); +} + +static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock, + int sleep) +{ + int err = fuse_lock_common(req, ino, fi, lock, sleep ? F_SETLKW : F_SETLK); + if (!err) { + struct fuse *f = req_fuse(req); + struct lock l; + flock_to_lock(lock, &l); + l.owner = fi->lock_owner; + pthread_mutex_lock(&f->lock); + locks_insert(get_node(f, ino), &l); + pthread_mutex_unlock(&f->lock); + } + reply_err(req, err); +} + +static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t idx) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_bmap(f->fs, path, blocksize, &idx); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) + fuse_reply_bmap(req, idx); + else + reply_err(req, err); +} + +static struct fuse_lowlevel_ops fuse_path_ops = { + .init = fuse_lib_init, + .destroy = fuse_lib_destroy, + .lookup = fuse_lib_lookup, + .forget = fuse_lib_forget, + .getattr = fuse_lib_getattr, + .setattr = fuse_lib_setattr, + .access = fuse_lib_access, + .readlink = fuse_lib_readlink, + .mknod = fuse_lib_mknod, + .mkdir = fuse_lib_mkdir, + .unlink = fuse_lib_unlink, + .rmdir = fuse_lib_rmdir, + .symlink = fuse_lib_symlink, + .rename = fuse_lib_rename, + .link = fuse_lib_link, + .create = fuse_lib_create, + .open = fuse_lib_open, + .read = fuse_lib_read, + .write = fuse_lib_write, + .flush = fuse_lib_flush, + .release = fuse_lib_release, + .fsync = fuse_lib_fsync, + .opendir = fuse_lib_opendir, + .readdir = fuse_lib_readdir, + .releasedir = fuse_lib_releasedir, + .fsyncdir = fuse_lib_fsyncdir, + .statfs = fuse_lib_statfs, + .setxattr = fuse_lib_setxattr, + .getxattr = fuse_lib_getxattr, + .listxattr = fuse_lib_listxattr, + .removexattr = fuse_lib_removexattr, + .getlk = fuse_lib_getlk, + .setlk = fuse_lib_setlk, + .bmap = fuse_lib_bmap, +}; + +struct fuse_session *fuse_get_session(struct fuse *f) +{ + return f->se; +} + +int fuse_loop(struct fuse *f) +{ + if (f) + return fuse_session_loop(f->se); + else + return -1; +} + +void fuse_exit(struct fuse *f) +{ + fuse_session_exit(f->se); +} + +struct fuse_context *fuse_get_context(void) +{ + return &fuse_get_context_internal()->ctx; +} + +int fuse_interrupted(void) +{ + return fuse_req_interrupted(fuse_get_context_internal()->req); +} + +enum { + KEY_HELP, +}; + +#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v } + +static const struct fuse_opt fuse_lib_opts[] = { + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), + FUSE_LIB_OPT("debug", debug, 1), + FUSE_LIB_OPT("-d", debug, 1), + FUSE_LIB_OPT("hard_remove", hard_remove, 1), + FUSE_LIB_OPT("use_ino", use_ino, 1), + FUSE_LIB_OPT("readdir_ino", readdir_ino, 1), + FUSE_LIB_OPT("direct_io", direct_io, 1), + FUSE_LIB_OPT("kernel_cache", kernel_cache, 1), + FUSE_LIB_OPT("auto_cache", auto_cache, 1), + FUSE_LIB_OPT("noauto_cache", auto_cache, 0), + FUSE_LIB_OPT("umask=", set_mode, 1), + FUSE_LIB_OPT("umask=%o", umask, 0), + FUSE_LIB_OPT("uid=", set_uid, 1), + FUSE_LIB_OPT("uid=%d", uid, 0), + FUSE_LIB_OPT("gid=", set_gid, 1), + FUSE_LIB_OPT("gid=%d", gid, 0), + FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0), + FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0), + FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0), + FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1), + FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), + FUSE_LIB_OPT("intr", intr, 1), + FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), + FUSE_OPT_END +}; + +static void fuse_lib_help(void) +{ + fprintf(stderr, +" -o hard_remove immediate removal (don't hide files)\n" +" -o use_ino let filesystem set inode numbers\n" +" -o readdir_ino try to fill in d_ino in readdir\n" +" -o direct_io use direct I/O\n" +" -o kernel_cache cache files in kernel\n" +" -o [no]auto_cache enable caching based on modification times\n" +" -o umask=M set file permissions (octal)\n" +" -o uid=N set file owner\n" +" -o gid=N set file group\n" +" -o entry_timeout=T cache timeout for names (1.0s)\n" +" -o negative_timeout=T cache timeout for deleted names (0.0s)\n" +" -o attr_timeout=T cache timeout for attributes (1.0s)\n" +" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n" +" -o intr allow requests to be interrupted\n" +" -o intr_signal=NUM signal to send on interrupt (%i)\n" +"\n", FUSE_DEFAULT_INTR_SIGNAL); +} + +static int fuse_lib_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void) arg; (void) outargs; + + if (key == KEY_HELP) { + struct fuse_config *conf = (struct fuse_config *) data; + fuse_lib_help(); + conf->help = 1; + } + + return 1; +} + +static int fuse_init_intr_signal(int signum, int *installed) +{ + struct sigaction old_sa; + + if (sigaction(signum, NULL, &old_sa) == -1) { + perror("fuse: cannot get old signal handler"); + return -1; + } + + if (old_sa.sa_handler == SIG_DFL) { + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = fuse_intr_sighandler; + sigemptyset(&sa.sa_mask); + + if (sigaction(signum, &sa, NULL) == -1) { + perror("fuse: cannot set interrupt signal handler"); + return -1; + } + *installed = 1; + } + return 0; +} + +static void fuse_restore_intr_signal(int signum) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = SIG_DFL; + sigaction(signum, &sa, NULL); +} + +struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, + void *user_data) +{ + struct fuse_fs *fs; + + if (sizeof(struct fuse_operations) < op_size) { + fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n"); + op_size = sizeof(struct fuse_operations); + } + + fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs)); + if (!fs) { + fprintf(stderr, "fuse: failed to allocate fuse_fs object\n"); + return NULL; + } + + fs->user_data = user_data; + if (op) + memcpy(&fs->op, op, op_size); + return fs; +} + +struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *op, + size_t op_size, void *user_data) +{ + struct fuse *f; + struct node *root; + struct fuse_fs *fs; + struct fuse_lowlevel_ops llop = fuse_path_ops; + + if (fuse_create_context_key() == -1) + goto out; + + f = (struct fuse *) calloc(1, sizeof(struct fuse)); + if (f == NULL) { + fprintf(stderr, "fuse: failed to allocate fuse object\n"); + goto out_delete_context_key; + } + + fs = fuse_fs_new(op, op_size, user_data); + if (!fs) + goto out_free; + + f->fs = fs; + + /* Oh f**k, this is ugly! */ + if (!fs->op.lock) { + llop.getlk = NULL; + llop.setlk = NULL; + } + + f->conf.entry_timeout = 1.0; + f->conf.attr_timeout = 1.0; + f->conf.negative_timeout = 0.0; + f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL; + + if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, fuse_lib_opt_proc) == -1) + goto out_free_fs; + + if (!f->conf.ac_attr_timeout_set) + f->conf.ac_attr_timeout = f->conf.attr_timeout; + +#ifdef __FreeBSD__ + /* + * In FreeBSD, we always use these settings as inode numbers are needed to + * make getcwd(3) work. + */ + f->conf.readdir_ino = 1; +#endif + + f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f); + if (f->se == NULL) { + goto out_free_fs; + } + + fuse_session_add_chan(f->se, ch); + + f->ctr = 0; + f->generation = 0; + /* FIXME: Dynamic hash table */ + f->name_table_size = 14057; + f->name_table = (struct node **) + calloc(1, sizeof(struct node *) * f->name_table_size); + if (f->name_table == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_session; + } + + f->id_table_size = 14057; + f->id_table = (struct node **) + calloc(1, sizeof(struct node *) * f->id_table_size); + if (f->id_table == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_name_table; + } + + fuse_mutex_init(&f->lock); + pthread_rwlock_init(&f->tree_lock, NULL); + + root = (struct node *) calloc(1, sizeof(struct node)); + if (root == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_id_table; + } + + root->name = strdup("/"); + if (root->name == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_root; + } + + if (f->conf.intr && + fuse_init_intr_signal(f->conf.intr_signal, &f->intr_installed) == -1) + goto out_free_root_name; + + root->parent = NULL; + root->nodeid = FUSE_ROOT_ID; + root->generation = 0; + root->refctr = 1; + root->nlookup = 1; + hash_id(f, root); + + return f; + + out_free_root_name: + free(root->name); + out_free_root: + free(root); + out_free_id_table: + free(f->id_table); + out_free_name_table: + free(f->name_table); + out_free_session: + fuse_session_destroy(f->se); + out_free_fs: + /* Horrible compatibility hack to stop the destructor from being + called on the filesystem without init being called first */ + fs->op.destroy = NULL; + fuse_fs_destroy(f->fs); + out_free: + free(f); + out_delete_context_key: + fuse_delete_context_key(); + out: + return NULL; +} + +struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *op, size_t op_size, + void *user_data) +{ + return fuse_new_common(ch, args, op, op_size, user_data); +} + +void fuse_destroy(struct fuse *f) +{ + size_t i; + + if (f->conf.intr && f->intr_installed) + fuse_restore_intr_signal(f->conf.intr_signal); + + if (f->fs) { + struct fuse_context_i *c = fuse_get_context_internal(); + + memset(c, 0, sizeof(*c)); + c->ctx.fuse = f; + + for (i = 0; i < f->id_table_size; i++) { + struct node *node; + + for (node = f->id_table[i]; node != NULL; node = node->id_next) { + if (node->is_hidden) { + char *path = get_path(f, node->nodeid); + if (path) { + fuse_fs_unlink(f->fs, path); + free(path); + } + } + } + } + } + for (i = 0; i < f->id_table_size; i++) { + struct node *node; + struct node *next; + + for (node = f->id_table[i]; node != NULL; node = next) { + next = node->id_next; + free_node(node); + } + } + free(f->id_table); + free(f->name_table); + pthread_mutex_destroy(&f->lock); + pthread_rwlock_destroy(&f->tree_lock); + fuse_session_destroy(f->se); + free(f); + fuse_delete_context_key(); +} + diff --git a/libfuse-lite/fuse_i.h b/libfuse-lite/fuse_i.h new file mode 100644 index 00000000..6c1027db --- /dev/null +++ b/libfuse-lite/fuse_i.h @@ -0,0 +1,33 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse.h" + +struct fuse_session; +struct fuse_chan; +struct fuse_lowlevel_ops; +struct fuse_req; + +struct fuse_cmd { + char *buf; + size_t buflen; + struct fuse_chan *ch; +}; + +struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *op, + size_t op_size, void *user_data); + +struct fuse_chan *fuse_kern_chan_new(int fd); + +struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata); + +void fuse_kern_unmount(const char *mountpoint, int fd); +int fuse_kern_mount(const char *mountpoint, struct fuse_args *args); diff --git a/libfuse-lite/fuse_kern_chan.c b/libfuse-lite/fuse_kern_chan.c new file mode 100644 index 00000000..49e88b7d --- /dev/null +++ b/libfuse-lite/fuse_kern_chan.c @@ -0,0 +1,95 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse_lowlevel.h" +#include "fuse_kernel.h" +#include "fuse_i.h" + +#include +#include +#include +#include + +static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf, + size_t size) +{ + struct fuse_chan *ch = *chp; + int err; + ssize_t res; + struct fuse_session *se = fuse_chan_session(ch); + assert(se != NULL); + + restart: + res = read(fuse_chan_fd(ch), buf, size); + err = errno; + + if (fuse_session_exited(se)) + return 0; + if (res == -1) { + /* ENOENT means the operation was interrupted, it's safe + to restart */ + if (err == ENOENT) + goto restart; + + if (err == ENODEV) { + fuse_session_exit(se); + return 0; + } + /* Errors occuring during normal operation: EINTR (read + interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem + umounted) */ + if (err != EINTR && err != EAGAIN) + perror("fuse: reading device"); + return -err; + } + if ((size_t) res < sizeof(struct fuse_in_header)) { + fprintf(stderr, "short read on fuse device\n"); + return -EIO; + } + return res; +} + +static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[], + size_t count) +{ + if (iov) { + ssize_t res = writev(fuse_chan_fd(ch), iov, count); + int err = errno; + + if (res == -1) { + struct fuse_session *se = fuse_chan_session(ch); + + assert(se != NULL); + + /* ENOENT means the operation was interrupted */ + if (!fuse_session_exited(se) && err != ENOENT) + perror("fuse: writing device"); + return -err; + } + } + return 0; +} + +static void fuse_kern_chan_destroy(struct fuse_chan *ch) +{ + close(fuse_chan_fd(ch)); +} + +#define MIN_BUFSIZE 0x21000 + +struct fuse_chan *fuse_kern_chan_new(int fd) +{ + struct fuse_chan_ops op = { + .receive = fuse_kern_chan_receive, + .send = fuse_kern_chan_send, + .destroy = fuse_kern_chan_destroy, + }; + size_t bufsize = getpagesize() + 0x1000; + bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize; + return fuse_chan_new(&op, fd, bufsize, NULL); +} diff --git a/libfuse-lite/fuse_loop.c b/libfuse-lite/fuse_loop.c new file mode 100644 index 00000000..f7c17faf --- /dev/null +++ b/libfuse-lite/fuse_loop.c @@ -0,0 +1,39 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse_lowlevel.h" + +#include +#include +#include + +int fuse_session_loop(struct fuse_session *se) +{ + int res = 0; + struct fuse_chan *ch = fuse_session_next_chan(se, NULL); + size_t bufsize = fuse_chan_bufsize(ch); + char *buf = (char *) malloc(bufsize); + if (!buf) { + fprintf(stderr, "fuse: failed to allocate read buffer\n"); + return -1; + } + + while (!fuse_session_exited(se)) { + struct fuse_chan *tmpch = ch; + res = fuse_chan_recv(&tmpch, buf, bufsize); + if (res == -EINTR) + continue; + if (res <= 0) + break; + fuse_session_process(se, buf, res, tmpch); + } + + free(buf); + fuse_session_reset(se); + return res < 0 ? -1 : 0; +} diff --git a/libfuse-lite/fuse_loop_mt.c b/libfuse-lite/fuse_loop_mt.c new file mode 100644 index 00000000..533d9c31 --- /dev/null +++ b/libfuse-lite/fuse_loop_mt.c @@ -0,0 +1,221 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "fuse_lowlevel.h" +#include "fuse_misc.h" +#include "fuse_kernel.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct fuse_worker { + struct fuse_worker *prev; + struct fuse_worker *next; + pthread_t thread_id; + size_t bufsize; + char *buf; + struct fuse_mt *mt; +}; + +struct fuse_mt { + pthread_mutex_t lock; + int numworker; + int numavail; + struct fuse_session *se; + struct fuse_chan *prevch; + struct fuse_worker main; + sem_t finish; + int exit; + int error; +}; + +static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next) +{ + struct fuse_worker *prev = next->prev; + w->next = next; + w->prev = prev; + prev->next = w; + next->prev = w; +} + +static void list_del_worker(struct fuse_worker *w) +{ + struct fuse_worker *prev = w->prev; + struct fuse_worker *next = w->next; + prev->next = next; + next->prev = prev; +} + +static int fuse_start_thread(struct fuse_mt *mt); + +static void *fuse_do_work(void *data) +{ + struct fuse_worker *w = (struct fuse_worker *) data; + struct fuse_mt *mt = w->mt; + + while (!fuse_session_exited(mt->se)) { + int isforget = 0; + struct fuse_chan *ch = mt->prevch; + int res = fuse_chan_recv(&ch, w->buf, w->bufsize); + if (res == -EINTR) + continue; + if (res <= 0) { + if (res < 0) { + fuse_session_exit(mt->se); + mt->error = -1; + } + break; + } + + pthread_mutex_lock(&mt->lock); + if (mt->exit) { + pthread_mutex_unlock(&mt->lock); + return NULL; + } + + /* + * This disgusting hack is needed so that zillions of threads + * are not created on a burst of FORGET messages + */ + if (((struct fuse_in_header *) w->buf)->opcode == FUSE_FORGET) + isforget = 1; + + if (!isforget) + mt->numavail--; + if (mt->numavail == 0) + fuse_start_thread(mt); + pthread_mutex_unlock(&mt->lock); + + fuse_session_process(mt->se, w->buf, res, ch); + + pthread_mutex_lock(&mt->lock); + if (!isforget) + mt->numavail++; + if (mt->numavail > 10) { + if (mt->exit) { + pthread_mutex_unlock(&mt->lock); + return NULL; + } + list_del_worker(w); + mt->numavail--; + mt->numworker--; + pthread_mutex_unlock(&mt->lock); + + pthread_detach(w->thread_id); + free(w->buf); + free(w); + return NULL; + } + pthread_mutex_unlock(&mt->lock); + } + + sem_post(&mt->finish); + pause(); + + return NULL; +} + +static int fuse_start_thread(struct fuse_mt *mt) +{ + sigset_t oldset; + sigset_t newset; + int res; + struct fuse_worker *w = malloc(sizeof(struct fuse_worker)); + if (!w) { + fprintf(stderr, "fuse: failed to allocate worker structure\n"); + return -1; + } + memset(w, 0, sizeof(struct fuse_worker)); + w->bufsize = fuse_chan_bufsize(mt->prevch); + w->buf = malloc(w->bufsize); + w->mt = mt; + if (!w->buf) { + fprintf(stderr, "fuse: failed to allocate read buffer\n"); + free(w); + return -1; + } + + /* Disallow signal reception in worker threads */ + sigemptyset(&newset); + sigaddset(&newset, SIGTERM); + sigaddset(&newset, SIGINT); + sigaddset(&newset, SIGHUP); + sigaddset(&newset, SIGQUIT); + pthread_sigmask(SIG_BLOCK, &newset, &oldset); + res = pthread_create(&w->thread_id, NULL, fuse_do_work, w); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + if (res != 0) { + fprintf(stderr, "fuse: error creating thread: %s\n", strerror(res)); + free(w->buf); + free(w); + return -1; + } + list_add_worker(w, &mt->main); + mt->numavail ++; + mt->numworker ++; + + return 0; +} + +static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w) +{ + pthread_join(w->thread_id, NULL); + pthread_mutex_lock(&mt->lock); + list_del_worker(w); + pthread_mutex_unlock(&mt->lock); + free(w->buf); + free(w); +} + +int fuse_session_loop_mt(struct fuse_session *se) +{ + int err; + struct fuse_mt mt; + struct fuse_worker *w; + + memset(&mt, 0, sizeof(struct fuse_mt)); + mt.se = se; + mt.prevch = fuse_session_next_chan(se, NULL); + mt.error = 0; + mt.numworker = 0; + mt.numavail = 0; + mt.main.thread_id = pthread_self(); + mt.main.prev = mt.main.next = &mt.main; + sem_init(&mt.finish, 0, 0); + fuse_mutex_init(&mt.lock); + + pthread_mutex_lock(&mt.lock); + err = fuse_start_thread(&mt); + pthread_mutex_unlock(&mt.lock); + if (!err) { + /* sem_wait() is interruptible */ + while (!fuse_session_exited(se)) + sem_wait(&mt.finish); + + for (w = mt.main.next; w != &mt.main; w = w->next) + pthread_cancel(w->thread_id); + mt.exit = 1; + pthread_mutex_unlock(&mt.lock); + + while (mt.main.next != &mt.main) + fuse_join_worker(&mt, mt.main.next); + + err = mt.error; + } + + pthread_mutex_destroy(&mt.lock); + sem_destroy(&mt.finish); + fuse_session_reset(se); + return err; +} diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c new file mode 100644 index 00000000..81287e2c --- /dev/null +++ b/libfuse-lite/fuse_lowlevel.c @@ -0,0 +1,1319 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse_lowlevel.h" +#include "fuse_kernel.h" +#include "fuse_opt.h" +#include "fuse_i.h" +#include "fuse_misc.h" +#include "fuse_lowlevel_compat.h" + +#include +#include +#include +#include +#include +#include +#include + +#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) +#define OFFSET_MAX 0x7fffffffffffffffLL + +struct fuse_ll; + +struct fuse_req { + struct fuse_ll *f; + uint64_t unique; + int ctr; + pthread_mutex_t lock; + struct fuse_ctx ctx; + struct fuse_chan *ch; + int interrupted; + union { + struct { + uint64_t unique; + } i; + struct { + fuse_interrupt_func_t func; + void *data; + } ni; + } u; + struct fuse_req *next; + struct fuse_req *prev; +}; + +struct fuse_ll { + int debug; + int allow_root; + struct fuse_lowlevel_ops op; + int got_init; + void *userdata; + uid_t owner; + struct fuse_conn_info conn; + struct fuse_req list; + struct fuse_req interrupts; + pthread_mutex_t lock; + int got_destroy; +}; + +static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) +{ + attr->ino = stbuf->st_ino; + attr->mode = stbuf->st_mode; + attr->nlink = stbuf->st_nlink; + attr->uid = stbuf->st_uid; + attr->gid = stbuf->st_gid; + attr->rdev = stbuf->st_rdev; + attr->size = stbuf->st_size; + attr->blocks = stbuf->st_blocks; + attr->atime = stbuf->st_atime; + attr->mtime = stbuf->st_mtime; + attr->ctime = stbuf->st_ctime; + attr->atimensec = ST_ATIM_NSEC(stbuf); + attr->mtimensec = ST_MTIM_NSEC(stbuf); + attr->ctimensec = ST_CTIM_NSEC(stbuf); +} + +static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) +{ + stbuf->st_mode = attr->mode; + stbuf->st_uid = attr->uid; + stbuf->st_gid = attr->gid; + stbuf->st_size = attr->size; + stbuf->st_atime = attr->atime; + stbuf->st_mtime = attr->mtime; + ST_ATIM_NSEC_SET(stbuf, attr->atimensec); + ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); +} + +static size_t iov_length(const struct iovec *iov, size_t count) +{ + size_t seg; + size_t ret = 0; + + for (seg = 0; seg < count; seg++) + ret += iov[seg].iov_len; + return ret; +} + +static void list_init_req(struct fuse_req *req) +{ + req->next = req; + req->prev = req; +} + +static void list_del_req(struct fuse_req *req) +{ + struct fuse_req *prev = req->prev; + struct fuse_req *next = req->next; + prev->next = next; + next->prev = prev; +} + +static void list_add_req(struct fuse_req *req, struct fuse_req *next) +{ + struct fuse_req *prev = next->prev; + req->next = next; + req->prev = prev; + prev->next = req; + next->prev = req; +} + +static void destroy_req(fuse_req_t req) +{ + pthread_mutex_destroy(&req->lock); + free(req); +} + +static void free_req(fuse_req_t req) +{ + int ctr; + struct fuse_ll *f = req->f; + + pthread_mutex_lock(&req->lock); + req->u.ni.func = NULL; + req->u.ni.data = NULL; + pthread_mutex_unlock(&req->lock); + + pthread_mutex_lock(&f->lock); + list_del_req(req); + ctr = --req->ctr; + pthread_mutex_unlock(&f->lock); + if (!ctr) + destroy_req(req); +} + +static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, + int count) +{ + struct fuse_out_header out; + int res; + + if (error <= -1000 || error > 0) { + fprintf(stderr, "fuse: bad error value: %i\n", error); + error = -ERANGE; + } + + out.unique = req->unique; + out.error = error; + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + out.len = iov_length(iov, count); + + if (req->f->debug) + fprintf(stderr, " unique: %llu, error: %i (%s), outsize: %i\n", + (unsigned long long) out.unique, out.error, + strerror(-out.error), out.len); + res = fuse_chan_send(req->ch, iov, count); + free_req(req); + + return res; +} + +static int send_reply(fuse_req_t req, int error, const void *arg, + size_t argsize) +{ + struct iovec iov[2]; + int count = 1; + if (argsize) { + iov[1].iov_base = (void *) arg; + iov[1].iov_len = argsize; + count++; + } + return send_reply_iov(req, error, iov, count); +} + +int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) +{ + int res; + struct iovec *padded_iov; + + padded_iov = malloc((count + 1) * sizeof(struct iovec)); + if (padded_iov == NULL) + return fuse_reply_err(req, -ENOMEM); + + memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); + count++; + + res = send_reply_iov(req, 0, padded_iov, count); + free(padded_iov); + + return res; +} + +size_t fuse_dirent_size(size_t namelen) +{ + return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen); +} + +char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, + off_t off) +{ + unsigned namelen = strlen(name); + unsigned entlen = FUSE_NAME_OFFSET + namelen; + unsigned entsize = fuse_dirent_size(namelen); + unsigned padlen = entsize - entlen; + struct fuse_dirent *dirent = (struct fuse_dirent *) buf; + + dirent->ino = stbuf->st_ino; + dirent->off = off; + dirent->namelen = namelen; + dirent->type = (stbuf->st_mode & 0170000) >> 12; + strncpy(dirent->name, name, namelen); + if (padlen) + memset(buf + entlen, 0, padlen); + + return buf + entsize; +} + +size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + const char *name, const struct stat *stbuf, off_t off) +{ + size_t entsize; + + (void) req; + entsize = fuse_dirent_size(strlen(name)); + if (entsize <= bufsize && buf) + fuse_add_dirent(buf, name, stbuf, off); + return entsize; +} + +static void convert_statfs(const struct statvfs *stbuf, + struct fuse_kstatfs *kstatfs) +{ + kstatfs->bsize = stbuf->f_bsize; + kstatfs->frsize = stbuf->f_frsize; + kstatfs->blocks = stbuf->f_blocks; + kstatfs->bfree = stbuf->f_bfree; + kstatfs->bavail = stbuf->f_bavail; + kstatfs->files = stbuf->f_files; + kstatfs->ffree = stbuf->f_ffree; + kstatfs->namelen = stbuf->f_namemax; +} + +static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) +{ + return send_reply(req, 0, arg, argsize); +} + +int fuse_reply_err(fuse_req_t req, int err) +{ + return send_reply(req, -err, NULL, 0); +} + +void fuse_reply_none(fuse_req_t req) +{ + fuse_chan_send(req->ch, NULL, 0); + free_req(req); +} + +static unsigned long calc_timeout_sec(double t) +{ + if (t > (double) ULONG_MAX) + return ULONG_MAX; + else if (t < 0.0) + return 0; + else + return (unsigned long) t; +} + +static unsigned int calc_timeout_nsec(double t) +{ + double f = t - (double) calc_timeout_sec(t); + if (f < 0.0) + return 0; + else if (f >= 0.999999999) + return 999999999; + else + return (unsigned int) (f * 1.0e9); +} + +static void fill_entry(struct fuse_entry_out *arg, + const struct fuse_entry_param *e) +{ + arg->nodeid = e->ino; + arg->generation = e->generation; + arg->entry_valid = calc_timeout_sec(e->entry_timeout); + arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); + arg->attr_valid = calc_timeout_sec(e->attr_timeout); + arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); + convert_stat(&e->attr, &arg->attr); +} + +static void fill_open(struct fuse_open_out *arg, + const struct fuse_file_info *f) +{ + arg->fh = f->fh; + if (f->direct_io) + arg->open_flags |= FOPEN_DIRECT_IO; + if (f->keep_cache) + arg->open_flags |= FOPEN_KEEP_CACHE; +} + +int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) +{ + struct fuse_entry_out arg; + + /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant + negative entry */ + if (!e->ino && req->f->conn.proto_minor < 4) + return fuse_reply_err(req, ENOENT); + + memset(&arg, 0, sizeof(arg)); + fill_entry(&arg, e); + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *f) +{ + struct { + struct fuse_entry_out e; + struct fuse_open_out o; + } arg; + + memset(&arg, 0, sizeof(arg)); + fill_entry(&arg.e, e); + fill_open(&arg.o, f); + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_attr(fuse_req_t req, const struct stat *attr, + double attr_timeout) +{ + struct fuse_attr_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.attr_valid = calc_timeout_sec(attr_timeout); + arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); + convert_stat(attr, &arg.attr); + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_readlink(fuse_req_t req, const char *linkname) +{ + return send_reply_ok(req, linkname, strlen(linkname)); +} + +int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) +{ + struct fuse_open_out arg; + + memset(&arg, 0, sizeof(arg)); + fill_open(&arg, f); + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_write(fuse_req_t req, size_t count) +{ + struct fuse_write_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.size = count; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) +{ + return send_reply_ok(req, buf, size); +} + +int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) +{ + struct fuse_statfs_out arg; + size_t size = req->f->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + convert_statfs(stbuf, &arg.st); + + return send_reply_ok(req, &arg, size); +} + +int fuse_reply_xattr(fuse_req_t req, size_t count) +{ + struct fuse_getxattr_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.size = count; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_lock(fuse_req_t req, struct flock *lock) +{ + struct fuse_lk_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.lk.type = lock->l_type; + if (lock->l_type != F_UNLCK) { + arg.lk.start = lock->l_start; + if (lock->l_len == 0) + arg.lk.end = OFFSET_MAX; + else + arg.lk.end = lock->l_start + lock->l_len - 1; + } + arg.lk.pid = lock->l_pid; + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_bmap(fuse_req_t req, uint64_t idx) +{ + struct fuse_bmap_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.block = idx; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + char *name = (char *) inarg; + + if (req->f->op.lookup) + req->f->op.lookup(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; + + if (req->f->op.forget) + req->f->op.forget(req, nodeid, arg->nlookup); + else + fuse_reply_none(req); +} + +static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + (void) inarg; + + if (req->f->op.getattr) + req->f->op.getattr(req, nodeid, NULL); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg; + + if (req->f->op.setattr) { + struct fuse_file_info *fi = NULL; + struct fuse_file_info fi_store; + struct stat stbuf; + memset(&stbuf, 0, sizeof(stbuf)); + convert_attr(arg, &stbuf); + if (arg->valid & FATTR_FH) { + arg->valid &= ~FATTR_FH; + memset(&fi_store, 0, sizeof(fi_store)); + fi = &fi_store; + fi->fh = arg->fh; + fi->fh_old = fi->fh; + } + req->f->op.setattr(req, nodeid, &stbuf, arg->valid, fi); + } else + fuse_reply_err(req, ENOSYS); +} + +static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_access_in *arg = (struct fuse_access_in *) inarg; + + if (req->f->op.access) + req->f->op.access(req, nodeid, arg->mask); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + (void) inarg; + + if (req->f->op.readlink) + req->f->op.readlink(req, nodeid); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg; + + if (req->f->op.mknod) + req->f->op.mknod(req, nodeid, PARAM(arg), arg->mode, arg->rdev); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg; + + if (req->f->op.mkdir) + req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + char *name = (char *) inarg; + + if (req->f->op.unlink) + req->f->op.unlink(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + char *name = (char *) inarg; + + if (req->f->op.rmdir) + req->f->op.rmdir(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + char *name = (char *) inarg; + char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1; + + if (req->f->op.symlink) + req->f->op.symlink(req, linkname, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg; + char *oldname = PARAM(arg); + char *newname = oldname + strlen(oldname) + 1; + + if (req->f->op.rename) + req->f->op.rename(req, nodeid, oldname, arg->newdir, newname); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_link_in *arg = (struct fuse_link_in *) inarg; + + if (req->f->op.link) + req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_open_in *arg = (struct fuse_open_in *) inarg; + + if (req->f->op.create) { + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + req->f->op.create(req, nodeid, PARAM(arg), arg->mode, &fi); + } else + fuse_reply_err(req, ENOSYS); +} + +static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_open_in *arg = (struct fuse_open_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + if (req->f->op.open) + req->f->op.open(req, nodeid, &fi); + else + fuse_reply_open(req, &fi); +} + +static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + + if (req->f->op.read) { + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + req->f->op.read(req, nodeid, arg->size, arg->offset, &fi); + } else + fuse_reply_err(req, ENOSYS); +} + +static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_write_in *arg = (struct fuse_write_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + fi.writepage = arg->write_flags & 1; + + if (req->f->op.write) + req->f->op.write(req, nodeid, PARAM(arg), arg->size, arg->offset, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + fi.flush = 1; + if (req->f->conn.proto_minor >= 7) + fi.lock_owner = arg->lock_owner; + + if (req->f->op.flush) + req->f->op.flush(req, nodeid, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + fi.fh = arg->fh; + fi.fh_old = fi.fh; + if (req->f->conn.proto_minor >= 8) { + fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; + fi.lock_owner = arg->lock_owner; + } + + if (req->f->op.release) + req->f->op.release(req, nodeid, &fi); + else + fuse_reply_err(req, 0); +} + +static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.fsync) + req->f->op.fsync(req, nodeid, arg->fsync_flags & 1, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_open_in *arg = (struct fuse_open_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + if (req->f->op.opendir) + req->f->op.opendir(req, nodeid, &fi); + else + fuse_reply_open(req, &fi); +} + +static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.readdir) + req->f->op.readdir(req, nodeid, arg->size, arg->offset, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.releasedir) + req->f->op.releasedir(req, nodeid, &fi); + else + fuse_reply_err(req, 0); +} + +static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.fsyncdir) + req->f->op.fsyncdir(req, nodeid, arg->fsync_flags & 1, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + (void) nodeid; + (void) inarg; + + if (req->f->op.statfs) + req->f->op.statfs(req, nodeid); + else { + struct statvfs buf = { + .f_namemax = 255, + .f_bsize = 512, + }; + fuse_reply_statfs(req, &buf); + } +} + +static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; + char *name = PARAM(arg); + char *value = name + strlen(name) + 1; + + if (req->f->op.setxattr) + req->f->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; + + if (req->f->op.getxattr) + req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; + + if (req->f->op.listxattr) + req->f->op.listxattr(req, nodeid, arg->size); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + char *name = (char *) inarg; + + if (req->f->op.removexattr) + req->f->op.removexattr(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void convert_fuse_file_lock(struct fuse_file_lock *fl, + struct flock *flock) +{ + memset(flock, 0, sizeof(struct flock)); + flock->l_type = fl->type; + flock->l_whence = SEEK_SET; + flock->l_start = fl->start; + if (fl->end == OFFSET_MAX) + flock->l_len = 0; + else + flock->l_len = fl->end - fl->start + 1; + flock->l_pid = fl->pid; +} + +static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; + struct fuse_file_info fi; + struct flock flock; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.lock_owner = arg->owner; + + convert_fuse_file_lock(&arg->lk, &flock); + if (req->f->op.getlk) + req->f->op.getlk(req, nodeid, &fi, &flock); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, + const void *inarg, int sleep) +{ + struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; + struct fuse_file_info fi; + struct flock flock; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.lock_owner = arg->owner; + + convert_fuse_file_lock(&arg->lk, &flock); + if (req->f->op.setlk) + req->f->op.setlk(req, nodeid, &fi, &flock, sleep); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + do_setlk_common(req, nodeid, inarg, 0); +} + +static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + do_setlk_common(req, nodeid, inarg, 1); +} + +static int find_interrupted(struct fuse_ll *f, struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = f->list.next; curr != &f->list; curr = curr->next) { + if (curr->unique == req->u.i.unique) { + curr->ctr++; + pthread_mutex_unlock(&f->lock); + + /* Ugh, ugly locking */ + pthread_mutex_lock(&curr->lock); + pthread_mutex_lock(&f->lock); + curr->interrupted = 1; + pthread_mutex_unlock(&f->lock); + if (curr->u.ni.func) + curr->u.ni.func(curr, curr->u.ni.data); + pthread_mutex_unlock(&curr->lock); + + pthread_mutex_lock(&f->lock); + curr->ctr--; + if (!curr->ctr) + destroy_req(curr); + + return 1; + } + } + for (curr = f->interrupts.next; curr != &f->interrupts; + curr = curr->next) { + if (curr->u.i.unique == req->u.i.unique) + return 1; + } + return 0; +} + +static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg; + struct fuse_ll *f = req->f; + + (void) nodeid; + if (f->debug) + fprintf(stderr, "INTERRUPT: %llu\n", (unsigned long long) arg->unique); + + req->u.i.unique = arg->unique; + + pthread_mutex_lock(&f->lock); + if (find_interrupted(f, req)) + destroy_req(req); + else + list_add_req(req, &f->interrupts); + pthread_mutex_unlock(&f->lock); +} + +static struct fuse_req *check_interrupt(struct fuse_ll *f, struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) { + if (curr->u.i.unique == req->unique) { + req->interrupted = 1; + list_del_req(curr); + free(curr); + return NULL; + } + } + curr = f->interrupts.next; + if (curr != &f->interrupts) { + list_del_req(curr); + list_init_req(curr); + return curr; + } else + return NULL; +} + +static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg; + + if (req->f->op.bmap) + req->f->op.bmap(req, nodeid, arg->blocksize, arg->block); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_init_in *arg = (struct fuse_init_in *) inarg; + struct fuse_init_out outarg; + struct fuse_ll *f = req->f; + size_t bufsize = fuse_chan_bufsize(req->ch); + + (void) nodeid; + if (f->debug) { + fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor); + if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) { + fprintf(stderr, "flags=0x%08x\n", arg->flags); + fprintf(stderr, "max_readahead=0x%08x\n", arg->max_readahead); + } + } + f->conn.proto_major = arg->major; + f->conn.proto_minor = arg->minor; + + if (arg->major < 7) { + fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n", + arg->major, arg->minor); + fuse_reply_err(req, EPROTO); + return; + } + + if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) { + if (f->conn.async_read) + f->conn.async_read = arg->flags & FUSE_ASYNC_READ; + if (arg->max_readahead < f->conn.max_readahead) + f->conn.max_readahead = arg->max_readahead; + } else { + f->conn.async_read = 0; + f->conn.max_readahead = 0; + } + + if (bufsize < FUSE_MIN_READ_BUFFER) { + fprintf(stderr, "fuse: warning: buffer size too small: %zu\n", + bufsize); + bufsize = FUSE_MIN_READ_BUFFER; + } + + bufsize -= 4096; + if (bufsize < f->conn.max_write) + f->conn.max_write = bufsize; + + f->got_init = 1; + if (f->op.init) + f->op.init(f->userdata, &f->conn); + + memset(&outarg, 0, sizeof(outarg)); + outarg.major = FUSE_KERNEL_VERSION; + outarg.minor = FUSE_KERNEL_MINOR_VERSION; + if (f->conn.async_read) + outarg.flags |= FUSE_ASYNC_READ; + if (f->op.getlk && f->op.setlk) + outarg.flags |= FUSE_POSIX_LOCKS; + outarg.max_readahead = f->conn.max_readahead; + outarg.max_write = f->conn.max_write; + + if (f->debug) { + fprintf(stderr, " INIT: %u.%u\n", outarg.major, outarg.minor); + fprintf(stderr, " flags=0x%08x\n", outarg.flags); + fprintf(stderr, " max_readahead=0x%08x\n", outarg.max_readahead); + fprintf(stderr, " max_write=0x%08x\n", outarg.max_write); + } + + send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg)); +} + +static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_ll *f = req->f; + + (void) nodeid; + (void) inarg; + + f->got_destroy = 1; + if (f->op.destroy) + f->op.destroy(f->userdata); + + send_reply_ok(req, NULL, 0); +} + +void *fuse_req_userdata(fuse_req_t req) +{ + return req->f->userdata; +} + +const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) +{ + return &req->ctx; +} + +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + void *data) +{ + pthread_mutex_lock(&req->lock); + req->u.ni.func = func; + req->u.ni.data = data; + if (req->interrupted && func) + func(req, data); + pthread_mutex_unlock(&req->lock); +} + +int fuse_req_interrupted(fuse_req_t req) +{ + int interrupted; + + pthread_mutex_lock(&req->f->lock); + interrupted = req->interrupted; + pthread_mutex_unlock(&req->f->lock); + + return interrupted; +} + +static struct { + void (*func)(fuse_req_t, fuse_ino_t, const void *); + const char *name; +} fuse_ll_ops[] = { + [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, + [FUSE_FORGET] = { do_forget, "FORGET" }, + [FUSE_GETATTR] = { do_getattr, "GETATTR" }, + [FUSE_SETATTR] = { do_setattr, "SETATTR" }, + [FUSE_READLINK] = { do_readlink, "READLINK" }, + [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, + [FUSE_MKNOD] = { do_mknod, "MKNOD" }, + [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, + [FUSE_UNLINK] = { do_unlink, "UNLINK" }, + [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, + [FUSE_RENAME] = { do_rename, "RENAME" }, + [FUSE_LINK] = { do_link, "LINK" }, + [FUSE_OPEN] = { do_open, "OPEN" }, + [FUSE_READ] = { do_read, "READ" }, + [FUSE_WRITE] = { do_write, "WRITE" }, + [FUSE_STATFS] = { do_statfs, "STATFS" }, + [FUSE_RELEASE] = { do_release, "RELEASE" }, + [FUSE_FSYNC] = { do_fsync, "FSYNC" }, + [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, + [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, + [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, + [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, + [FUSE_FLUSH] = { do_flush, "FLUSH" }, + [FUSE_INIT] = { do_init, "INIT" }, + [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, + [FUSE_READDIR] = { do_readdir, "READDIR" }, + [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, + [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, + [FUSE_GETLK] = { do_getlk, "GETLK" }, + [FUSE_SETLK] = { do_setlk, "SETLK" }, + [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, + [FUSE_ACCESS] = { do_access, "ACCESS" }, + [FUSE_CREATE] = { do_create, "CREATE" }, + [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, + [FUSE_BMAP] = { do_bmap, "BMAP" }, + [FUSE_DESTROY] = { do_destroy, "DESTROY" }, +}; + +#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) + +static const char *opname(enum fuse_opcode opcode) +{ + if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) + return "???"; + else + return fuse_ll_ops[opcode].name; +} + +static void fuse_ll_process(void *data, const char *buf, size_t len, + struct fuse_chan *ch) +{ + struct fuse_ll *f = (struct fuse_ll *) data; + struct fuse_in_header *in = (struct fuse_in_header *) buf; + const void *inarg = buf + sizeof(struct fuse_in_header); + struct fuse_req *req; + + if (f->debug) + fprintf(stderr, "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu\n", + (unsigned long long) in->unique, + opname((enum fuse_opcode) in->opcode), in->opcode, + (unsigned long) in->nodeid, len); + + req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); + if (req == NULL) { + fprintf(stderr, "fuse: failed to allocate request\n"); + return; + } + + req->f = f; + req->unique = in->unique; + req->ctx.uid = in->uid; + req->ctx.gid = in->gid; + req->ctx.pid = in->pid; + req->ch = ch; + req->ctr = 1; + list_init_req(req); + fuse_mutex_init(&req->lock); + + if (!f->got_init && in->opcode != FUSE_INIT) + fuse_reply_err(req, EIO); + else if (f->allow_root && in->uid != f->owner && in->uid != 0 && + in->opcode != FUSE_INIT && in->opcode != FUSE_READ && + in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && + in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && + in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) { + fuse_reply_err(req, EACCES); + } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) + fuse_reply_err(req, ENOSYS); + else { + if (in->opcode != FUSE_INTERRUPT) { + struct fuse_req *intr; + pthread_mutex_lock(&f->lock); + intr = check_interrupt(f, req); + list_add_req(req, &f->list); + pthread_mutex_unlock(&f->lock); + if (intr) + fuse_reply_err(intr, EAGAIN); + } + fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + } +} + +enum { + KEY_HELP, + KEY_VERSION, +}; + +static struct fuse_opt fuse_ll_opts[] = { + { "debug", offsetof(struct fuse_ll, debug), 1 }, + { "-d", offsetof(struct fuse_ll, debug), 1 }, + { "allow_root", offsetof(struct fuse_ll, allow_root), 1 }, + { "max_write=%u", offsetof(struct fuse_ll, conn.max_write), 0 }, + { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 }, + { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 }, + { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 }, + FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD), + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_END +}; + +static void fuse_ll_version(void) +{ + fprintf(stderr, "using FUSE kernel interface version %i.%i\n", + FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); +} + +static void fuse_ll_help(void) +{ + fprintf(stderr, +" -o max_write=N set maximum size of write requests\n" +" -o max_readahead=N set maximum readahead\n" +" -o async_read perform reads asynchronously (default)\n" +" -o sync_read perform reads synchronously\n"); +} + +static int fuse_ll_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void) data; (void) outargs; + + switch (key) { + case KEY_HELP: + fuse_ll_help(); + break; + + case KEY_VERSION: + fuse_ll_version(); + break; + + default: + fprintf(stderr, "fuse: unknown option `%s'\n", arg); + } + + return -1; +} + +static void fuse_ll_destroy(void *data) +{ + struct fuse_ll *f = (struct fuse_ll *) data; + + if (f->got_init && !f->got_destroy) { + if (f->op.destroy) + f->op.destroy(f->userdata); + } + + pthread_mutex_destroy(&f->lock); + free(f); +} + +/* + * always call fuse_lowlevel_new_common() internally, to work around a + * misfeature in the FreeBSD runtime linker, which links the old + * version of a symbol to internal references. + */ +struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata) +{ + struct fuse_ll *f; + struct fuse_session *se; + struct fuse_session_ops sop = { + .process = fuse_ll_process, + .destroy = fuse_ll_destroy, + }; + + if (sizeof(struct fuse_lowlevel_ops) < op_size) { + fprintf(stderr, "fuse: warning: library too old, some operations may not work\n"); + op_size = sizeof(struct fuse_lowlevel_ops); + } + + f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll)); + if (f == NULL) { + fprintf(stderr, "fuse: failed to allocate fuse object\n"); + goto out; + } + + f->conn.async_read = 1; + f->conn.max_write = UINT_MAX; + f->conn.max_readahead = UINT_MAX; + list_init_req(&f->list); + list_init_req(&f->interrupts); + fuse_mutex_init(&f->lock); + + if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1) + goto out_free; + + memcpy(&f->op, op, op_size); + f->owner = getuid(); + f->userdata = userdata; + + se = fuse_session_new(&sop, f); + if (!se) + goto out_free; + + return se; + + out_free: + free(f); + out: + return NULL; +} + + +struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata) +{ + return fuse_lowlevel_new_common(args, op, op_size, userdata); +} + + +struct fuse_ll_compat_conf { + unsigned max_read; + int set_max_read; +}; + +static const struct fuse_opt fuse_ll_opts_compat[] = { + { "max_read=", offsetof(struct fuse_ll_compat_conf, set_max_read), 1 }, + { "max_read=%u", offsetof(struct fuse_ll_compat_conf, max_read), 0 }, + FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP), + FUSE_OPT_END +}; + diff --git a/libfuse-lite/fuse_misc.h b/libfuse-lite/fuse_misc.h new file mode 100644 index 00000000..99e5117a --- /dev/null +++ b/libfuse-lite/fuse_misc.h @@ -0,0 +1,45 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include + +#ifndef USE_UCLIBC +#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL) +#else +static inline void fuse_mutex_init(pthread_mutex_t *mut) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + pthread_mutex_init(mut, &attr); + pthread_mutexattr_destroy(&attr); +} +#endif + +#ifdef HAVE_STRUCT_STAT_ST_ATIM +/* Linux */ +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) +#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) +/* FreeBSD */ +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) +#else +#define ST_ATIM_NSEC(stbuf) 0 +#define ST_CTIM_NSEC(stbuf) 0 +#define ST_MTIM_NSEC(stbuf) 0 +#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) +#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) +#endif diff --git a/libfuse-lite/fuse_mt.c b/libfuse-lite/fuse_mt.c new file mode 100644 index 00000000..0fda2a3d --- /dev/null +++ b/libfuse-lite/fuse_mt.c @@ -0,0 +1,24 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "fuse_i.h" +#include "fuse_lowlevel.h" + +#include +#include +#include +#include +#include + +int fuse_loop_mt(struct fuse *f) +{ + if (f == NULL) + return -1; + + return fuse_session_loop_mt(fuse_get_session(f)); +} diff --git a/libfuse-lite/fuse_opt.c b/libfuse-lite/fuse_opt.c new file mode 100644 index 00000000..1c6a3157 --- /dev/null +++ b/libfuse-lite/fuse_opt.c @@ -0,0 +1,367 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse_opt.h" + +#include +#include +#include +#include + +struct fuse_opt_context { + void *data; + const struct fuse_opt *opt; + fuse_opt_proc_t proc; + int argctr; + int argc; + char **argv; + struct fuse_args outargs; + char *opts; + int nonopt; +}; + +void fuse_opt_free_args(struct fuse_args *args) +{ + if (args) { + if (args->argv && args->allocated) { + int i; + for (i = 0; i < args->argc; i++) + free(args->argv[i]); + free(args->argv); + } + args->argc = 0; + args->argv = NULL; + args->allocated = 0; + } +} + +static int alloc_failed(void) +{ + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; +} + +int fuse_opt_add_arg(struct fuse_args *args, const char *arg) +{ + char **newargv; + char *newarg; + + assert(!args->argv || args->allocated); + + newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); + newarg = newargv ? strdup(arg) : NULL; + if (!newargv || !newarg) + return alloc_failed(); + + args->argv = newargv; + args->allocated = 1; + args->argv[args->argc++] = newarg; + args->argv[args->argc] = NULL; + return 0; +} + +int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) +{ + assert(pos <= args->argc); + if (fuse_opt_add_arg(args, arg) == -1) + return -1; + + if (pos != args->argc - 1) { + char *newarg = args->argv[args->argc - 1]; + memmove(&args->argv[pos + 1], &args->argv[pos], + sizeof(char *) * (args->argc - pos - 1)); + args->argv[pos] = newarg; + } + return 0; +} + +static int next_arg(struct fuse_opt_context *ctx, const char *opt) +{ + if (ctx->argctr + 1 >= ctx->argc) { + fprintf(stderr, "fuse: missing argument after `%s'\n", opt); + return -1; + } + ctx->argctr++; + return 0; +} + +static int add_arg(struct fuse_opt_context *ctx, const char *arg) +{ + return fuse_opt_add_arg(&ctx->outargs, arg); +} + +int fuse_opt_add_opt(char **opts, const char *opt) +{ + char *newopts; + if (!*opts) + newopts = strdup(opt); + else { + unsigned oldlen = strlen(*opts); + newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1); + if (newopts) { + newopts[oldlen] = ','; + strcpy(newopts + oldlen + 1, opt); + } + } + if (!newopts) + return alloc_failed(); + + *opts = newopts; + return 0; +} + +static int add_opt(struct fuse_opt_context *ctx, const char *opt) +{ + return fuse_opt_add_opt(&ctx->opts, opt); +} + +static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, + int iso) +{ + if (key == FUSE_OPT_KEY_DISCARD) + return 0; + + if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { + int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); + if (res == -1 || !res) + return res; + } + if (iso) + return add_opt(ctx, arg); + else + return add_arg(ctx, arg); +} + +static int match_template(const char *t, const char *arg, unsigned *sepp) +{ + int arglen = strlen(arg); + const char *sep = strchr(t, '='); + sep = sep ? sep : strchr(t, ' '); + if (sep && (!sep[1] || sep[1] == '%')) { + int tlen = sep - t; + if (sep[0] == '=') + tlen ++; + if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { + *sepp = sep - t; + return 1; + } + } + if (strcmp(t, arg) == 0) { + *sepp = 0; + return 1; + } + return 0; +} + +static const struct fuse_opt *find_opt(const struct fuse_opt *opt, + const char *arg, unsigned *sepp) +{ + for (; opt && opt->templ; opt++) + if (match_template(opt->templ, arg, sepp)) + return opt; + return NULL; +} + +int fuse_opt_match(const struct fuse_opt *opts, const char *opt) +{ + unsigned dummy; + return find_opt(opts, opt, &dummy) ? 1 : 0; +} + +static int process_opt_param(void *var, const char *format, const char *param, + const char *arg) +{ + assert(format[0] == '%'); + if (format[1] == 's') { + char *copy = strdup(param); + if (!copy) + return alloc_failed(); + + *(char **) var = copy; + } else { + if (sscanf(param, format, var) != 1) { + fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg); + return -1; + } + } + return 0; +} + +static int process_opt(struct fuse_opt_context *ctx, + const struct fuse_opt *opt, unsigned sep, + const char *arg, int iso) +{ + if (opt->offset == -1U) { + if (call_proc(ctx, arg, opt->value, iso) == -1) + return -1; + } else { + void *var = ctx->data + opt->offset; + if (sep && opt->templ[sep + 1]) { + const char *param = arg + sep; + if (opt->templ[sep] == '=') + param ++; + if (process_opt_param(var, opt->templ + sep + 1, + param, arg) == -1) + return -1; + } else + *(int *)var = opt->value; + } + return 0; +} + +static int process_opt_sep_arg(struct fuse_opt_context *ctx, + const struct fuse_opt *opt, unsigned sep, + const char *arg, int iso) +{ + int res; + char *newarg; + char *param; + + if (next_arg(ctx, arg) == -1) + return -1; + + param = ctx->argv[ctx->argctr]; + newarg = malloc(sep + strlen(param) + 1); + if (!newarg) + return alloc_failed(); + + memcpy(newarg, arg, sep); + strcpy(newarg + sep, param); + res = process_opt(ctx, opt, sep, newarg, iso); + free(newarg); + + return res; +} + +static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) +{ + unsigned sep; + const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); + if (opt) { + for (; opt; opt = find_opt(opt + 1, arg, &sep)) { + int res; + if (sep && opt->templ[sep] == ' ' && !arg[sep]) + res = process_opt_sep_arg(ctx, opt, sep, arg, iso); + else + res = process_opt(ctx, opt, sep, arg, iso); + if (res == -1) + return -1; + } + return 0; + } else + return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); +} + +static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) +{ + char *sep; + + do { + int res; + sep = strchr(opts, ','); + if (sep) + *sep = '\0'; + res = process_gopt(ctx, opts, 1); + if (res == -1) + return -1; + opts = sep + 1; + } while (sep); + + return 0; +} + +static int process_option_group(struct fuse_opt_context *ctx, const char *opts) +{ + int res; + char *copy; + const char *sep = strchr(opts, ','); + if (!sep) + return process_gopt(ctx, opts, 1); + + copy = strdup(opts); + if (!copy) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + res = process_real_option_group(ctx, copy); + free(copy); + return res; +} + +static int process_one(struct fuse_opt_context *ctx, const char *arg) +{ + if (ctx->nonopt || arg[0] != '-') + return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); + else if (arg[1] == 'o') { + if (arg[2]) + return process_option_group(ctx, arg + 2); + else { + if (next_arg(ctx, arg) == -1) + return -1; + + return process_option_group(ctx, ctx->argv[ctx->argctr]); + } + } else if (arg[1] == '-' && !arg[2]) { + if (add_arg(ctx, arg) == -1) + return -1; + ctx->nonopt = ctx->outargs.argc; + return 0; + } else + return process_gopt(ctx, arg, 0); +} + +static int opt_parse(struct fuse_opt_context *ctx) +{ + if (ctx->argc) { + if (add_arg(ctx, ctx->argv[0]) == -1) + return -1; + } + + for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) + if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) + return -1; + + if (ctx->opts) { + if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || + fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) + return -1; + } + if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) { + free(ctx->outargs.argv[ctx->outargs.argc - 1]); + ctx->outargs.argv[--ctx->outargs.argc] = NULL; + } + + return 0; +} + +int fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc) +{ + int res; + struct fuse_opt_context ctx = { + .data = data, + .opt = opts, + .proc = proc, + }; + + if (!args || !args->argv || !args->argc) + return 0; + + ctx.argc = args->argc; + ctx.argv = args->argv; + + res = opt_parse(&ctx); + if (res != -1) { + struct fuse_args tmp = *args; + *args = ctx.outargs; + ctx.outargs = tmp; + } + free(ctx.opts); + fuse_opt_free_args(&ctx.outargs); + return res; +} diff --git a/libfuse-lite/fuse_session.c b/libfuse-lite/fuse_session.c new file mode 100644 index 00000000..67c41a0d --- /dev/null +++ b/libfuse-lite/fuse_session.c @@ -0,0 +1,182 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse_lowlevel.h" +#include "fuse_lowlevel_compat.h" + +#include +#include +#include +#include +#include + +struct fuse_session { + struct fuse_session_ops op; + + void *data; + + volatile int exited; + + struct fuse_chan *ch; +}; + +struct fuse_chan { + struct fuse_chan_ops op; + + struct fuse_session *se; + + int fd; + + size_t bufsize; + + void *data; +}; + +struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data) +{ + struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se)); + if (se == NULL) { + fprintf(stderr, "fuse: failed to allocate session\n"); + return NULL; + } + + memset(se, 0, sizeof(*se)); + se->op = *op; + se->data = data; + + return se; +} + +void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch) +{ + assert(se->ch == NULL); + assert(ch->se == NULL); + se->ch = ch; + ch->se = se; +} + +void fuse_session_remove_chan(struct fuse_chan *ch) +{ + struct fuse_session *se = ch->se; + if (se) { + assert(se->ch == ch); + se->ch = NULL; + ch->se = NULL; + } +} + +struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, + struct fuse_chan *ch) +{ + assert(ch == NULL || ch == se->ch); + if (ch == NULL) + return se->ch; + else + return NULL; +} + +void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, + struct fuse_chan *ch) +{ + se->op.process(se->data, buf, len, ch); +} + +void fuse_session_destroy(struct fuse_session *se) +{ + if (se->op.destroy) + se->op.destroy(se->data); + if (se->ch != NULL) + fuse_chan_destroy(se->ch); + free(se); +} + +void fuse_session_exit(struct fuse_session *se) +{ + if (se->op.exit) + se->op.exit(se->data, 1); + se->exited = 1; +} + +void fuse_session_reset(struct fuse_session *se) +{ + if (se->op.exit) + se->op.exit(se->data, 0); + se->exited = 0; +} + +int fuse_session_exited(struct fuse_session *se) +{ + if (se->op.exited) + return se->op.exited(se->data); + else + return se->exited; +} + +static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd, + size_t bufsize, void *data) +{ + struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch)); + if (ch == NULL) { + fprintf(stderr, "fuse: failed to allocate channel\n"); + return NULL; + } + + memset(ch, 0, sizeof(*ch)); + ch->op = *op; + ch->fd = fd; + ch->bufsize = bufsize; + ch->data = data; + + return ch; +} + +struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, + size_t bufsize, void *data) +{ + return fuse_chan_new_common(op, fd, bufsize, data); +} + +int fuse_chan_fd(struct fuse_chan *ch) +{ + return ch->fd; +} + +size_t fuse_chan_bufsize(struct fuse_chan *ch) +{ + return ch->bufsize; +} + +void *fuse_chan_data(struct fuse_chan *ch) +{ + return ch->data; +} + +struct fuse_session *fuse_chan_session(struct fuse_chan *ch) +{ + return ch->se; +} + +int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size) +{ + struct fuse_chan *ch = *chp; + return ch->op.receive(chp, buf, size); +} + +int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count) +{ + return ch->op.send(ch, iov, count); +} + +void fuse_chan_destroy(struct fuse_chan *ch) +{ + fuse_session_remove_chan(ch); + if (ch->op.destroy) + ch->op.destroy(ch); + free(ch); +} + diff --git a/libfuse-lite/fuse_signals.c b/libfuse-lite/fuse_signals.c new file mode 100644 index 00000000..25998bfc --- /dev/null +++ b/libfuse-lite/fuse_signals.c @@ -0,0 +1,72 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse_lowlevel.h" + +#include +#include +#include + +static struct fuse_session *fuse_instance; + +static void exit_handler(int sig) +{ + (void) sig; + if (fuse_instance) + fuse_session_exit(fuse_instance); +} + +static int set_one_signal_handler(int sig, void (*handler)(int)) +{ + struct sigaction sa; + struct sigaction old_sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = handler; + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = 0; + + if (sigaction(sig, NULL, &old_sa) == -1) { + perror("fuse: cannot get old signal handler"); + return -1; + } + + if (old_sa.sa_handler == SIG_DFL && + sigaction(sig, &sa, NULL) == -1) { + perror("fuse: cannot set signal handler"); + return -1; + } + return 0; +} + +int fuse_set_signal_handlers(struct fuse_session *se) +{ + if (set_one_signal_handler(SIGHUP, exit_handler) == -1 || + set_one_signal_handler(SIGINT, exit_handler) == -1 || + set_one_signal_handler(SIGTERM, exit_handler) == -1 || + set_one_signal_handler(SIGPIPE, SIG_IGN) == -1) + return -1; + + fuse_instance = se; + return 0; +} + +void fuse_remove_signal_handlers(struct fuse_session *se) +{ + if (fuse_instance != se) + fprintf(stderr, + "fuse: fuse_remove_signal_handlers: unknown session\n"); + else + fuse_instance = NULL; + + set_one_signal_handler(SIGHUP, SIG_DFL); + set_one_signal_handler(SIGINT, SIG_DFL); + set_one_signal_handler(SIGTERM, SIG_DFL); + set_one_signal_handler(SIGPIPE, SIG_DFL); +} + diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c new file mode 100644 index 00000000..b3f67e07 --- /dev/null +++ b/libfuse-lite/helper.c @@ -0,0 +1,337 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "config.h" +#include "fuse_i.h" +#include "fuse_opt.h" +#include "fuse_lowlevel.h" + +#include +#include +#include +#include +#include +#include +#include + +enum { + KEY_HELP, + KEY_HELP_NOHEADER, + KEY_VERSION, +}; + +struct helper_opts { + int singlethread; + int foreground; + int nodefault_subtype; + char *mountpoint; +}; + +#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 } + +static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("-d", foreground), + FUSE_HELPER_OPT("debug", foreground), + FUSE_HELPER_OPT("-f", foreground), + FUSE_HELPER_OPT("-s", singlethread), + FUSE_HELPER_OPT("fsname=", nodefault_subtype), + FUSE_HELPER_OPT("subtype=", nodefault_subtype), + + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), + FUSE_OPT_END +}; + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s mountpoint [options]\n\n", progname); + fprintf(stderr, + "general options:\n" + " -o opt,[opt...] mount options\n" + " -h --help print help\n" + " -V --version print version\n" + "\n"); +} + +static void helper_help(void) +{ + fprintf(stderr, + "FUSE options:\n" + " -d -o debug enable debug output (implies -f)\n" + " -f foreground operation\n" + " -s disable multi-threaded operation\n" + "\n" + ); +} + +static void helper_version(void) +{ + fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION); +} + +static int fuse_helper_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + struct helper_opts *hopts = data; + + switch (key) { + case KEY_HELP: + usage(outargs->argv[0]); + /* fall through */ + + case KEY_HELP_NOHEADER: + helper_help(); + return fuse_opt_add_arg(outargs, "-h"); + + case KEY_VERSION: + helper_version(); + return 1; + + case FUSE_OPT_KEY_NONOPT: + if (!hopts->mountpoint) { + char mountpoint[PATH_MAX]; + if (realpath(arg, mountpoint) == NULL) { + fprintf(stderr, "fuse: bad mount point `%s': %s\n", arg, strerror(errno)); + return -1; + } + return fuse_opt_add_opt(&hopts->mountpoint, mountpoint); + } else { + fprintf(stderr, "fuse: invalid argument `%s'\n", arg); + return -1; + } + + default: + return 1; + } +} + +static int add_default_subtype(const char *progname, struct fuse_args *args) +{ + int res; + char *subtype_opt; + const char *basename = strrchr(progname, '/'); + if (basename == NULL) + basename = progname; + else if (basename[1] != '\0') + basename++; + + subtype_opt = (char *) malloc(strlen(basename) + 64); + if (subtype_opt == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + sprintf(subtype_opt, "-osubtype=%s", basename); + res = fuse_opt_add_arg(args, subtype_opt); + free(subtype_opt); + return res; +} + +int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, + int *multithreaded, int *foreground) +{ + int res; + struct helper_opts hopts; + + memset(&hopts, 0, sizeof(hopts)); + res = fuse_opt_parse(args, &hopts, fuse_helper_opts, fuse_helper_opt_proc); + if (res == -1) + return -1; + + if (!hopts.nodefault_subtype) { + res = add_default_subtype(args->argv[0], args); + if (res == -1) + goto err; + } + if (mountpoint) + *mountpoint = hopts.mountpoint; + else + free(hopts.mountpoint); + + if (multithreaded) + *multithreaded = !hopts.singlethread; + if (foreground) + *foreground = hopts.foreground; + return 0; + + err: + free(hopts.mountpoint); + return -1; +} + +int fuse_daemonize(int foreground) +{ + int res; + + if (!foreground) { + res = daemon(0, 0); + if (res == -1) { + perror("fuse: failed to daemonize program\n"); + return -1; + } + } + return 0; +} + +static struct fuse_chan *fuse_mount_common(const char *mountpoint, + struct fuse_args *args) +{ + struct fuse_chan *ch; + int 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); + + fd = fuse_kern_mount(mountpoint, args); + if (fd == -1) + return NULL; + + ch = fuse_kern_chan_new(fd); + if (!ch) + fuse_kern_unmount(mountpoint, fd); + + return ch; +} + +struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args) +{ + return fuse_mount_common(mountpoint, args); +} + +static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch) +{ + int fd = ch ? fuse_chan_fd(ch) : -1; + fuse_kern_unmount(mountpoint, fd); + fuse_chan_destroy(ch); +} + +void fuse_unmount(const char *mountpoint, struct fuse_chan *ch) +{ + fuse_unmount_common(mountpoint, ch); +} + +static struct fuse *fuse_setup_common(int argc, char *argv[], + const struct fuse_operations *op, + size_t op_size, + char **mountpoint, + int *multithreaded, + int *fd, + void *user_data) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_chan *ch; + struct fuse *fuse; + int foreground; + int res; + + res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground); + if (res == -1) + return NULL; + + ch = fuse_mount_common(*mountpoint, &args); + if (!ch) { + fuse_opt_free_args(&args); + goto err_free; + } + + fuse = fuse_new_common(ch, &args, op, op_size, user_data); + fuse_opt_free_args(&args); + if (fuse == NULL) + goto err_unmount; + + res = fuse_daemonize(foreground); + if (res == -1) + goto err_unmount; + + res = fuse_set_signal_handlers(fuse_get_session(fuse)); + if (res == -1) + goto err_unmount; + + if (fd) + *fd = fuse_chan_fd(ch); + + return fuse; + + err_unmount: + fuse_unmount_common(*mountpoint, ch); + if (fuse) + fuse_destroy(fuse); + err_free: + free(*mountpoint); + return NULL; +} + +static void fuse_teardown_common(struct fuse *fuse, char *mountpoint) +{ + struct fuse_session *se = fuse_get_session(fuse); + struct fuse_chan *ch = fuse_session_next_chan(se, NULL); + fuse_remove_signal_handlers(se); + fuse_unmount_common(mountpoint, ch); + fuse_destroy(fuse); + free(mountpoint); +} + +static int fuse_main_common(int argc, char *argv[], + const struct fuse_operations *op, size_t op_size, + void *user_data) +{ + struct fuse *fuse; + char *mountpoint; + int multithreaded; + int res; + + fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint, + &multithreaded, NULL, user_data); + if (fuse == NULL) + return 1; + + if (multithreaded) + res = fuse_loop_mt(fuse); + else + res = fuse_loop(fuse); + + fuse_teardown_common(fuse, mountpoint); + if (res == -1) + return 1; + + return 0; +} + +int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, + size_t op_size, void *user_data) +{ + return fuse_main_common(argc, argv, op, op_size, user_data); +} + +#undef fuse_main +int fuse_main(void); +int fuse_main(void) +{ + fprintf(stderr, "fuse_main(): This function does not exist\n"); + return -1; +} + +int fuse_version(void) +{ + return FUSE_VERSION; +} + diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c new file mode 100644 index 00000000..739850ff --- /dev/null +++ b/libfuse-lite/mount.c @@ -0,0 +1,570 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "config.h" +#include "fuse_i.h" +#include "fuse_opt.h" +#include "mount_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUSERMOUNT_PROG "fusermount" +#define FUSE_COMMFD_ENV "_FUSE_COMMFD" + +#ifndef HAVE_FORK +#define fork() vfork() +#endif + +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + +enum { + KEY_KERN_FLAG, + KEY_KERN_OPT, + KEY_FUSERMOUNT_OPT, + KEY_SUBTYPE_OPT, + KEY_MTAB_OPT, + KEY_ALLOW_ROOT, + KEY_RO, + KEY_HELP, + KEY_VERSION, +}; + +struct mount_opts { + int allow_other; + int allow_root; + int ishelp; + int flags; + int nonempty; + int blkdev; + char *fsname; + char *subtype; + char *subtype_opt; + char *mtab_opts; + char *fusermount_opts; + char *kernel_opts; +}; + +#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 } + +static const struct fuse_opt fuse_mount_opts[] = { + FUSE_MOUNT_OPT("allow_other", allow_other), + FUSE_MOUNT_OPT("allow_root", allow_root), + FUSE_MOUNT_OPT("nonempty", nonempty), + FUSE_MOUNT_OPT("blkdev", blkdev), + FUSE_MOUNT_OPT("fsname=%s", fsname), + FUSE_MOUNT_OPT("subtype=%s", subtype), + FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), + FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), + FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT), + FUSE_OPT_KEY("large_read", KEY_KERN_OPT), + FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), + FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), + FUSE_OPT_KEY("max_read=", KEY_KERN_OPT), + FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("user=", KEY_MTAB_OPT), + FUSE_OPT_KEY("-r", KEY_RO), + FUSE_OPT_KEY("ro", KEY_KERN_FLAG), + FUSE_OPT_KEY("rw", KEY_KERN_FLAG), + FUSE_OPT_KEY("suid", KEY_KERN_FLAG), + FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG), + FUSE_OPT_KEY("dev", KEY_KERN_FLAG), + FUSE_OPT_KEY("nodev", KEY_KERN_FLAG), + FUSE_OPT_KEY("exec", KEY_KERN_FLAG), + FUSE_OPT_KEY("noexec", KEY_KERN_FLAG), + FUSE_OPT_KEY("async", KEY_KERN_FLAG), + FUSE_OPT_KEY("sync", KEY_KERN_FLAG), + FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG), + FUSE_OPT_KEY("atime", KEY_KERN_FLAG), + FUSE_OPT_KEY("noatime", KEY_KERN_FLAG), + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_END +}; + +static void mount_help(void) +{ + fprintf(stderr, + " -o allow_other allow access to other users\n" + " -o allow_root allow access to root\n" + " -o nonempty allow mounts over non-empty file/dir\n" + " -o default_permissions enable permission checking by kernel\n" + " -o fsname=NAME set filesystem name\n" + " -o subtype=NAME set filesystem type\n" + " -o large_read issue large read requests (2.4 only)\n" + " -o max_read=N set maximum size of read requests\n" + "\n" + ); +} + +static void exec_fusermount(const char *argv[]) +{ + execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv); + execvp(FUSERMOUNT_PROG, (char **) argv); +} + +static void mount_version(void) +{ + int pid = fork(); + if (!pid) { + const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL }; + exec_fusermount(argv); + _exit(1); + } else if (pid != -1) + waitpid(pid, NULL, 0); +} + +struct mount_flags { + const char *opt; + unsigned long flag; + int on; +}; + +static struct mount_flags mount_flags[] = { + {"rw", MS_RDONLY, 0}, + {"ro", MS_RDONLY, 1}, + {"suid", MS_NOSUID, 0}, + {"nosuid", MS_NOSUID, 1}, + {"dev", MS_NODEV, 0}, + {"nodev", MS_NODEV, 1}, + {"exec", MS_NOEXEC, 0}, + {"noexec", MS_NOEXEC, 1}, + {"async", MS_SYNCHRONOUS, 0}, + {"sync", MS_SYNCHRONOUS, 1}, + {"atime", MS_NOATIME, 0}, + {"noatime", MS_NOATIME, 1}, + {"dirsync", MS_DIRSYNC, 1}, + {NULL, 0, 0} +}; + +static void set_mount_flag(const char *s, int *flags) +{ + int i; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + const char *opt = mount_flags[i].opt; + if (strcmp(opt, s) == 0) { + if (mount_flags[i].on) + *flags |= mount_flags[i].flag; + else + *flags &= ~mount_flags[i].flag; + return; + } + } + fprintf(stderr, "fuse: internal error, can't find mount flag\n"); + abort(); +} + +static int fuse_mount_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + struct mount_opts *mo = data; + + switch (key) { + case KEY_ALLOW_ROOT: + if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 || + fuse_opt_add_arg(outargs, "-oallow_root") == -1) + return -1; + return 0; + + case KEY_RO: + arg = "ro"; + /* fall through */ + case KEY_KERN_FLAG: + set_mount_flag(arg, &mo->flags); + return 0; + + case KEY_KERN_OPT: + return fuse_opt_add_opt(&mo->kernel_opts, arg); + + case KEY_FUSERMOUNT_OPT: + return fuse_opt_add_opt(&mo->fusermount_opts, arg); + + case KEY_SUBTYPE_OPT: + return fuse_opt_add_opt(&mo->subtype_opt, arg); + + case KEY_MTAB_OPT: + return fuse_opt_add_opt(&mo->mtab_opts, arg); + + case KEY_HELP: + mount_help(); + mo->ishelp = 1; + break; + + case KEY_VERSION: + mount_version(); + mo->ishelp = 1; + break; + } + return 1; +} + +/* return value: + * >= 0 => fd + * -1 => error + */ +static int receive_fd(int fd) +{ + struct msghdr msg; + struct iovec iov; + char buf[1]; + int rv; + size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; + struct cmsghdr *cmsg; + + iov.iov_base = buf; + iov.iov_len = 1; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* old BSD implementations should use msg_accrights instead of + * msg_control; the interface is different. */ + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof(ccmsg); + + while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); + if (rv == -1) { + perror("recvmsg"); + return -1; + } + if(!rv) { + /* EOF */ + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg->cmsg_type == SCM_RIGHTS) { + fprintf(stderr, "got control message of unknown type %d\n", + cmsg->cmsg_type); + return -1; + } + return *(int*)CMSG_DATA(cmsg); +} + +void fuse_kern_unmount(const char *mountpoint, int fd) +{ + int res; + int pid; + + if (!mountpoint) + return; + + if (fd != -1) { + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = 0; + res = poll(&pfd, 1, 0); + /* If file poll returns POLLERR on the device file descriptor, + then the filesystem is already unmounted */ + if (res == 1 && (pfd.revents & POLLERR)) + return; + } + + if (geteuid() == 0) { + fuse_mnt_umount("fuse", mountpoint, 1); + return; + } + + res = umount2(mountpoint, 2); + if (res == 0) + return; + + pid = fork(); + if(pid == -1) + return; + + if(pid == 0) { + const char *argv[] = + { FUSERMOUNT_PROG, "-u", "-q", "-z", "--", mountpoint, NULL }; + + exec_fusermount(argv); + _exit(1); + } + waitpid(pid, NULL, 0); +} + +static int fuse_mount_fusermount(const char *mountpoint, const char *opts, + int quiet) +{ + int fds[2], pid; + int res; + int rv; + + if (!mountpoint) { + fprintf(stderr, "fuse: missing mountpoint\n"); + return -1; + } + + res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); + if(res == -1) { + perror("fuse: socketpair() failed"); + return -1; + } + + pid = fork(); + if(pid == -1) { + perror("fuse: fork() failed"); + close(fds[0]); + close(fds[1]); + return -1; + } + + if(pid == 0) { + char env[10]; + const char *argv[32]; + int a = 0; + + if (quiet) { + int fd = open("/dev/null", O_RDONLY); + dup2(fd, 1); + dup2(fd, 2); + } + + argv[a++] = FUSERMOUNT_PROG; + if (opts) { + argv[a++] = "-o"; + argv[a++] = opts; + } + argv[a++] = "--"; + argv[a++] = mountpoint; + argv[a++] = NULL; + + close(fds[1]); + fcntl(fds[0], F_SETFD, 0); + snprintf(env, sizeof(env), "%i", fds[0]); + setenv(FUSE_COMMFD_ENV, env, 1); + exec_fusermount(argv); + perror("fuse: failed to exec fusermount"); + _exit(1); + } + + close(fds[0]); + rv = receive_fd(fds[1]); + close(fds[1]); + waitpid(pid, NULL, 0); /* bury zombie */ + + return rv; +} + +static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, + const char *mnt_opts) +{ + char tmp[128]; + const char *devname = "/dev/fuse"; + char *source = NULL; + char *type = NULL; + struct stat stbuf; + int fd; + int res; + + if (!mnt) { + fprintf(stderr, "fuse: missing mountpoint\n"); + return -1; + } + + res = lstat(mnt, &stbuf); + if (res == -1) { + fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n", + mnt, strerror(errno)); + return -1; + } + + if (!mo->nonempty) { + res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode, stbuf.st_size); + if (res == -1) + return -1; + } + + fd = open(devname, O_RDWR); + if (fd == -1) { + if (errno == ENODEV || errno == ENOENT) + fprintf(stderr, + "fuse: device not found, try 'modprobe fuse' first\n"); + else + fprintf(stderr, "fuse: failed to open %s: %s\n", devname, + strerror(errno)); + return -1; + } + + snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i", fd, + stbuf.st_mode & S_IFMT, getuid(), getgid()); + + res = fuse_opt_add_opt(&mo->kernel_opts, tmp); + if (res == -1) + goto out_close; + + source = malloc((mo->fsname ? strlen(mo->fsname) : 0) + + (mo->subtype ? strlen(mo->subtype) : 0) + + strlen(devname) + 32); + + type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32); + if (!type || !source) { + fprintf(stderr, "fuse: failed to allocate memory\n"); + goto out_close; + } + + strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); + if (mo->subtype) { + strcat(type, "."); + strcat(type, mo->subtype); + } + strcpy(source, + mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname)); + + res = mount(source, mnt, type, mo->flags, mo->kernel_opts); + if (res == -1 && errno == ENODEV && mo->subtype) { + /* Probably missing subtype support */ + strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); + if (mo->fsname) { + if (!mo->blkdev) + sprintf(source, "%s#%s", mo->subtype, mo->fsname); + } else { + strcpy(source, type); + } + res = mount(source, mnt, type, mo->flags, mo->kernel_opts); + } + if (res == -1) { + /* + * Maybe kernel doesn't support unprivileged mounts, in this + * case try falling back to fusermount + */ + if (errno == EPERM) { + res = -2; + } else { + int errno_save = errno; + if (mo->blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) + fprintf(stderr, "fuse: 'fuseblk' support missing\n"); + else + fprintf(stderr, "fuse: mount failed: %s\n", + strerror(errno_save)); + } + + goto out_close; + } + + if (geteuid() == 0) { + char *newmnt = fuse_mnt_resolve_path("fuse", mnt); + res = -1; + if (!newmnt) + goto out_umount; + + res = fuse_mnt_add_mount("fuse", source, newmnt, type, mnt_opts); + free(newmnt); + if (res == -1) + goto out_umount; + } + + return fd; + + out_umount: + umount2(mnt, 2); /* lazy umount */ + out_close: + free(type); + free(source); + close(fd); + return res; +} + +static int get_mnt_flag_opts(char **mnt_optsp, int flags) +{ + int i; + + if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1) + return -1; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + if (mount_flags[i].on && (flags & mount_flags[i].flag) && + fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1) + return -1; + } + return 0; +} + +int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) +{ + struct mount_opts mo; + int res = -1; + char *mnt_opts = NULL; + + memset(&mo, 0, sizeof(mo)); + mo.flags = MS_NOSUID | MS_NODEV; + + if (args && + fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) + return -1; + + if (mo.allow_other && mo.allow_root) { + fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n"); + goto out; + } + res = 0; + if (mo.ishelp) + goto out; + + res = -1; + if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1) + goto out; + if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1) + goto out; + if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1) + goto out; + + res = fuse_mount_sys(mountpoint, &mo, mnt_opts); + if (res == -2) { + if (mo.fusermount_opts && + fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1) + goto out; + + if (mo.subtype) { + char *tmp_opts = NULL; + + res = -1; + if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 || + fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) { + free(tmp_opts); + goto out; + } + + res = fuse_mount_fusermount(mountpoint, tmp_opts, 1); + free(tmp_opts); + if (res == -1) + res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + } else { + res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + } + } + out: + free(mnt_opts); + free(mo.fsname); + free(mo.subtype); + free(mo.fusermount_opts); + free(mo.subtype_opt); + free(mo.kernel_opts); + free(mo.mtab_opts); + return res; +} + diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c new file mode 100644 index 00000000..9ce431ae --- /dev/null +++ b/libfuse-lite/mount_util.c @@ -0,0 +1,240 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "mount_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int mtab_needs_update(const char *mnt) +{ + struct stat stbuf; + + /* If mtab is within new mount, don't touch it */ + if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && + _PATH_MOUNTED[strlen(mnt)] == '/') + return 0; + + if (lstat(_PATH_MOUNTED, &stbuf) != -1 && S_ISLNK(stbuf.st_mode)) + return 0; + + return 1; +} + +int fuse_mnt_add_mount(const char *progname, const char *fsname, + const char *mnt, const char *type, const char *opts) +{ + int res; + int status; + + if (!mtab_needs_update(mnt)) + return 0; + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return -1; + } + if (res == 0) { + char templ[] = "/tmp/fusermountXXXXXX"; + char *tmp; + + setuid(geteuid()); + + /* + * hide in a directory, where mount isn't able to resolve + * fsname as a valid path + */ + tmp = mkdtemp(templ); + if (!tmp) { + fprintf(stderr, "%s: failed to create temporary directory\n", + progname); + exit(1); + } + if (chdir(tmp)) { + fprintf(stderr, "%s: failed to chdir to %s: %s\n", + progname, tmp, strerror(errno)); + exit(1); + } + rmdir(tmp); + execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts, + fsname, mnt, NULL); + fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", progname, + strerror(errno)); + exit(1); + } + res = waitpid(res, &status, 0); + if (res == -1) { + fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); + return -1; + } + if (status != 0) + return -1; + + return 0; +} + +int fuse_mnt_umount(const char *progname, const char *mnt, int lazy) +{ + int res; + int status; + + if (!mtab_needs_update(mnt)) { + res = umount2(mnt, lazy ? 2 : 0); + if (res == -1) + fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, + mnt, strerror(errno)); + return res; + } + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return -1; + } + if (res == 0) { + setuid(geteuid()); + execl("/bin/umount", "/bin/umount", "-i", mnt, lazy ? "-l" : NULL, + NULL); + fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname, + strerror(errno)); + exit(1); + } + res = waitpid(res, &status, 0); + if (res == -1) { + fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); + return -1; + } + if (status != 0) + return -1; + + return 0; +} + +char *fuse_mnt_resolve_path(const char *progname, const char *orig) +{ + char buf[PATH_MAX]; + char *copy; + char *dst; + char *end; + char *lastcomp; + const char *toresolv; + + if (!orig[0]) { + fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig); + return NULL; + } + + copy = strdup(orig); + if (copy == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return NULL; + } + + toresolv = copy; + lastcomp = NULL; + for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --); + if (end[0] != '/') { + char *tmp; + end[1] = '\0'; + tmp = strrchr(copy, '/'); + if (tmp == NULL) { + lastcomp = copy; + toresolv = "."; + } else { + lastcomp = tmp + 1; + if (tmp == copy) + toresolv = "/"; + } + if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) { + lastcomp = NULL; + toresolv = copy; + } + else if (tmp) + tmp[0] = '\0'; + } + if (realpath(toresolv, buf) == NULL) { + fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig, + strerror(errno)); + free(copy); + return NULL; + } + if (lastcomp == NULL) + dst = strdup(buf); + else { + dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1); + if (dst) { + unsigned buflen = strlen(buf); + if (buflen && buf[buflen-1] == '/') + sprintf(dst, "%s%s", buf, lastcomp); + else + sprintf(dst, "%s/%s", buf, lastcomp); + } + } + free(copy); + if (dst == NULL) + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return dst; +} + +int fuse_mnt_check_empty(const char *progname, const char *mnt, + mode_t rootmode, off_t rootsize) +{ + int isempty = 1; + + if (S_ISDIR(rootmode)) { + struct dirent *ent; + DIR *dp = opendir(mnt); + if (dp == NULL) { + fprintf(stderr, "%s: failed to open mountpoint for reading: %s\n", + progname, strerror(errno)); + return -1; + } + while ((ent = readdir(dp)) != NULL) { + if (strcmp(ent->d_name, ".") != 0 && + strcmp(ent->d_name, "..") != 0) { + isempty = 0; + break; + } + } + closedir(dp); + } else if (rootsize) + isempty = 0; + + if (!isempty) { + fprintf(stderr, "%s: mountpoint is not empty\n", progname); + fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname); + return -1; + } + return 0; +} + +int fuse_mnt_check_fuseblk(void) +{ + char buf[256]; + FILE *f = fopen("/proc/filesystems", "r"); + if (!f) + return 1; + + while (fgets(buf, sizeof(buf), f)) + if (strstr(buf, "fuseblk\n")) { + fclose(f); + return 1; + } + + fclose(f); + return 0; +} diff --git a/libfuse-lite/mount_util.h b/libfuse-lite/mount_util.h new file mode 100644 index 00000000..29de2beb --- /dev/null +++ b/libfuse-lite/mount_util.h @@ -0,0 +1,17 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include + +int fuse_mnt_add_mount(const char *progname, const char *fsname, + const char *mnt, const char *type, const char *opts); +int fuse_mnt_umount(const char *progname, const char *mnt, int lazy); +char *fuse_mnt_resolve_path(const char *progname, const char *orig); +int fuse_mnt_check_empty(const char *progname, const char *mnt, + mode_t rootmode, off_t rootsize); +int fuse_mnt_check_fuseblk(void); diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index 5dca7cf5..f2df582d 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -1,70 +1,75 @@ -# -# Before making a release, the LTVERSION string should be modified. -# The string is of the form CURRENT:REVISION:AGE. -# -# CURRENT (C) -# The most recent interface number that this library implements. -# -# REVISION (R) -# The implementation number that this library implements. -# -# AGE (A) -# The difference between the newest and oldest interfaces that this -# library implements. In other works, the library implements all the -# interface numbers in the range from number 'CURRENT - AGE' to -# 'CURRENT'. -# -# This means that: -# -# - If interfaces have been changed or added, but binary compatibility has -# been preserved, change to C+1:0:A+1 -# -# - If binary compatibility has been broken (eg removed or changed -# interfaces) change to C+1:0:0 -# -# - If the interface is the same as the previous version, change to C:R+1:A -# -linux_ntfsincludedir = -I$(top_srcdir)/include/ntfs-3g +MAINTAINERCLEANFILES = Makefile.in if INSTALL_LIBRARY -lib_LTLIBRARIES = libntfs-3g.la +rootlib_LTLIBRARIES=#Create directory +lib_LTLIBRARIES = libntfs-3g.la +pkgconfig_DATA = libntfs-3g.pc else noinst_LTLIBRARIES = libntfs-3g.la endif -libntfs_3g_la_LDFLAGS = -version-number $(LIBNTFS_3G_VERSION) -libntfs_3g_la_CFLAGS = $(LIBNTFS_3G_CFLAGS) +libntfs_3g_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g + +libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined + +if FUSE_INTERNAL +libntfs_3g_la_LIBADD = $(top_srcdir)/libfuse-lite/libfuse-lite.la +endif + libntfs_3g_la_SOURCES = \ - attrib.c \ - attrlist.c \ - bitmap.c \ - bootsect.c \ - collate.c \ - compat.c \ - compress.c \ - debug.c \ - device.c \ - device_io.c \ - dir.c \ - index.c \ - inode.c \ - lcnalloc.c \ - logfile.c \ - logging.c \ - mft.c \ - misc.c \ - mst.c \ - runlist.c \ - security.c \ - unistr.c \ - version.c \ + attrib.c \ + attrlist.c \ + bitmap.c \ + bootsect.c \ + collate.c \ + compat.c \ + compress.c \ + debug.c \ + device.c \ + dir.c \ + index.c \ + inode.c \ + lcnalloc.c \ + logfile.c \ + logging.c \ + mft.c \ + misc.c \ + mst.c \ + runlist.c \ + security.c \ + unistr.c \ + version.c \ volume.c -AM_CPPFLAGS = $(linux_ntfsincludedir) $(all_includes) +if NTFS_DEVICE_DEFAULT_IO_OPS +if WINDOWS +libntfs_3g_la_SOURCES += win32_io.c +else +libntfs_3g_la_SOURCES += unix_io.c +endif +endif -EXTRA_DIST = unix_io.c +# We may need to move .so files to root +# And create ldscript or symbolic link from /usr +install-exec-hook: install-rootlibLTLIBRARIES +if INSTALL_LIBRARY + if [ "$(rootlibdir)" != "$(libdir)" ]; then \ + $(MV) -f "$(DESTDIR)/$(libdir)"/libntfs-3g.so* "$(DESTDIR)/$(rootlibdir)"; \ + fi +if GENERATE_LDSCRIPT + if [ "$(rootlibdir)" != "$(libdir)" ]; then \ + $(install_sh_PROGRAM) "libntfs-3g.script.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ + fi +else + if [ "$(rootlibdir)" != "$(libdir)" ]; then \ + $(LN_S) "$(rootlibdir)/libntfs-3g.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ + fi +endif +endif -MAINTAINERCLEANFILES = Makefile.in +uninstall-local: +if INSTALL_LIBRARY + $(RM) -f "$(DESTDIR)/$(rootlibdir)"/libntfs-3g.so* +endif -libs: $(lib_LTLIBRARIES) diff --git a/libntfs-3g/libntfs-3g.pc.in b/libntfs-3g/libntfs-3g.pc.in new file mode 100644 index 00000000..5f2fea73 --- /dev/null +++ b/libntfs-3g/libntfs-3g.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libntfs-3g +Description: NTFS-3G Read/Write Driver Library +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: @LIBFUSE_LITE_LIBS@ -L${libdir} -lntfs-3g diff --git a/libntfs-3g/libntfs-3g.script.so.in b/libntfs-3g/libntfs-3g.script.so.in new file mode 100644 index 00000000..d050cfa3 --- /dev/null +++ b/libntfs-3g/libntfs-3g.script.so.in @@ -0,0 +1,2 @@ +@OUTPUT_FORMAT@ +GROUP ( @rootlibdir@/libntfs-3g.so ) diff --git a/src/Makefile.am b/src/Makefile.am index f7e10dbc..5bc02646 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,39 +1,37 @@ + +MAINTAINERCLEANFILES=\ + Makefile.in + +rootbin_PROGRAMS=\ + ntfs-3g +rootsbin_DATA=#Create directory +man_MANS=\ + ntfs-3g.8 + +ntfs_3g_LDADD =\ + $(top_builddir)/libntfs-3g/libntfs-3g.la if REALLYSTATIC -AM_LIBS = $(top_builddir)/libntfs-3g/.libs/libntfs-3g.a -AM_LFLAGS = -static -STATIC_LINK = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ -else -AM_LIBS = $(top_builddir)/libntfs-3g/libntfs-3g.la -AM_LFLAGS = $(all_libraries) -LIBTOOL_LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +ntfs_3g_LDFLAGS=\ + $(AM_LDFLAGS) \ + -all-static +endif +ntfs_3g_CFLAGS=\ + $(AM_CFLAGS) \ + -DFUSE_USE_VERSION=26 \ + -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_SOURCES=\ + ntfs-3g.c \ + utils.c \ + utils.h +if FUSE_INTERNAL +ntfs_3g_CFLAGS+=\ + -I$(top_srcdir)/include/fuse-lite +else +ntfs_3g_CFLAGS+=\ + $(FUSE_MODULE_CFLAGS) +ntfs_3g_LDADD+=\ + $(FUSE_MODULE_LIBS) endif - -# Workaround to make REALLYSTATIC work with automake 1.5. -LINK=$(STATIC_LINK) $(LIBTOOL_LINK) - -man_MANS = ntfs-3g.8 - -MAINTAINERCLEANFILES = Makefile.in - -linux_ntfsincludedir = -I$(top_srcdir)/include/ntfs - -bin_PROGRAMS = ntfs-3g - -# Set the include path. -AM_CPPFLAGS = -I$(top_srcdir)/include/ntfs-3g $(all_includes) - -ntfs_3g_SOURCES = ntfs-3g.c utils.c utils.h -ntfs_3g_LDADD = $(AM_LIBS) $(FUSE_MODULE_LIBS) $(libfuse_libs) -ntfs_3g_LDFLAGS = $(AM_LFLAGS) -ntfs_3g_CFLAGS = $(FUSE_MODULE_CFLAGS) -DFUSE_USE_VERSION=26 - -# Extra targets - -strip: $(bin_PROGRAMS) - $(STRIP) $^ - -libs: - (cd ../libntfs-3g && $(MAKE) libs) || exit 1; if RUN_LDCONFIG install-exec-hook: @@ -41,14 +39,13 @@ install-exec-hook: endif install-exec-local: - $(INSTALL) -d $(DESTDIR)/sbin - $(LN_S) -f $(bindir)/ntfs-3g $(DESTDIR)/sbin/mount.ntfs-3g + $(INSTALL) -d "$(DESTDIR)$(rootsbindir)" + $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)$(rootsbindir)/mount.ntfs-3g" install-data-local: - $(INSTALL) -d $(DESTDIR)$(man8dir) - $(LN_S) -f ntfs-3g.8 $(DESTDIR)$(man8dir)/mount.ntfs-3g.8 + $(INSTALL) -d "$(DESTDIR)$(man8dir)" + $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" uninstall-local: - $(RM) -f $(DESTDIR)/sbin/mount.ntfs-3g - $(RM) -f $(DESTDIR)$(man8dir)/mount.ntfs-3g.8 - + $(RM) -f "$(DESTDIR)$(rootsbindir)/mount.ntfs-3g" + $(RM) -f "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" From a33787171c00eda04e09d7f210f483fec883e8a8 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 15 Dec 2007 08:56:37 +0000 Subject: [PATCH 054/328] fix: close fuse device before calling umount to prevent deadlock after a SIGTERM if synchronous umount is used (Miklos Szeredi, Szabolcs Szakacsits) --- libfuse-lite/mount.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 739850ff..55bd7a38 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -283,6 +283,7 @@ void fuse_kern_unmount(const char *mountpoint, int fd) if (res == 1 && (pfd.revents & POLLERR)) return; } + close(fd); if (geteuid() == 0) { fuse_mnt_umount("fuse", mountpoint, 1); From 3b01ef27540ef7f3bb81f74a6aa0e639eb1a180e Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 15 Dec 2007 09:04:08 +0000 Subject: [PATCH 055/328] fix: use fuse signal handler, exit at SIGHUP, SIGTERM, SIGINT & ignore SIGPIPE --- src/ntfs-3g.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 1f4dc4f3..d75f3e33 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1648,11 +1648,6 @@ static int ntfs_open(const char *device, char *mntpoint) return 0; } -static void signal_handler(int arg __attribute__((unused))) -{ - fuse_exit((fuse_get_context())->fuse); -} - static char *parse_mount_options(const char *orig_opts) { char *options, *s, *opt, *val, *ret; @@ -2210,6 +2205,12 @@ static struct fuse *mount_fuse(char *parsed_options) if (!fh) goto err; + if (fuse_set_signal_handlers(fuse_get_session(fh))) { + fuse_destroy(fh); + fh = NULL; + goto err; + } + ctx->mounted = TRUE; out: fuse_opt_free_args(&args); @@ -2230,8 +2231,6 @@ int main(int argc, char *argv[]) utils_set_locale(); ntfs_log_set_handler(ntfs_log_handler_stderr); - signal(SIGINT, signal_handler); - signal(SIGTERM, signal_handler); if (parse_options(argc, argv)) { usage(); From fc53b9bcb9909ffbf5bd66d3b2e172dc1f6a2b1b Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 15 Dec 2007 09:10:55 +0000 Subject: [PATCH 056/328] compatibility: use AC_PATH_PROG to find pkg-config instead of PKG_PROG_PKG_CONFIG --- configure.ac | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b657fd7e..9bc832ae 100644 --- a/configure.ac +++ b/configure.ac @@ -192,8 +192,10 @@ if test "${with_fuse}" = "internal"; then [Define to 1 if using internal fuse] ) else - PKG_PROG_PKG_CONFIG - test -z "${PKG_CONFIG}" && AC_MSG_ERROR([pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/]) + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + test "x${PKG_CONFIG}" = "xno" && AC_MSG_ERROR([pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/]) # Libraries often install their metadata .pc files in directories # not searched by pkg-config. Let's workaround this. export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/lib/pkgconfig:/usr/lib/pkgconfig:/opt/gnome/lib/pkgconfig:/usr/share/pkgconfig:/usr/local/lib/pkgconfig:$prefix/lib/pkgconfig:/opt/gnome/share/pkgconfig:/usr/local/share/pkgconfig From a1402b3c3e77588e222b4ce5ebbdd014e1ef72f7 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 15 Dec 2007 09:17:17 +0000 Subject: [PATCH 057/328] new: ntfs-3g exit() value is set according to the type of mount error (e.g. not ntfs, corrupted, access denied, hibernated, unclean journal, etc) --- include/ntfs-3g/volume.h | 17 ++++++++++++++++ libntfs-3g/volume.c | 36 ++++++++++++++++++++++++++++++++++ src/ntfs-3g.c | 42 +++++++++++++++++++++++++++------------- src/utils.c | 42 +++++++++++++++++++++------------------- src/utils.h | 4 +--- 5 files changed, 105 insertions(+), 36 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index c4fb9df5..c00e5d56 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -78,6 +78,21 @@ typedef enum { extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); +typedef enum { + NTFS_VOLUME_OK = 0, + NTFS_VOLUME_SYNTAX_ERROR = 11, + NTFS_VOLUME_NOT_NTFS = 12, + NTFS_VOLUME_CORRUPT = 13, + NTFS_VOLUME_HIBERNATED = 14, + NTFS_VOLUME_UNCLEAN_UNMOUNT = 15, + NTFS_VOLUME_LOCKED = 16, + NTFS_VOLUME_RAID = 17, + NTFS_VOLUME_UNKNOWN_REASON = 18, + NTFS_VOLUME_NO_PRIVILEGE = 19, + NTFS_VOLUME_OUT_OF_MEMORY = 20, + NTFS_VOLUME_FUSE_ERROR = 21 +} ntfs_volume_status; + /** * enum ntfs_volume_state_bits - * @@ -222,5 +237,7 @@ extern int ntfs_logfile_reset(ntfs_volume *vol); extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); +extern int ntfs_volume_error(int err); + #endif /* defined _NTFS_VOLUME_H */ diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 699c2dd2..b90a48e6 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1484,3 +1484,39 @@ err_out: return ret; } +int ntfs_volume_error(int err) +{ + int ret; + + switch (err) { + case 0: + ret = NTFS_VOLUME_OK; + break; + case EINVAL: + ret = NTFS_VOLUME_NOT_NTFS; + break; + case EIO: + ret = NTFS_VOLUME_CORRUPT; + break; + case EPERM: + ret = NTFS_VOLUME_HIBERNATED; + break; + case EOPNOTSUPP: + ret = NTFS_VOLUME_UNCLEAN_UNMOUNT; + break; + case EBUSY: + ret = NTFS_VOLUME_LOCKED; + break; + case ENXIO: + ret = NTFS_VOLUME_RAID; + break; + case EACCES: + ret = NTFS_VOLUME_NO_PRIVILEGE; + break; + default: + ret = NTFS_VOLUME_UNKNOWN_REASON; + break; + } + return ret; +} + diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index d75f3e33..a59b6612 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1629,23 +1629,28 @@ static int ntfs_open(const char *device, char *mntpoint) if (ctx->force) flags |= MS_FORCE; - ctx->vol = utils_mount_volume(device, mntpoint, flags); - if (!ctx->vol) - return -1; + 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"); - return -1; + 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"); - return -1; + goto err_out; } + + errno = 0; +err_out: + return ntfs_volume_error(errno); - return 0; } static char *parse_mount_options(const char *orig_opts) @@ -2227,28 +2232,31 @@ int main(int argc, char *argv[]) fuse_fstype fstype = FSTYPE_UNKNOWN; struct stat sbuf; uid_t uid, euid; - int err = 10; + int err; utils_set_locale(); ntfs_log_set_handler(ntfs_log_handler_stderr); if (parse_options(argc, argv)) { usage(); - return 1; + return NTFS_VOLUME_SYNTAX_ERROR; } if (ntfs_fuse_init()) - return 2; + return NTFS_VOLUME_OUT_OF_MEMORY; parsed_options = parse_mount_options(opts.options ? opts.options : ""); - if (!parsed_options) + if (!parsed_options) { + err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; + } uid = getuid(); euid = geteuid(); if (setuid(euid)) { ntfs_log_perror("Failed to set user ID to %d", euid); + err = NTFS_VOLUME_NO_PRIVILEGE; goto err_out; } @@ -2262,13 +2270,15 @@ int main(int argc, char *argv[]) if (stat(opts.device, &sbuf)) { ntfs_log_perror("Failed to access '%s'", opts.device); + err = NTFS_VOLUME_NO_PRIVILEGE; goto err_out; } /* Always use fuseblk for block devices unless it's surely missing. */ if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) ctx->blkdev = TRUE; - if (ntfs_open(opts.device, opts.mnt_point)) + err = ntfs_open(opts.device, opts.mnt_point); + if (err) goto err_out; if (ctx->blkdev) { @@ -2278,14 +2288,19 @@ int main(int argc, char *argv[]) } fh = mount_fuse(parsed_options); - if (!fh) + if (!fh) { + err = NTFS_VOLUME_FUSE_ERROR; goto err_out; + } if (setuid(uid)) { - ntfs_log_perror("Failed to set user ID to %d", uid); + ntfs_log_perror("Failed to drop privilege (uid to %d)", uid); + err = NTFS_VOLUME_NO_PRIVILEGE; goto err_umount; } + ctx->mounted = TRUE; + #if defined(linux) || defined(__uClinux__) if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) ntfs_log_info(fuse26_kmod_msg); @@ -2317,6 +2332,7 @@ err_umount: fuse_unmount(opts.mnt_point, ctx->fc); fuse_destroy(fh); err_out: + utils_mount_error(opts.device, opts.mnt_point, err); ntfs_close(); free(ctx); free(parsed_options); diff --git a/src/utils.c b/src/utils.c index 2b14f74d..ce93a5fb 100644 --- a/src/utils.c +++ b/src/utils.c @@ -84,6 +84,10 @@ static const char *fakeraid_msg = "different device under /dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1)\n" "to mount NTFS. Please see the 'dmraid' documentation for help.\n"; +static const char *access_denied_msg = +"Please check the volume and the NTFS-3G binary permissions, the mounting\n" +"user and group ID, and the mount options.\n"; + static const char *forced_mount_msg = "\n" " mount -t ntfs-3g %s %s -o force\n" @@ -109,34 +113,32 @@ int utils_set_locale(void) return 0; } -ntfs_volume *utils_mount_volume(const char *volume, const char *mntpoint, - unsigned long flags) +void utils_mount_error(const char *volume, const char *mntpoint, int err) { - ntfs_volume *vol; - - vol = ntfs_mount(volume, flags); - if (!vol) { - - ntfs_log_perror("Failed to mount '%s'", volume); - - if (errno == EINVAL) + switch (err) { + case NTFS_VOLUME_NOT_NTFS: ntfs_log_error(invalid_ntfs_msg, volume); - else if (errno == EIO) + break; + case NTFS_VOLUME_CORRUPT: ntfs_log_error("%s", corrupt_volume_msg); - else if (errno == EPERM) + break; + case NTFS_VOLUME_HIBERNATED: ntfs_log_error("%s", hibernated_volume_msg); - else if (errno == EOPNOTSUPP) { + break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: ntfs_log_error(unclean_journal_msg); - ntfs_log_error(forced_mount_msg, volume, mntpoint, + ntfs_log_error(forced_mount_msg, volume, mntpoint, volume, mntpoint); - } else if (errno == EBUSY) + break; + case NTFS_VOLUME_LOCKED: ntfs_log_error("%s", opened_volume_msg); - else if (errno == ENXIO) + break; + case NTFS_VOLUME_RAID: ntfs_log_error("%s", fakeraid_msg); - - return NULL; + break; + case NTFS_VOLUME_NO_PRIVILEGE: + ntfs_log_error(access_denied_msg, volume); + break; } - - return vol; } diff --git a/src/utils.h b/src/utils.h index 400d465f..2d47ca4b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,8 +32,6 @@ extern const char *ntfs_home; extern const char *ntfs_gpl; int utils_set_locale(void); - -ntfs_volume *utils_mount_volume(const char *device, const char *mntpoint, - unsigned long flags); +void utils_mount_error(const char *vol, const char *mntpoint, int err); #endif /* _NTFS_UTILS_H_ */ From 4090a07081122e2027c6aa84ec8235f5c0c54cd8 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 15 Dec 2007 09:27:00 +0000 Subject: [PATCH 058/328] add ntfs-3g.probe utility which probes read-only or read-write mountability --- src/Makefile.am | 57 ++++++++------- src/ntfs-3g.probe.c | 166 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 29 deletions(-) create mode 100644 src/ntfs-3g.probe.c diff --git a/src/Makefile.am b/src/Makefile.am index 5bc02646..fc74bbdf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,38 +1,37 @@ -MAINTAINERCLEANFILES=\ - Makefile.in +MAINTAINERCLEANFILES = Makefile.in -rootbin_PROGRAMS=\ - ntfs-3g -rootsbin_DATA=#Create directory -man_MANS=\ - ntfs-3g.8 - -ntfs_3g_LDADD =\ - $(top_builddir)/libntfs-3g/libntfs-3g.la -if REALLYSTATIC -ntfs_3g_LDFLAGS=\ - $(AM_LDFLAGS) \ - -all-static -endif -ntfs_3g_CFLAGS=\ - $(AM_CFLAGS) \ - -DFUSE_USE_VERSION=26 \ - -I$(top_srcdir)/include/ntfs-3g -ntfs_3g_SOURCES=\ - ntfs-3g.c \ - utils.c \ - utils.h if FUSE_INTERNAL -ntfs_3g_CFLAGS+=\ - -I$(top_srcdir)/include/fuse-lite +FUSE_CFLAGS = -I$(top_srcdir)/include/fuse-lite +FUSE_LIBS = else -ntfs_3g_CFLAGS+=\ - $(FUSE_MODULE_CFLAGS) -ntfs_3g_LDADD+=\ - $(FUSE_MODULE_LIBS) +FUSE_CFLAGS = $(FUSE_MODULE_CFLAGS) +FUSE_LIBS = $(FUSE_MODULE_LIBS) endif +bin_PROGRAMS = ntfs-3g.probe +rootbin_PROGRAMS = ntfs-3g +rootsbin_DATA = #Create directory +man_MANS = ntfs-3g.8 + +ntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la +if REALLYSTATIC +ntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static +endif +ntfs_3g_CFLAGS = \ + $(AM_CFLAGS) \ + -DFUSE_USE_VERSION=26 \ + $(FUSE_CFLAGS) \ + -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_SOURCES = ntfs-3g.c utils.c utils.h + +ntfs_3g_probe_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +if REALLYSTATIC +ntfs_3g_probe_LDFLAGS = $(AM_LDFLAGS) -all-static +endif +ntfs_3g_probe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_probe_SOURCES = ntfs-3g.probe.c utils.c utils.h + if RUN_LDCONFIG install-exec-hook: $(LDCONFIG) diff --git a/src/ntfs-3g.probe.c b/src/ntfs-3g.probe.c new file mode 100644 index 00000000..a4088038 --- /dev/null +++ b/src/ntfs-3g.probe.c @@ -0,0 +1,166 @@ +/** + * ntfs-3g.probe - Probe NTFS volume mountability + * + * Copyright (c) 2007 Szabolcs Szakacsits + * + * 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" + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#include + +#include "volume.h" +#include "utils.h" +#include "misc.h" + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +typedef enum { + PROBE_UNSET, + PROBE_READONLY, + PROBE_READWRITE +} probe_t; + +static struct options { + probe_t probetype; + char *device; +} opts; + +static const char *EXEC_NAME = "ntfs-3g.probe"; + +static const char *usage_msg = +"\n" +"%s %s - Probe NTFS volume mountability\n" +"\n" +"Copyright (C) 2007 Szabolcs Szakacsits\n" +"\n" +"Usage: %s <--readonly|--readwrite> \n" +"\n" +"Example: ntfs-3g.probe --readwrite /dev/sda1\n" +"\n" +"%s"; + +static int ntfs_open(const char *device) +{ + ntfs_volume *vol; + unsigned long flags = 0; + int ret = NTFS_VOLUME_OK; + + if (opts.probetype == PROBE_READONLY) + flags |= MS_RDONLY; + + vol = ntfs_mount(device, flags); + if (!vol) + ret = ntfs_volume_error(errno); + + ntfs_umount(vol, FALSE); + + return ret; +} + +static void usage(void) +{ + ntfs_log_info(usage_msg, EXEC_NAME, VERSION, EXEC_NAME, ntfs_home); +} + +static int parse_options(int argc, char *argv[]) +{ + int c; + + static const char *sopt = "-hrw"; + static const struct option lopt[] = { + { "readonly", no_argument, NULL, 'r' }, + { "readwrite", no_argument, NULL, 'w' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + opterr = 0; /* We handle errors. */ + opts.probetype = PROBE_UNSET; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = ntfs_malloc(PATH_MAX + 1); + if (!opts.device) + return -1; + + strcpy(opts.device, optarg); + } else { + ntfs_log_error("%s: You must specify exactly " + "one device\n", EXEC_NAME); + return -1; + } + break; + case 'h': + usage(); + exit(0); + case 'r': + opts.probetype = PROBE_READONLY; + break; + case 'w': + opts.probetype = PROBE_READWRITE; + break; + default: + ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME, + argv[optind - 1]); + return -1; + } + } + + if (!opts.device) { + ntfs_log_error("ERROR: %s: Device is missing\n", EXEC_NAME); + return -1; + } + + if (opts.probetype == PROBE_UNSET) { + ntfs_log_error("ERROR: %s: Probe type is missing\n", EXEC_NAME); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int err; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + if (parse_options(argc, argv)) { + usage(); + exit(NTFS_VOLUME_SYNTAX_ERROR); + } + + err = ntfs_open(opts.device); + + free(opts.device); + exit(err); +} + From f101412c2309ceca1fa965572c2913830975863b Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 15 Dec 2007 09:36:12 +0000 Subject: [PATCH 059/328] display "Unmounting ..." only if no error during the entire mount phase --- src/ntfs-3g.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a59b6612..9f302e11 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2215,8 +2215,6 @@ static struct fuse *mount_fuse(char *parsed_options) fh = NULL; goto err; } - - ctx->mounted = TRUE; out: fuse_opt_free_args(&args); return fh; From 9c0f8eef728bba03e62aee7c76869ae10633e5dc Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 19 Jan 2008 22:55:04 +0000 Subject: [PATCH 060/328] new: support user mount via setuid root ntfs-3g (fusermount isn't required) --- libfuse-lite/Makefile.am | 1 + libfuse-lite/fusermount.c | 793 ++++++++++++++++++++++++++++++++++++++ libfuse-lite/mount.c | 170 +------- libfuse-lite/mount_util.h | 4 + src/ntfs-3g.c | 63 +-- src/utils.c | 5 +- 6 files changed, 849 insertions(+), 187 deletions(-) create mode 100644 libfuse-lite/fusermount.c diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am index f1455544..986f7a3e 100644 --- a/libfuse-lite/Makefile.am +++ b/libfuse-lite/Makefile.am @@ -26,6 +26,7 @@ libfuse_lite_la_SOURCES = \ fuse_opt.c \ fuse_session.c \ fuse_signals.c \ + fusermount.c \ helper.c \ mount.c \ mount_util.c \ diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c new file mode 100644 index 00000000..290b47c3 --- /dev/null +++ b/libfuse-lite/fusermount.c @@ -0,0 +1,793 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ +/* This program does the mounting and unmounting of FUSE filesystems */ + +#include + +#include "mount_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUSE_DEV_OLD "/proc/fs/fuse/dev" +#define FUSE_DEV_NEW "/dev/fuse" +#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version" +#define FUSE_CONF "/etc/fuse.conf" + +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + +static const char *progname = "ntfs-3g-mount"; + +static int user_allow_other = 0; +static int mount_max = 1000; + +static const char *get_user_name(void) +{ + struct passwd *pw = getpwuid(getuid()); + if (pw != NULL && pw->pw_name != NULL) + return pw->pw_name; + else { + fprintf(stderr, "%s: could not determine username\n", progname); + return NULL; + } +} + +static uid_t oldfsuid; +static gid_t oldfsgid; + +static void drop_privs(void) +{ + if (getuid() != 0) { + oldfsuid = setfsuid(getuid()); + oldfsgid = setfsgid(getgid()); + } +} + +static void restore_privs(void) +{ + if (getuid() != 0) { + setfsuid(oldfsuid); + setfsgid(oldfsgid); + } +} + +#ifndef IGNORE_MTAB +static int add_mount(const char *source, const char *mnt, const char *type, + const char *opts) +{ + return fuse_mnt_add_mount(progname, source, mnt, type, opts); +} + +static int unmount_fuse(const char *mnt, int quiet, int lazy) +{ + if (getuid() != 0) { + struct mntent *entp; + FILE *fp; + const char *user = NULL; + char uidstr[32]; + unsigned uidlen = 0; + int found; + const char *mtab = _PATH_MOUNTED; + + user = get_user_name(); + if (user == NULL) + return -1; + + fp = setmntent(mtab, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, + strerror(errno)); + return -1; + } + + uidlen = sprintf(uidstr, "%u", getuid()); + + found = 0; + while ((entp = getmntent(fp)) != NULL) { + if (!found && strcmp(entp->mnt_dir, mnt) == 0 && + (strcmp(entp->mnt_type, "fuse") == 0 || + strcmp(entp->mnt_type, "fuseblk") == 0 || + strncmp(entp->mnt_type, "fuse.", 5) == 0 || + strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) { + char *p = strstr(entp->mnt_opts, "user="); + if (p && (p == entp->mnt_opts || *(p-1) == ',') && + strcmp(p + 5, user) == 0) { + found = 1; + break; + } + /* /etc/mtab is a link pointing to /proc/mounts: */ + else if ((p = strstr(entp->mnt_opts, "user_id=")) && + (p == entp->mnt_opts || *(p-1) == ',') && + strncmp(p + 8, uidstr, uidlen) == 0 && + (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0')) { + found = 1; + break; + } + } + } + endmntent(fp); + + if (!found) { + if (!quiet) + fprintf(stderr, "%s: entry for %s not found in %s\n", progname, + mnt, mtab); + return -1; + } + } + + return fuse_mnt_umount(progname, mnt, lazy); +} + +static int count_fuse_fs(void) +{ + struct mntent *entp; + int count = 0; + const char *mtab = _PATH_MOUNTED; + FILE *fp = setmntent(mtab, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, + strerror(errno)); + return -1; + } + while ((entp = getmntent(fp)) != NULL) { + if (strcmp(entp->mnt_type, "fuse") == 0 || + strncmp(entp->mnt_type, "fuse.", 5) == 0) + count ++; + } + endmntent(fp); + return count; +} + + +#else /* IGNORE_MTAB */ +static int count_fuse_fs() +{ + return 0; +} + +static int add_mount(const char *source, const char *mnt, const char *type, + const char *opts) +{ + (void) source; + (void) mnt; + (void) type; + (void) opts; + return 0; +} + +static int unmount_fuse(const char *mnt, int quiet, int lazy) +{ + return fuse_mnt_umount(progname, mnt, lazy); +} +#endif /* IGNORE_MTAB */ + +static void strip_line(char *line) +{ + char *s = strchr(line, '#'); + if (s != NULL) + s[0] = '\0'; + for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--); + s[1] = '\0'; + for (s = line; isspace((unsigned char) *s); s++); + if (s != line) + memmove(line, s, strlen(s)+1); +} + +static void parse_line(char *line, int linenum) +{ + int tmp; + if (strcmp(line, "user_allow_other") == 0) + user_allow_other = 1; + else if (sscanf(line, "mount_max = %i", &tmp) == 1) + mount_max = tmp; + else if(line[0]) + fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n", + progname, FUSE_CONF, linenum, line); +} + +static void read_conf(void) +{ + FILE *fp = fopen(FUSE_CONF, "r"); + if (fp != NULL) { + int linenum = 1; + char line[256]; + int isnewline = 1; + while (fgets(line, sizeof(line), fp) != NULL) { + if (isnewline) { + if (line[strlen(line)-1] == '\n') { + strip_line(line); + parse_line(line, linenum); + } else { + fprintf(stderr, "%s: reading %s: line %i too long\n", + progname, FUSE_CONF, linenum); + isnewline = 0; + } + } else if(line[strlen(line)-1] == '\n') + isnewline = 1; + if (isnewline) + linenum ++; + } + fclose(fp); + } else if (errno != ENOENT) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF, + strerror(errno)); + } +} + +static int begins_with(const char *s, const char *beg) +{ + if (strncmp(s, beg, strlen(beg)) == 0) + return 1; + else + return 0; +} + +struct mount_flags { + const char *opt; + unsigned long flag; + int on; + int safe; +}; + +static struct mount_flags mount_flags[] = { + {"rw", MS_RDONLY, 0, 1}, + {"ro", MS_RDONLY, 1, 1}, + {"suid", MS_NOSUID, 0, 0}, + {"nosuid", MS_NOSUID, 1, 1}, + {"dev", MS_NODEV, 0, 0}, + {"nodev", MS_NODEV, 1, 1}, + {"exec", MS_NOEXEC, 0, 1}, + {"noexec", MS_NOEXEC, 1, 1}, + {"async", MS_SYNCHRONOUS, 0, 1}, + {"sync", MS_SYNCHRONOUS, 1, 1}, + {"atime", MS_NOATIME, 0, 1}, + {"noatime", MS_NOATIME, 1, 1}, + {"dirsync", MS_DIRSYNC, 1, 1}, + {NULL, 0, 0, 0} +}; + +static int find_mount_flag(const char *s, unsigned len, int *on, int *flag) +{ + int i; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + const char *opt = mount_flags[i].opt; + if (strlen(opt) == len && strncmp(opt, s, len) == 0) { + *on = mount_flags[i].on; + *flag = mount_flags[i].flag; + if (!mount_flags[i].safe && getuid() != 0) { + *flag = 0; + fprintf(stderr, "%s: unsafe option %s ignored\n", + progname, opt); + } + return 1; + } + } + return 0; +} + +static int add_option(char **optsp, const char *opt, unsigned expand) +{ + char *newopts; + if (*optsp == NULL) + newopts = strdup(opt); + else { + unsigned oldsize = strlen(*optsp); + unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1; + newopts = (char *) realloc(*optsp, newsize); + if (newopts) + sprintf(newopts + oldsize, ",%s", opt); + } + if (newopts == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + *optsp = newopts; + return 0; +} + +static int get_mnt_opts(int flags, char *opts, char **mnt_optsp) +{ + int i; + int l; + + if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1) + return -1; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + if (mount_flags[i].on && (flags & mount_flags[i].flag) && + add_option(mnt_optsp, mount_flags[i].opt, 0) == -1) + return -1; + } + + if (add_option(mnt_optsp, opts, 0) == -1) + return -1; + /* remove comma from end of opts*/ + l = strlen(*mnt_optsp); + if ((*mnt_optsp)[l-1] == ',') + (*mnt_optsp)[l-1] = '\0'; + if (getuid() != 0) { + const char *user = get_user_name(); + if (user == NULL) + return -1; + + if (add_option(mnt_optsp, "user=", strlen(user)) == -1) + return -1; + strcat(*mnt_optsp, user); + } + return 0; +} + +static int opt_eq(const char *s, unsigned len, const char *opt) +{ + if(strlen(opt) == len && strncmp(s, opt, len) == 0) + return 1; + else + return 0; +} + +static int get_string_opt(const char *s, unsigned len, const char *opt, + char **val) +{ + unsigned opt_len = strlen(opt); + + if (*val) + free(*val); + *val = (char *) malloc(len - opt_len + 1); + if (!*val) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return 0; + } + + memcpy(*val, s + opt_len, len - opt_len); + (*val)[len - opt_len] = '\0'; + return 1; +} + +static int do_mount(const char *mnt, char **typep, mode_t rootmode, + int fd, const char *opts, const char *dev, char **sourcep, + char **mnt_optsp, off_t rootsize) +{ + int res; + int flags = MS_NOSUID | MS_NODEV; + char *optbuf; + char *mnt_opts = NULL; + const char *s; + char *d; + char *fsname = NULL; + char *subtype = NULL; + char *source = NULL; + char *type = NULL; + int check_empty = 1; + int blkdev = 0; + + optbuf = (char *) malloc(strlen(opts) + 128); + if (!optbuf) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + + for (s = opts, d = optbuf; *s;) { + unsigned len; + const char *fsname_str = "fsname="; + const char *subtype_str = "subtype="; + for (len = 0; s[len] && s[len] != ','; len++); + if (begins_with(s, fsname_str)) { + if (!get_string_opt(s, len, fsname_str, &fsname)) + goto err; + } else if (begins_with(s, subtype_str)) { + if (!get_string_opt(s, len, subtype_str, &subtype)) + goto err; + } else if (opt_eq(s, len, "blkdev")) { + if (getuid() != 0) { + fprintf(stderr, "%s: option blkdev is privileged\n", progname); + goto err; + } + blkdev = 1; + } else if (opt_eq(s, len, "nonempty")) { + check_empty = 0; + } else if (!begins_with(s, "fd=") && + !begins_with(s, "rootmode=") && + !begins_with(s, "user_id=") && + !begins_with(s, "group_id=")) { + int on; + int flag; + int skip_option = 0; + if (opt_eq(s, len, "large_read")) { + struct utsname utsname; + unsigned kmaj, kmin; + res = uname(&utsname); + if (res == 0 && + sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 && + (kmaj > 2 || (kmaj == 2 && kmin > 4))) { + fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin); + skip_option = 1; + } + } + if (getuid() != 0 && !user_allow_other && + (opt_eq(s, len, "allow_other") || + opt_eq(s, len, "allow_root"))) { + fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s); + goto err; + } + if (!skip_option) { + if (find_mount_flag(s, len, &on, &flag)) { + if (on) + flags |= flag; + else + flags &= ~flag; + } else { + memcpy(d, s, len); + d += len; + *d++ = ','; + } + } + } + s += len; + if (*s) + s++; + } + *d = '\0'; + res = get_mnt_opts(flags, optbuf, &mnt_opts); + if (res == -1) + goto err; + + sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i", + fd, rootmode, getuid(), getgid()); + + if (check_empty && + fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1) + goto err; + + source = malloc((fsname ? strlen(fsname) : 0) + + (subtype ? strlen(subtype) : 0) + strlen(dev) + 32); + + type = malloc((subtype ? strlen(subtype) : 0) + 32); + if (!type || !source) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + goto err; + } + + if (subtype) + sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype); + else + strcpy(type, blkdev ? "fuseblk" : "fuse"); + + if (fsname) + strcpy(source, fsname); + else + strcpy(source, subtype ? subtype : dev); + + res = mount(source, mnt, type, flags, optbuf); + if (res == -1 && errno == ENODEV && subtype) { + /* Probably missing subtype support */ + strcpy(type, blkdev ? "fuseblk" : "fuse"); + if (fsname) { + if (!blkdev) + sprintf(source, "%s#%s", subtype, fsname); + } else { + strcpy(source, type); + } + + res = mount(source, mnt, type, flags, optbuf); + } + if (res == -1 && errno == EINVAL) { + /* It could be an old version not supporting group_id */ + sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid()); + res = mount(source, mnt, type, flags, optbuf); + } + if (res == -1) { + int errno_save = errno; + if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) + fprintf(stderr, "%s: 'fuseblk' support missing\n", progname); + else + fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save)); + goto err; + } else { + *sourcep = source; + *typep = type; + *mnt_optsp = mnt_opts; + } + free(optbuf); + + return res; + + err: + free(fsname); + free(subtype); + free(source); + free(type); + free(mnt_opts); + free(optbuf); + return -1; +} + +static int check_version(const char *dev) +{ + int res; + int majorver; + int minorver; + const char *version_file; + FILE *vf; + + if (strcmp(dev, FUSE_DEV_OLD) != 0) + return 0; + + version_file = FUSE_VERSION_FILE_OLD; + vf = fopen(version_file, "r"); + if (vf == NULL) { + fprintf(stderr, "%s: kernel interface too old\n", progname); + return -1; + } + res = fscanf(vf, "%i.%i", &majorver, &minorver); + fclose(vf); + if (res != 2) { + fprintf(stderr, "%s: error reading %s\n", progname, version_file); + return -1; + } + if (majorver < 3) { + fprintf(stderr, "%s: kernel interface too old\n", progname); + return -1; + } + return 0; +} + +static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, + int *mountpoint_fd) +{ + int res; + const char *mnt = *mntp; + const char *origmnt = mnt; + + res = lstat(mnt, stbuf); + if (res == -1) { + fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", + progname, mnt, strerror(errno)); + return -1; + } + + /* No permission checking is done for root */ + if (getuid() == 0) + return 0; + + if (S_ISDIR(stbuf->st_mode)) { + *currdir_fd = open(".", O_RDONLY); + if (*currdir_fd == -1) { + fprintf(stderr, "%s: failed to open current directory: %s\n", + progname, strerror(errno)); + return -1; + } + res = chdir(mnt); + if (res == -1) { + fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n", + progname, strerror(errno)); + return -1; + } + mnt = *mntp = "."; + res = lstat(mnt, stbuf); + if (res == -1) { + fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", + progname, origmnt, strerror(errno)); + return -1; + } + + if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) { + fprintf(stderr, "%s: mountpoint %s not owned by user\n", + progname, origmnt); + return -1; + } + + res = access(mnt, W_OK); + if (res == -1) { + fprintf(stderr, "%s: user has no write access to mountpoint %s\n", + progname, origmnt); + return -1; + } + } else if (S_ISREG(stbuf->st_mode)) { + static char procfile[256]; + *mountpoint_fd = open(mnt, O_WRONLY); + if (*mountpoint_fd == -1) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt, + strerror(errno)); + return -1; + } + res = fstat(*mountpoint_fd, stbuf); + if (res == -1) { + fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", + progname, mnt, strerror(errno)); + return -1; + } + if (!S_ISREG(stbuf->st_mode)) { + fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n", + progname, mnt); + return -1; + } + + sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd); + *mntp = procfile; + } else { + fprintf(stderr, + "%s: mountpoint %s is not a directory or a regular file\n", + progname, mnt); + return -1; + } + + + return 0; +} + +static int try_open(const char *dev, char **devp, int silent) +{ + int fd = open(dev, O_RDWR); + if (fd != -1) { + *devp = strdup(dev); + if (*devp == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + close(fd); + fd = -1; + } + } else if (errno == ENODEV || + errno == ENOENT) /* check for ENOENT too, for the udev case */ + return -2; + else if (!silent) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev, + strerror(errno)); + } + return fd; +} + +static int try_open_fuse_device(char **devp) +{ + int fd; + int err; + + drop_privs(); + fd = try_open(FUSE_DEV_NEW, devp, 0); + restore_privs(); + if (fd >= 0) + return fd; + + err = fd; + fd = try_open(FUSE_DEV_OLD, devp, 1); + if (fd >= 0) + return fd; + + return err; +} + +static int open_fuse_device(char **devp) +{ + int fd = try_open_fuse_device(devp); + if (fd >= -1) + return fd; + + fprintf(stderr, "%s: fuse device not found, try 'modprobe fuse' first\n", + progname); + + return -1; +} + + +static int mount_fuse(const char *mnt, const char *opts) +{ + int res; + int fd; + char *dev; + struct stat stbuf; + char *type = NULL; + char *source = NULL; + char *mnt_opts = NULL; + const char *real_mnt = mnt; + int currdir_fd = -1; + int mountpoint_fd = -1; + + fd = open_fuse_device(&dev); + if (fd == -1) + return -1; + + drop_privs(); + read_conf(); + + if (getuid() != 0 && mount_max != -1) { + int mount_count = count_fuse_fs(); + if (mount_count >= mount_max) { + fprintf(stderr, "%s: too many FUSE filesystems mounted; " + "mount_max=N can be set in /etc/fuse.conf\n", progname); + close(fd); + return -1; + } + } + + res = check_version(dev); + if (res != -1) { + res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd); + restore_privs(); + if (res != -1) + res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, + dev, &source, &mnt_opts, stbuf.st_size); + } else + restore_privs(); + + if (currdir_fd != -1) { + fchdir(currdir_fd); + close(currdir_fd); + } + if (mountpoint_fd != -1) + close(mountpoint_fd); + + if (res == -1) { + close(fd); + return -1; + } + + if (geteuid() == 0) { + res = add_mount(source, mnt, type, mnt_opts); + if (res == -1) { + umount2(mnt, 2); /* lazy umount */ + close(fd); + return -1; + } + } + + free(source); + free(type); + free(mnt_opts); + free(dev); + + return fd; +} + +int fusermount(int unmount, int quiet, int lazy, const char *opts, + const char *origmnt) +{ + int res; + char *mnt; + mode_t old_umask; + + if (lazy && !unmount) { + fprintf(stderr, "%s: -z can only be used with -u\n", progname); + return -1; + } + + drop_privs(); + mnt = fuse_mnt_resolve_path(progname, origmnt); + restore_privs(); + if (mnt == NULL) + return -1; + + old_umask = umask(033); + if (unmount) { + if (geteuid() == 0) + res = unmount_fuse(mnt, quiet, lazy); + else { + res = umount2(mnt, lazy ? 2 : 0); + if (res == -1 && !quiet) + fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, + mnt, strerror(errno)); + } + } else + res = mount_fuse(mnt, opts); + + umask(old_umask); + return res; +} diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 55bd7a38..2c95e5eb 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -23,13 +23,6 @@ #include #include -#define FUSERMOUNT_PROG "fusermount" -#define FUSE_COMMFD_ENV "_FUSE_COMMFD" - -#ifndef HAVE_FORK -#define fork() vfork() -#endif - #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 #endif @@ -103,38 +96,6 @@ static const struct fuse_opt fuse_mount_opts[] = { FUSE_OPT_END }; -static void mount_help(void) -{ - fprintf(stderr, - " -o allow_other allow access to other users\n" - " -o allow_root allow access to root\n" - " -o nonempty allow mounts over non-empty file/dir\n" - " -o default_permissions enable permission checking by kernel\n" - " -o fsname=NAME set filesystem name\n" - " -o subtype=NAME set filesystem type\n" - " -o large_read issue large read requests (2.4 only)\n" - " -o max_read=N set maximum size of read requests\n" - "\n" - ); -} - -static void exec_fusermount(const char *argv[]) -{ - execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv); - execvp(FUSERMOUNT_PROG, (char **) argv); -} - -static void mount_version(void) -{ - int pid = fork(); - if (!pid) { - const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL }; - exec_fusermount(argv); - _exit(1); - } else if (pid != -1) - waitpid(pid, NULL, 0); -} - struct mount_flags { const char *opt; unsigned long flag; @@ -208,66 +169,19 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key, return fuse_opt_add_opt(&mo->mtab_opts, arg); case KEY_HELP: - mount_help(); mo->ishelp = 1; break; case KEY_VERSION: - mount_version(); mo->ishelp = 1; break; } return 1; } -/* return value: - * >= 0 => fd - * -1 => error - */ -static int receive_fd(int fd) -{ - struct msghdr msg; - struct iovec iov; - char buf[1]; - int rv; - size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; - struct cmsghdr *cmsg; - - iov.iov_base = buf; - iov.iov_len = 1; - - msg.msg_name = 0; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - /* old BSD implementations should use msg_accrights instead of - * msg_control; the interface is different. */ - msg.msg_control = ccmsg; - msg.msg_controllen = sizeof(ccmsg); - - while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); - if (rv == -1) { - perror("recvmsg"); - return -1; - } - if(!rv) { - /* EOF */ - return -1; - } - - cmsg = CMSG_FIRSTHDR(&msg); - if (!cmsg->cmsg_type == SCM_RIGHTS) { - fprintf(stderr, "got control message of unknown type %d\n", - cmsg->cmsg_type); - return -1; - } - return *(int*)CMSG_DATA(cmsg); -} - void fuse_kern_unmount(const char *mountpoint, int fd) { int res; - int pid; if (!mountpoint) return; @@ -294,81 +208,17 @@ void fuse_kern_unmount(const char *mountpoint, int fd) if (res == 0) return; - pid = fork(); - if(pid == -1) - return; - - if(pid == 0) { - const char *argv[] = - { FUSERMOUNT_PROG, "-u", "-q", "-z", "--", mountpoint, NULL }; - - exec_fusermount(argv); - _exit(1); - } - waitpid(pid, NULL, 0); + fusermount(1, 0, 1, "", mountpoint); } -static int fuse_mount_fusermount(const char *mountpoint, const char *opts, - int quiet) +static int fuse_mount_fusermount(const char *mountpoint, const char *opts) { - int fds[2], pid; - int res; - int rv; + if (!mountpoint) { + fprintf(stderr, "fuse: missing mountpoint\n"); + return -1; + } - if (!mountpoint) { - fprintf(stderr, "fuse: missing mountpoint\n"); - return -1; - } - - res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); - if(res == -1) { - perror("fuse: socketpair() failed"); - return -1; - } - - pid = fork(); - if(pid == -1) { - perror("fuse: fork() failed"); - close(fds[0]); - close(fds[1]); - return -1; - } - - if(pid == 0) { - char env[10]; - const char *argv[32]; - int a = 0; - - if (quiet) { - int fd = open("/dev/null", O_RDONLY); - dup2(fd, 1); - dup2(fd, 2); - } - - argv[a++] = FUSERMOUNT_PROG; - if (opts) { - argv[a++] = "-o"; - argv[a++] = opts; - } - argv[a++] = "--"; - argv[a++] = mountpoint; - argv[a++] = NULL; - - close(fds[1]); - fcntl(fds[0], F_SETFD, 0); - snprintf(env, sizeof(env), "%i", fds[0]); - setenv(FUSE_COMMFD_ENV, env, 1); - exec_fusermount(argv); - perror("fuse: failed to exec fusermount"); - _exit(1); - } - - close(fds[0]); - rv = receive_fd(fds[1]); - close(fds[1]); - waitpid(pid, NULL, 0); /* bury zombie */ - - return rv; + return fusermount(0, 0, 0, opts ? opts : "", mountpoint); } static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, @@ -550,12 +400,12 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) goto out; } - res = fuse_mount_fusermount(mountpoint, tmp_opts, 1); + res = fuse_mount_fusermount(mountpoint, tmp_opts); free(tmp_opts); if (res == -1) - res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + res = fuse_mount_fusermount(mountpoint, mnt_opts); } else { - res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + res = fuse_mount_fusermount(mountpoint, mnt_opts); } } out: diff --git a/libfuse-lite/mount_util.h b/libfuse-lite/mount_util.h index 29de2beb..b7350213 100644 --- a/libfuse-lite/mount_util.h +++ b/libfuse-lite/mount_util.h @@ -15,3 +15,7 @@ char *fuse_mnt_resolve_path(const char *progname, const char *orig); int fuse_mnt_check_empty(const char *progname, const char *mnt, mode_t rootmode, off_t rootsize); int fuse_mnt_check_fuseblk(void); + +int fusermount(int unmount, int quiet, int lazy, const char *opts, + const char *origmnt); + diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 9f302e11..8bd8890d 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2100,7 +2100,7 @@ static fuse_fstype load_fuse_module(void) struct timespec req = { 0, 100000000 }; /* 100 msec */ fuse_fstype fstype; - if (!stat(cmd, &st) && !getuid()) { + if (!stat(cmd, &st) && !geteuid()) { pid = fork(); if (!pid) { execl(cmd, cmd, "fuse", NULL); @@ -2185,14 +2185,34 @@ static void set_user_mount_option(char *parsed_options, uid_t uid) strcat(parsed_options, option); } +static int set_uid(uid_t uid) +{ + if (setuid(uid)) { + ntfs_log_perror("Failed to set uid to %d", uid); + return NTFS_VOLUME_NO_PRIVILEGE; + } + return NTFS_VOLUME_OK; +} + static struct fuse *mount_fuse(char *parsed_options) { + uid_t uid, euid; struct fuse *fh = NULL; struct fuse_args args = FUSE_ARGS_INIT(0, NULL); /* Libfuse can't always find fusermount, so let's help it. */ if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) ntfs_log_perror("WARNING: Failed to set $PATH\n"); + /* + * We must raise privilege if possible, otherwise the user[s] fstab + * option doesn't work because mount(8) always drops privilege what + * the blkdev option requires. + */ + uid = getuid(); + euid = geteuid(); + + if (set_uid(euid)) + return NULL; ctx->fc = try_fuse_mount(parsed_options); if (!ctx->fc) @@ -2210,14 +2230,23 @@ static struct fuse *mount_fuse(char *parsed_options) if (!fh) goto err; - if (fuse_set_signal_handlers(fuse_get_session(fh))) { - fuse_destroy(fh); - fh = NULL; - goto err; - } + if (fuse_set_signal_handlers(fuse_get_session(fh))) + goto err_destory; + /* + * We can't drop privilege if internal FUSE is used because internal + * unmount needs it. Kernel 2.6.25 may include unprivileged full + * mount/unmount support. + */ +#ifndef FUSE_INTERNAL + if (set_uid(uid)) + goto err_destory; +#endif out: fuse_opt_free_args(&args); return fh; +err_destory: + fuse_destroy(fh); + fh = NULL; err: fuse_unmount(opts.mnt_point, ctx->fc); goto out; @@ -2229,7 +2258,6 @@ int main(int argc, char *argv[]) struct fuse *fh; fuse_fstype fstype = FSTYPE_UNKNOWN; struct stat sbuf; - uid_t uid, euid; int err; utils_set_locale(); @@ -2248,16 +2276,7 @@ int main(int argc, char *argv[]) err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; } - - uid = getuid(); - euid = geteuid(); - if (setuid(euid)) { - ntfs_log_perror("Failed to set user ID to %d", euid); - err = NTFS_VOLUME_NO_PRIVILEGE; - goto err_out; - } - #if defined(linux) || defined(__uClinux__) fstype = get_fuse_fstype(); if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN) @@ -2282,7 +2301,7 @@ int main(int argc, char *argv[]) if (ctx->blkdev) { /* Must do after ntfs_open() to set the right blksize. */ set_fuseblk_options(parsed_options); - set_user_mount_option(parsed_options, uid); + set_user_mount_option(parsed_options, getuid()); } fh = mount_fuse(parsed_options); @@ -2290,13 +2309,7 @@ int main(int argc, char *argv[]) err = NTFS_VOLUME_FUSE_ERROR; goto err_out; } - - if (setuid(uid)) { - ntfs_log_perror("Failed to drop privilege (uid to %d)", uid); - err = NTFS_VOLUME_NO_PRIVILEGE; - goto err_umount; - } - + ctx->mounted = TRUE; #if defined(linux) || defined(__uClinux__) @@ -2326,7 +2339,7 @@ int main(int argc, char *argv[]) fuse_loop(fh); err = 0; -err_umount: + fuse_unmount(opts.mnt_point, ctx->fc); fuse_destroy(fh); err_out: diff --git a/src/utils.c b/src/utils.c index ce93a5fb..2627d83f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -85,8 +85,9 @@ static const char *fakeraid_msg = "to mount NTFS. Please see the 'dmraid' documentation for help.\n"; static const char *access_denied_msg = -"Please check the volume and the NTFS-3G binary permissions, the mounting\n" -"user and group ID, and the mount options.\n"; +"Please check the device and the ntfs-3g binary permissions, the mounting\n" +"user and group ID, and the mount options. You can find more explanation\n" +"at http://ntfs-3g.org/support.html#useroption\n"; static const char *forced_mount_msg = "\n" From 8c8b108e28eab27ecb2dd23132e27fb5221bc6cc Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 19 Jan 2008 22:59:25 +0000 Subject: [PATCH 061/328] ntfs_open(): remove unused argument --- src/ntfs-3g.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 8bd8890d..0eac4996 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1618,7 +1618,7 @@ static int ntfs_fuse_init(void) return 0; } -static int ntfs_open(const char *device, char *mntpoint) +static int ntfs_open(const char *device) { unsigned long flags = 0; @@ -2294,7 +2294,7 @@ int main(int argc, char *argv[]) if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) ctx->blkdev = TRUE; - err = ntfs_open(opts.device, opts.mnt_point); + err = ntfs_open(opts.device); if (err) goto err_out; From 8704a2a97396b571fb18139a4a8a38ed9f3e2dc0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 13:05:19 +0000 Subject: [PATCH 062/328] do_mount(): hint user if mount(2) returns EPERM --- libfuse-lite/fusermount.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 290b47c3..744f53dd 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -500,9 +500,13 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, int errno_save = errno; if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) fprintf(stderr, "%s: 'fuseblk' support missing\n", progname); - else + else { fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save)); - goto err; + if (errno_save == EPERM) + fprintf(stderr, "No privilege to mount. Please see " + "http://ntfs-3g.org/support.html#useroption\n"); + } + goto err; } else { *sourcep = source; *typep = type; From 45d077860ce1a4779d2fd1beb2408ad1115cfaf0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 13:14:49 +0000 Subject: [PATCH 063/328] factor out setup_logging() --- src/ntfs-3g.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 0eac4996..da9b1905 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2252,6 +2252,29 @@ err: 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 + } + } + + ntfs_log_info("Version %s\n", 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); + ntfs_log_info("Mount options: %s\n", parsed_options); +} + int main(int argc, char *argv[]) { char *parsed_options = NULL; @@ -2316,25 +2339,7 @@ int main(int argc, char *argv[]) if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) ntfs_log_info(fuse26_kmod_msg); #endif - 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 - } - } - - ntfs_log_info("Version %s\n", 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); - ntfs_log_info("Mount options: %s\n", parsed_options); + setup_logging(parsed_options); fuse_loop(fh); From f2ffe05969218ef6b8e57f0f323d816deed15597 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 13:20:09 +0000 Subject: [PATCH 064/328] NInoFileName* cleanup --- include/ntfs-3g/inode.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index 90a3e654..b8c4ec5b 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -85,16 +85,13 @@ typedef enum { #define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) #define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) -#define NInoFileNameDirty(ni) \ - test_nino_flag(ni, FileNameDirty) -#define NInoFileNameSetDirty(ni) \ - set_nino_flag(ni, FileNameDirty) -#define NInoFileNameClearDirty(ni) \ - clear_nino_flag(ni, FileNameDirty) +#define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty) +#define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty) +#define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty) #define NInoFileNameTestAndSetDirty(ni) \ - test_and_set_nino_flag(ni, FileNameDirty) + test_and_set_nino_flag(ni, FileNameDirty) #define NInoFileNameTestAndClearDirty(ni) \ - test_and_clear_nino_flag(ni, FileNameDirty) + test_and_clear_nino_flag(ni, FileNameDirty) /** * struct _ntfs_inode - The NTFS in-memory inode structure. From cf9fe6e329416f0beb725f43ddcb416414bc560f Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 13:42:08 +0000 Subject: [PATCH 065/328] log fuse type and version --- src/ntfs-3g.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index da9b1905..db401a44 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -35,6 +35,12 @@ #error "***********************************************************" #endif +#ifdef FUSE_INTERNAL +#define FUSE_TYPE "integrated FUSE" +#else +#define FUSE_TYPE "external FUSE" +#endif + #ifdef HAVE_STDIO_H #include #endif @@ -146,7 +152,7 @@ static const char *locale_msg = static const char *usage_msg = "\n" -"%s %s - Third Generation NTFS Driver\n" +"%s %s %s %d - Third Generation NTFS Driver\n" "\n" "Copyright (C) 2005-2006 Yura Pakhuchiy\n" "Copyright (C) 2006-2007 Szabolcs Szakacsits\n" @@ -1889,7 +1895,8 @@ err_exit: static void usage(void) { - ntfs_log_info(usage_msg, EXEC_NAME, VERSION, EXEC_NAME, ntfs_home); + ntfs_log_info(usage_msg, EXEC_NAME, VERSION, FUSE_TYPE, fuse_version(), + EXEC_NAME, ntfs_home); } #ifndef HAVE_REALPATH @@ -2266,7 +2273,7 @@ static void setup_logging(char *parsed_options) } } - ntfs_log_info("Version %s\n", VERSION); + 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, From 72f55b007eb47efb368adebae437c79a673df9f1 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 13:54:41 +0000 Subject: [PATCH 066/328] copyright update --- src/ntfs-3g.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index db401a44..07f0e4f0 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1,9 +1,9 @@ /** * ntfs-3g - Third Generation NTFS Driver * - * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel - * Copyright (c) 2006-2007 Szabolcs Szakacsits + * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This file is originated from the Linux-NTFS project. * @@ -154,19 +154,18 @@ static const char *usage_msg = "\n" "%s %s %s %d - Third Generation NTFS Driver\n" "\n" -"Copyright (C) 2005-2006 Yura Pakhuchiy\n" -"Copyright (C) 2006-2007 Szabolcs Szakacsits\n" +"Copyright (C) 2006-2008 Szabolcs Szakacsits\n" +"Copyright (C) 2005-2007 Yura Pakhuchiy\n" "\n" "Usage: %s [-o option[,...]]\n" "\n" "Options: ro, force, locale=, uid=, gid=, umask=, fmask=, dmask=,\n" " streams_interface=. Please see details in the manual.\n" "\n" -"Example: ntfs-3g /dev/sda1 /mnt/win -o force,locale=en_EN.UTF-8\n" +"Example: ntfs-3g /dev/sda1 /mnt/win -o force\n" "\n" "%s"; - /** * ntfs_fuse_is_named_data_stream - check path to be to named data stream * @path: path to check From bd657b66cc577f12afc50fae120d3e66644b611b Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 14:26:51 +0000 Subject: [PATCH 067/328] README update --- README | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/README b/README index d3ee16c6..b4a1c73f 100644 --- a/README +++ b/README @@ -2,11 +2,11 @@ INTRODUCTION ============ -The ntfs-3g driver is an open source, freely available read/write NTFS -driver, which provides safe and fast handling of the Windows XP, Windows -Server 2003, Windows 2000 and Windows Vista filesystems. Almost the full -POSIX filesystem functionality is supported, the major exceptions are -changing the file ownerships and the access rights. +The NTFS-3G driver is an open source, freely available read/write NTFS driver +for Linux, FreeBSD, Mac OS X, NetBSD, and Haiku. It provides safe and fast +handling of the Windows XP, Windows Server 2003, Windows 2000 and Windows +Vista file systems. Most POSIX file system operations are supported, and +full file ownership and permission support is also coming along fast. The purpose of the project is to develop, continuously quality test and support a trustable, featureful and high performance solution for hardware @@ -29,26 +29,30 @@ web site at QUICK INSTALLATION ================== - -Make sure you have the basic Linux development tools and the full FUSE -package (http://fuse.sourceforge.net) is already installed correctly on -the computer. Then type: + +Linux: Make sure you have the basic development tools and the kernel includes +the FUSE kernel module. Then type: ./configure make make install # or 'sudo make install' if you aren't root. +Please note that NTFS-3G doesn't require the FUSE user space package anymore. + +Non-Linux: Please see the NTFS-3G web page for OS specific installation and +source packages. + USAGE ===== If there was no error during installation then the NTFS volume can be read-write mounted for everybody the following way (unmount the volume if -it was already mounted, and replace /dev/hda1 and /mnt/windows, if needed): +it was already mounted, and replace /dev/sda1 and /mnt/windows, if needed): - mount -t ntfs-3g /dev/hda1 /mnt/windows + mount -t ntfs-3g /dev/sda1 /mnt/windows or - ntfs-3g /dev/hda1 /mnt/windows + ntfs-3g /dev/sda1 /mnt/windows If your Operating System vendor didn't setup your language specific settings then you may also need to set the 'locale' mount option to make all filenames @@ -63,5 +67,5 @@ Please see the ntfs-3g manual page for more options and examples. You can also make NTFS to be mounted during boot by putting the below line at the __END__ of the /etc/fstab file: - /dev/hda1 /mnt/windows ntfs-3g defaults 0 0 + /dev/sda1 /mnt/windows ntfs-3g defaults 0 0 From db85bed00084b84d7178d5277e6d67d86c2e119f Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 16:31:10 +0000 Subject: [PATCH 068/328] add ntfs-3g.probe manual --- configure.ac | 1 + src/Makefile.am | 2 +- src/ntfs-3g.probe.8.in | 81 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/ntfs-3g.probe.8.in diff --git a/configure.ac b/configure.ac index 9bc832ae..b0c2e027 100644 --- a/configure.ac +++ b/configure.ac @@ -336,6 +336,7 @@ AC_CONFIG_FILES([ libntfs-3g/libntfs-3g.script.so src/Makefile src/ntfs-3g.8 + src/ntfs-3g.probe.8 ]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index fc74bbdf..af22dadc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,7 +12,7 @@ endif bin_PROGRAMS = ntfs-3g.probe rootbin_PROGRAMS = ntfs-3g rootsbin_DATA = #Create directory -man_MANS = ntfs-3g.8 +man_MANS = ntfs-3g.8 ntfs-3g.probe.8 ntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la if REALLYSTATIC diff --git a/src/ntfs-3g.probe.8.in b/src/ntfs-3g.probe.8.in new file mode 100644 index 00000000..3e8a2678 --- /dev/null +++ b/src/ntfs-3g.probe.8.in @@ -0,0 +1,81 @@ +.\" Copyright (c) 2008 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G.PROBE 8 "January 2008" "ntfs-3g.probe @VERSION@" +.SH NAME +ntfs-3g.probe \- Probe an NTFS volume mountability +.SH SYNOPSIS +.B ntfs-3g.probe +.I <\-\-readonly|\-\-readwrite> +.I volume +.br +.SH DESCRIPTION +The \fBntfs-3g.probe\fR utility tests a volume if it's NTFS mountable +read-only or read-write, and exits with a status value accordingly. +The \fIvolume\fR can be a block device or image file. +.SH OPTIONS +Below is a summary of the options that \fBntfs-3g.probe\fR accepts. +.TP +.B \-r, \-\-readonly +Test if the volume can be mounted read-only. +.TP +.B \-w, \-\-readwrite +Test if the volume can be mounted read-write. +.TP +.B \-h, \-\-help +Display help and exit. +.SH EXAMPLE +Test if /dev/sda1 can be mounted read-write: +.RS +.sp +.B ntfs-3g.probe --readwrite /dev/sda1 +.sp +.RE +.SH EXIT CODES +The exit codes are as follows: +.IP 0 +Volume is mountable. +.IP 11 +Syntax error, command line parsing failed. +.IP 12 +The volume doesn't have a valid NTFS. +.IP 13 +Inconsistent NTFS, hardware or device driver fault, or unsetup +SoftRAID/FakeRAID hardware. +.IP 14 +The NTFS partition is hibernated. +.IP 15 +The volume was not cleanly unmounted. +.IP 16 +The volume is already exclusively opened and in use by a kernel +driver or software. +.IP 17 +Unsetup SoftRAID/FakeRAID hardware. +.IP 18 +Unknown reason. +.IP 19 +Not enough privilege to mount. +.IP 20 +Out of memory. +.IP 21 +Unclassified FUSE error. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://ntfs-3g.org/support.html +.sp +.RE +for common questions and known issues. +If you think you have found an undocumented problem in the latest release of +the software then please send an email describing it in detail. +You can contact the development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g.probe +was written by Szabolcs Szakacsits (szaka@ntfs-3g.org). +.SH THANKS +Alon Bar-Lev has integrated the utility into the NTFS-3G build process and +tested it with Erik Larsson before the public release. +.SH SEE ALSO +.BR ntfs-3g (8) From 5d99f42e742216a0f8f0fbc324942b0f5fcd09fe Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 16:51:36 +0000 Subject: [PATCH 069/328] update ntfs-3g manual --- src/ntfs-3g.8.in | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 31854dd4..8664859b 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -1,18 +1,18 @@ .\" Copyright (c) 2005-2006 Yura Pakhuchiy. .\" Copyright (c) 2005 Richard Russon. -.\" Copyright (c) 2006-2007 Szabolcs Szakacsits. +.\" Copyright (c) 2006-2008 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G 8 "November 2007" "ntfs-3g @VERSION@" +.TH NTFS-3G 8 "January 2008" "ntfs-3g @VERSION@" .SH NAME ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS .B ntfs-3g -.I device mount_point +.I volume mount_point \fB[-o \fIoption\fP\fB[,...]]\fR .br .B mount \-t ntfs-3g -.I device mount_point +.I volume mount_point \fB[-o \fIoption\fP\fB[,...]]\fR .SH DESCRIPTION \fBntfs-3g\fR is an NTFS driver, which can @@ -21,6 +21,9 @@ streams; it can read and write files, including streams and sparse files; it can handle special files like symbolic links, devices, and FIFOs; moreover it can also read transparently compressed files. +.PP +The \fIvolume\fR to be mounted can be either a block device or +an image file. .SS Access Handling and Security By default, files and directories are owned by the effective user and group of the mounting process and everybody has @@ -43,14 +46,10 @@ Windows users have full access to the files created by If .B ntfs-3g is set setuid-root then non-root users will -be also able to mount block devices or via /etc/fstab if the 'user' +be also able to mount volumes and via /etc/fstab if the 'user' or 'users' .BR mount (8) -option is specified. The -.B ntfs-3g -process drops the -root privilege after successful mount and runs unprivileged -afterwards. +option is specified. .SS Windows Filename Compatibility NTFS supports several filename namespaces: DOS, Win32 and POSIX. While the \fBntfs-3g\fR driver handles all of them, it always creates new files in the @@ -182,29 +181,29 @@ libntfs-3g and FUSE. .B no_detach Same as above but with less debug output. .SH EXAMPLES -Mount /dev/hda1 to /mnt/windows: +Mount /dev/sda1 to /mnt/windows: .RS .sp -.B ntfs-3g /dev/hda1 /mnt/windows +.B ntfs-3g /dev/sda1 /mnt/windows .sp .RE or .RS .sp -.B mount -t ntfs-3g /dev/hda1 /mnt/windows +.B mount -t ntfs-3g /dev/sda1 /mnt/windows .sp .RE -Read\-only mount /dev/hda5 to /home/user/mnt and make user with uid 1000 +Read\-only mount /dev/sda5 to /home/user/mnt and make user with uid 1000 to be the owner of all files: .RS .sp -.B ntfs-3g /dev/hda5 /home/user/mnt \-o ro,uid=1000 +.B ntfs-3g /dev/sda5 /home/user/mnt \-o ro,uid=1000 .sp .RE /etc/fstab entry for the above: .RS .sp -.B /dev/hda5 /home/user/mnt ntfs\-3g ro,uid=1000 0 0 +.B /dev/sda5 /home/user/mnt ntfs\-3g ro,uid=1000 0 0 .sp .RE Unmount /mnt/windows: @@ -213,6 +212,14 @@ Unmount /mnt/windows: .B umount /mnt/windows .sp .RE +.SH EXIT CODES +To facilitate the use of the +.B ntfs-3g +driver in scripts, an exit code is returned to give an indication of the +mountability status of a volume. Value 0 means success, and all other +ones mean an error. The unique error codes are documented in the +.BR ntfs-3g.probe (8) +manual page. .SH KNOWN ISSUES Please see .RS @@ -231,9 +238,7 @@ address. was based on and a major improvement to ntfsmount and libntfs which were written by Yura Pakhuchiy and the Linux-NTFS team. The improvements were made, the ntfs-3g project was initiated and currently led by long time -Linux-NTFS team developer Szabolcs Szakacsits (szaka@sienet.hu) to revive -the stalled open source development and project management. - +Linux-NTFS team developer Szabolcs Szakacsits (szaka@ntfs-3g.org). .SH THANKS Several people made heroic efforts, often over five or more years which resulted the ntfs-3g driver. Most importantly they are @@ -241,6 +246,7 @@ Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, Yuval Fledel, and the author of the groundbreaking FUSE filesystem development framework, Miklos Szeredi. .SH SEE ALSO +.BR ntfs-3g.probe (8), .BR ntfsprogs (8), .BR attr (5), .BR getfattr (1) From fd887a11f4b3e5405c5d11133ead2eb5a8e6d821 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 16:59:14 +0000 Subject: [PATCH 070/328] update AUTHORS and CREDITS --- AUTHORS | 1 + CREDITS | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index cde9c0f2..242aea05 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,6 +2,7 @@ Present authors of ntfs-3g in alphabetical order: Jean-Pierre Andre +Alon Bar-Lev Dominique L Bouix Csaba Henk Erik Larsson diff --git a/CREDITS b/CREDITS index 844a147e..12fc207f 100644 --- a/CREDITS +++ b/CREDITS @@ -38,4 +38,7 @@ Ismail Donmez Laszlo Dvornik Pallaghy Ajtony Szabolcs Szakacsits - +Jean-Pierre Andre +Alejandro Pulver +Erik Larsson +Alon Bar-Lev From a097dfad1bccc1f3aecf572aebaf540cbfa1368f Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 18:06:06 +0000 Subject: [PATCH 071/328] remove unused FUSERMOUNT_DIR define --- libfuse-lite/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am index 986f7a3e..d2f9ad5c 100644 --- a/libfuse-lite/Makefile.am +++ b/libfuse-lite/Makefile.am @@ -8,7 +8,6 @@ endif libfuse_lite_la_CFLAGS= \ $(AM_CFLAGS) \ -I$(top_srcdir)/include/fuse-lite \ - -DFUSERMOUNT_DIR=\"$(bindir)\" \ -D_REENTRANT \ -D_FILE_OFFSET_BITS=64 From 0afd8ce18b556c4e6a80b1ea5c2007a2941f7bd1 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 18:37:15 +0000 Subject: [PATCH 072/328] fix signal handling for integrated fuse user mounts --- src/ntfs-3g.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 07f0e4f0..d2e08792 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2191,6 +2191,7 @@ static void set_user_mount_option(char *parsed_options, uid_t uid) strcat(parsed_options, option); } +#ifndef FUSE_INTERNAL static int set_uid(uid_t uid) { if (setuid(uid)) { @@ -2199,16 +2200,15 @@ static int set_uid(uid_t uid) } return NTFS_VOLUME_OK; } +#endif static struct fuse *mount_fuse(char *parsed_options) { - uid_t uid, euid; struct fuse *fh = NULL; struct fuse_args args = FUSE_ARGS_INIT(0, NULL); +#ifndef FUSE_INTERNAL + uid_t uid, euid; - /* Libfuse can't always find fusermount, so let's help it. */ - if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) - ntfs_log_perror("WARNING: Failed to set $PATH\n"); /* * We must raise privilege if possible, otherwise the user[s] fstab * option doesn't work because mount(8) always drops privilege what @@ -2219,6 +2219,10 @@ static struct fuse *mount_fuse(char *parsed_options) if (set_uid(euid)) return NULL; +#endif + /* Libfuse can't always find fusermount, so let's help it. */ + if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) + ntfs_log_perror("WARNING: Failed to set $PATH\n"); ctx->fc = try_fuse_mount(parsed_options); if (!ctx->fc) From 8a1cc132ab88c43263f73ea2701ec4372fd11d09 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 18:43:57 +0000 Subject: [PATCH 073/328] remove root requirement for blkddev since still no security problem is known --- libfuse-lite/fusermount.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 744f53dd..06a226c1 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -399,10 +399,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, if (!get_string_opt(s, len, subtype_str, &subtype)) goto err; } else if (opt_eq(s, len, "blkdev")) { - if (getuid() != 0) { - fprintf(stderr, "%s: option blkdev is privileged\n", progname); - goto err; - } blkdev = 1; } else if (opt_eq(s, len, "nonempty")) { check_empty = 0; From a246e45f82a26bf4a124d6c59fd2a8a0a42a91ac Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 20 Jan 2008 19:26:26 +0000 Subject: [PATCH 074/328] release 1.2121-RC --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b0c2e027..c4596b53 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.1206-BETA],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=18 +AC_INIT([ntfs-3g],[1.2121-RC],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=20 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From dbbdb34f1b0a90d2357ee1235f690298e862281a Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:04:06 +0000 Subject: [PATCH 075/328] convert C++ style comments to C comments (Alon Bar-Lev) --- include/ntfs-3g/attrib.h | 1 - include/ntfs-3g/layout.h | 23 ++++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index 3aeec198..7656bf16 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -303,7 +303,6 @@ extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); -// FIXME / TODO: Above here the file is cleaned up. (AIA) /** * get_attribute_value_length - return the length of the value of an attribute * @a: pointer to a buffer containing the attribute record diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index a3d0b27e..8670557e 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -2340,17 +2340,18 @@ typedef struct { control entry in the data part of the index. */ } __attribute__((__packed__)) key; - /* The (optional) index data is inserted here when creating. */ - // VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last - // eight bytes of this index entry contain the virtual - // cluster number of the index block that holds the - // entries immediately preceding the current entry. - // - // If the key_length is zero, then the vcn immediately - // follows the INDEX_ENTRY_HEADER. - // - // The address of the vcn of "ie" INDEX_ENTRY is given by - // (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + /* The (optional) index data is inserted here when creating. + VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last + eight bytes of this index entry contain the virtual + cluster number of the index block that holds the + entries immediately preceding the current entry. + + If the key_length is zero, then the vcn immediately + follows the INDEX_ENTRY_HEADER. + + The address of the vcn of "ie" INDEX_ENTRY is given by + (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + */ } __attribute__((__packed__)) INDEX_ENTRY; /** From 48f32c048aa2a66c9c8a1ce8c04d52800c120a32 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:05:40 +0000 Subject: [PATCH 076/328] fix %lld when off_t is used on 64-bit (Alon Bar-Lev) --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index d2e08792..f932773c 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -703,7 +703,7 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, res = ntfs_attr_pwrite(na, offset, size, buf); if (res < (s64)size) ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " - "%lld <> %d)", (s64)offset, (s64)size, res); + "%lld <> %d)", (long long)offset, (long long)size, res); if (res <= 0) { res = -errno; goto exit; From ba4e6dbd9e0494c546bcf2bba7815c343dfebee6 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:07:59 +0000 Subject: [PATCH 077/328] fix void* arithmetic (Alon Bar-Lev) --- libfuse-lite/fuse_opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfuse-lite/fuse_opt.c b/libfuse-lite/fuse_opt.c index 1c6a3157..13a27a63 100644 --- a/libfuse-lite/fuse_opt.c +++ b/libfuse-lite/fuse_opt.c @@ -200,7 +200,7 @@ static int process_opt(struct fuse_opt_context *ctx, if (call_proc(ctx, arg, opt->value, iso) == -1) return -1; } else { - void *var = ctx->data + opt->offset; + void *var = (char *)ctx->data + opt->offset; if (sep && opt->templ[sep + 1]) { const char *param = arg + sep; if (opt->templ[sep] == '=') From a8791404f5005e48df006c9adc4e099d3f179708 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:15:00 +0000 Subject: [PATCH 078/328] rename variables with names as a standard library function (Alon Bar-Lev) --- libfuse-lite/fuse.c | 4 ++-- libfuse-lite/fuse_lowlevel.c | 4 ++-- libfuse-lite/helper.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 6c5deced..7671b5f3 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -2415,9 +2415,9 @@ static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino, static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, - int sleep) + int should_sleep) { - int err = fuse_lock_common(req, ino, fi, lock, sleep ? F_SETLKW : F_SETLK); + int err = fuse_lock_common(req, ino, fi, lock, should_sleep ? F_SETLKW : F_SETLK); if (!err) { struct fuse *f = req_fuse(req); struct lock l; diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index 81287e2c..8b0adac7 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -841,7 +841,7 @@ static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) } static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, - const void *inarg, int sleep) + const void *inarg, int should_sleep) { struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; struct fuse_file_info fi; @@ -853,7 +853,7 @@ static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, convert_fuse_file_lock(&arg->lk, &flock); if (req->f->op.setlk) - req->f->op.setlk(req, nodeid, &fi, &flock, sleep); + req->f->op.setlk(req, nodeid, &fi, &flock, should_sleep); else fuse_reply_err(req, ENOSYS); } diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c index b3f67e07..b0b6775c 100644 --- a/libfuse-lite/helper.c +++ b/libfuse-lite/helper.c @@ -122,18 +122,18 @@ static int add_default_subtype(const char *progname, struct fuse_args *args) { int res; char *subtype_opt; - const char *basename = strrchr(progname, '/'); - if (basename == NULL) - basename = progname; - else if (basename[1] != '\0') - basename++; + const char *prog_basename = strrchr(progname, '/'); + if (prog_basename == NULL) + prog_basename = progname; + else if (prog_basename[1] != '\0') + prog_basename++; - subtype_opt = (char *) malloc(strlen(basename) + 64); + subtype_opt = (char *) malloc(strlen(prog_basename) + 64); if (subtype_opt == NULL) { fprintf(stderr, "fuse: memory allocation failed\n"); return -1; } - sprintf(subtype_opt, "-osubtype=%s", basename); + sprintf(subtype_opt, "-osubtype=%s", prog_basename); res = fuse_opt_add_arg(args, subtype_opt); free(subtype_opt); return res; From 5e6d439ce85e76ca08501f54056f199a7127b2e0 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:20:16 +0000 Subject: [PATCH 079/328] add config.h to all fuse-lite sources (Alon Bar-Lev) --- libfuse-lite/fuse.c | 1 + libfuse-lite/fuse_kern_chan.c | 1 + libfuse-lite/fuse_loop.c | 1 + libfuse-lite/fuse_loop_mt.c | 1 + libfuse-lite/fuse_mt.c | 1 + libfuse-lite/fuse_opt.c | 1 + libfuse-lite/fuse_session.c | 1 + libfuse-lite/fuse_signals.c | 1 + libfuse-lite/mount_util.c | 1 + 9 files changed, 9 insertions(+) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 7671b5f3..f523cd6f 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -10,6 +10,7 @@ /* For pthread_rwlock_t */ #define _GNU_SOURCE +#include "config.h" #include "fuse_i.h" #include "fuse_lowlevel.h" #include "fuse_opt.h" diff --git a/libfuse-lite/fuse_kern_chan.c b/libfuse-lite/fuse_kern_chan.c index 49e88b7d..e9963b5d 100644 --- a/libfuse-lite/fuse_kern_chan.c +++ b/libfuse-lite/fuse_kern_chan.c @@ -6,6 +6,7 @@ See the file COPYING.LIB */ +#include "config.h" #include "fuse_lowlevel.h" #include "fuse_kernel.h" #include "fuse_i.h" diff --git a/libfuse-lite/fuse_loop.c b/libfuse-lite/fuse_loop.c index f7c17faf..0b592e53 100644 --- a/libfuse-lite/fuse_loop.c +++ b/libfuse-lite/fuse_loop.c @@ -6,6 +6,7 @@ See the file COPYING.LIB */ +#include "config.h" #include "fuse_lowlevel.h" #include diff --git a/libfuse-lite/fuse_loop_mt.c b/libfuse-lite/fuse_loop_mt.c index 533d9c31..f5082fd3 100644 --- a/libfuse-lite/fuse_loop_mt.c +++ b/libfuse-lite/fuse_loop_mt.c @@ -6,6 +6,7 @@ See the file COPYING.LIB. */ +#include "config.h" #include "fuse_lowlevel.h" #include "fuse_misc.h" #include "fuse_kernel.h" diff --git a/libfuse-lite/fuse_mt.c b/libfuse-lite/fuse_mt.c index 0fda2a3d..1b850c62 100644 --- a/libfuse-lite/fuse_mt.c +++ b/libfuse-lite/fuse_mt.c @@ -6,6 +6,7 @@ See the file COPYING.LIB. */ +#include "config.h" #include "fuse_i.h" #include "fuse_lowlevel.h" diff --git a/libfuse-lite/fuse_opt.c b/libfuse-lite/fuse_opt.c index 13a27a63..7d76e022 100644 --- a/libfuse-lite/fuse_opt.c +++ b/libfuse-lite/fuse_opt.c @@ -6,6 +6,7 @@ See the file COPYING.LIB */ +#include "config.h" #include "fuse_opt.h" #include diff --git a/libfuse-lite/fuse_session.c b/libfuse-lite/fuse_session.c index 67c41a0d..c4e544d1 100644 --- a/libfuse-lite/fuse_session.c +++ b/libfuse-lite/fuse_session.c @@ -6,6 +6,7 @@ See the file COPYING.LIB */ +#include "config.h" #include "fuse_lowlevel.h" #include "fuse_lowlevel_compat.h" diff --git a/libfuse-lite/fuse_signals.c b/libfuse-lite/fuse_signals.c index 25998bfc..bf979563 100644 --- a/libfuse-lite/fuse_signals.c +++ b/libfuse-lite/fuse_signals.c @@ -6,6 +6,7 @@ See the file COPYING.LIB */ +#include "config.h" #include "fuse_lowlevel.h" #include diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c index 9ce431ae..05098293 100644 --- a/libfuse-lite/mount_util.c +++ b/libfuse-lite/mount_util.c @@ -6,6 +6,7 @@ See the file COPYING.LIB. */ +#include "config.h" #include "mount_util.h" #include #include From 9c92941a6f6e32c21a4386e42d0407c320f1c30b Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:23:55 +0000 Subject: [PATCH 080/328] define _REENTRANT in configure instead of in Makefile.am (Alon Bar-Lev) --- configure.ac | 5 +++++ libfuse-lite/Makefile.am | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c4596b53..62bbef3f 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,11 @@ if test "${with_fuse}" = "internal"; then [LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lrt"], [AC_MSG_ERROR([Cannot find rt library])] ) + AC_DEFINE( + [_REENTRANT], + [1], + [Required define if using POSIX threads] + ) # required so that we re-compile anything AC_DEFINE( [FUSE_INTERNAL], diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am index d2f9ad5c..c8339718 100644 --- a/libfuse-lite/Makefile.am +++ b/libfuse-lite/Makefile.am @@ -8,7 +8,6 @@ endif libfuse_lite_la_CFLAGS= \ $(AM_CFLAGS) \ -I$(top_srcdir)/include/fuse-lite \ - -D_REENTRANT \ -D_FILE_OFFSET_BITS=64 libfuse_lite_la_LIBADD = $(LIBFUSE_LITE_LIBS) From 7fe7e1c9c6d07fb13b3ea80aeeb747cff7623ca1 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:27:11 +0000 Subject: [PATCH 081/328] remove _GNU_SOURCE define from source since it's in configure (Alon Bar-Lev) --- configure.ac | 1 + libfuse-lite/fuse.c | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 62bbef3f..5ed19e19 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,7 @@ AC_ARG_ENABLE( ) # Programs +# pthread_rwlock_t requires _GNU_SOURCE AC_GNU_SOURCE AC_PROG_CC AC_PROG_LIBTOOL diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index f523cd6f..1820f26b 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -6,10 +6,6 @@ See the file COPYING.LIB */ - -/* For pthread_rwlock_t */ -#define _GNU_SOURCE - #include "config.h" #include "fuse_i.h" #include "fuse_lowlevel.h" From ce46eba9793025f8b555326a9bc6909c3fa14fdd Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:32:27 +0000 Subject: [PATCH 082/328] remove _FILE_OFFSET_BITS define/check since it's in config.h (Alon Bar-Lev) --- include/fuse-lite/fuse_common.h | 5 ----- libfuse-lite/Makefile.am | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/include/fuse-lite/fuse_common.h b/include/fuse-lite/fuse_common.h index b7e5791b..ddc9ce30 100644 --- a/include/fuse-lite/fuse_common.h +++ b/include/fuse-lite/fuse_common.h @@ -27,11 +27,6 @@ #define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) -/* This interface uses 64 bit off_t */ -#if _FILE_OFFSET_BITS != 64 -#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags! -#endif - #ifdef __cplusplus extern "C" { #endif diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am index c8339718..9e73d4e8 100644 --- a/libfuse-lite/Makefile.am +++ b/libfuse-lite/Makefile.am @@ -7,8 +7,7 @@ endif libfuse_lite_la_CFLAGS= \ $(AM_CFLAGS) \ - -I$(top_srcdir)/include/fuse-lite \ - -D_FILE_OFFSET_BITS=64 + -I$(top_srcdir)/include/fuse-lite libfuse_lite_la_LIBADD = $(LIBFUSE_LITE_LIBS) From 42c3d8593bc30696183080a27a6f500ae09f1189 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:45:36 +0000 Subject: [PATCH 083/328] remove -fno-strict-aliasing (Alon Bar-Lev) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5ed19e19..865fe4b9 100644 --- a/configure.ac +++ b/configure.ac @@ -265,7 +265,7 @@ AC_CHECK_FUNCS([ \ AC_SYS_LARGEFILE # We add -Wall to enable some compiler warnings. -CFLAGS="${CFLAGS} -Wall -fno-strict-aliasing" +CFLAGS="${CFLAGS} -Wall" if test "${enable_pedantic}" = "yes"; then enable_warnings="yes" From 49fd15caf2539b8d820aebe45ea447a94d701689 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 21:56:06 +0000 Subject: [PATCH 084/328] release 1.2124-RC --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 865fe4b9..fa2fbaf7 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2121-RC],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=20 +AC_INIT([ntfs-3g],[1.2124-RC],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=21 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 8abe8928aebfd813c79319bd182b710ce2952b58 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 22:01:02 +0000 Subject: [PATCH 085/328] add config.h (Alon Bar-Lev) --- libfuse-lite/fuse_lowlevel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index 8b0adac7..847bd917 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -6,6 +6,7 @@ See the file COPYING.LIB */ +#include "config.h" #include "fuse_lowlevel.h" #include "fuse_kernel.h" #include "fuse_opt.h" From 515aaffdfa4d5fd11dbc15b7020d29431a7b69b8 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 23 Jan 2008 22:02:14 +0000 Subject: [PATCH 086/328] release 1.2125-RC --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fa2fbaf7..22ff3ada 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2124-RC],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[1.2125-RC],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION=21 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) From d5cc4bf050b1f1ab7e154c1b10e2a249d32e13d2 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 28 Jan 2008 19:06:29 +0000 Subject: [PATCH 087/328] release 1.2129 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 22ff3ada..c5c4b971 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2125-RC],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[1.2129],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION=21 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) From 34ad920d447281b4051242c072c3802762365007 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 12:11:10 +0000 Subject: [PATCH 088/328] remove unneeded/redundant fuse_kern_chan_new() declaration --- include/fuse-lite/fuse_lowlevel_compat.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/fuse-lite/fuse_lowlevel_compat.h b/include/fuse-lite/fuse_lowlevel_compat.h index 87be491b..1bdebd8a 100644 --- a/include/fuse-lite/fuse_lowlevel_compat.h +++ b/include/fuse-lite/fuse_lowlevel_compat.h @@ -14,4 +14,3 @@ size_t fuse_dirent_size(size_t namelen); char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, off_t off); -struct fuse_chan *fuse_kern_chan_new(int fd); From 23dfee43665b77fecf0445020ea4096239966bfa Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 12:15:31 +0000 Subject: [PATCH 089/328] ntfs_delete(): factor out ntfs_index_remove() --- include/ntfs-3g/index.h | 2 +- libntfs-3g/dir.c | 32 +++----------------------------- libntfs-3g/index.c | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 9d7fd88b..97044938 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -115,7 +115,7 @@ extern int ntfs_index_lookup(const void *key, const int key_len, extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref); -extern int ntfs_index_rm(ntfs_index_context *ictx); +extern int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen); extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index b82885d3..bed0d181 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1357,7 +1357,6 @@ no_hardlink: int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) { ntfs_attr_search_ctx *actx = NULL; - ntfs_index_context *ictx = NULL; FILE_NAME_ATTR *fn = NULL; BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; BOOL case_sensitive_match = TRUE; @@ -1452,19 +1451,7 @@ search: if (ntfs_check_unlinkable_dir(ni, fn) < 0) goto err_out; - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) - goto err_out; - if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->value_length), ictx)) - goto err_out; - - if (((FILE_NAME_ATTR*)ictx->data)->file_attributes & - FILE_ATTR_REPARSE_POINT) { - errno = EOPNOTSUPP; - goto err_out; - } - - if (ntfs_index_rm(ictx)) + if (ntfs_index_remove(dir_ni, fn, le32_to_cpu(actx->attr->value_length))) goto err_out; if (ntfs_attr_record_rm(actx)) @@ -1535,8 +1522,6 @@ ok: out: if (actx) ntfs_attr_put_search_ctx(actx); - if (ictx) - ntfs_index_ctx_put(ictx); if (ntfs_inode_close(dir_ni) && !err) err = errno; if (ntfs_inode_close(ni) && !err) @@ -1617,22 +1602,11 @@ int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) } /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { - ntfs_index_context *ictx; - - err = errno; ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + err = errno; /* Try to remove just added attribute from index. */ - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) + if (ntfs_index_remove(dir_ni, fn, fn_len)) goto rollback_failed; - if (ntfs_index_lookup(fn, fn_len, ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } - if (ntfs_index_rm(ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } goto err_out; } /* Increment hard links count. */ diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index cb4918b3..76879521 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1828,6 +1828,33 @@ err_out: return -1; } +int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen) +{ + int ret = -1; + ntfs_index_context *ctx; + + ctx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!ctx) + return -1; + + if (ntfs_index_lookup(key, keylen, ctx)) + goto err_out; + + if (((FILE_NAME_ATTR *)ctx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + goto err_out; + } + + if (ntfs_index_rm(ctx)) + goto err_out; + + ret = 0; +err_out: + ntfs_index_ctx_put(ctx); + return ret; +} + /** * ntfs_index_root_get - read the index root of an attribute * @ni: open ntfs inode in which the ntfs attribute resides From 81a74bfbeccaed846a0dc54e26d520d83a12ffbe Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 12:28:17 +0000 Subject: [PATCH 090/328] ntfs_index_lookup(): fix memory leak on an unlikely error path --- libntfs-3g/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 76879521..1587bf86 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -690,7 +690,7 @@ int ntfs_index_lookup(const void *key, const int key_len, errno = EINVAL; ntfs_log_perror("Index block size (%d) is smaller than the " "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); - return -1; + return err_out; } if (ni->vol->cluster_size <= icx->block_size) From d776c522099885cb82fa8c2236e6cecf17dc76aa Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 12:34:06 +0000 Subject: [PATCH 091/328] ntfs_index_lookup(): fix typo in previous commit --- libntfs-3g/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 1587bf86..247fbd12 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -690,7 +690,7 @@ int ntfs_index_lookup(const void *key, const int key_len, errno = EINVAL; ntfs_log_perror("Index block size (%d) is smaller than the " "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); - return err_out; + goto err_out; } if (ni->vol->cluster_size <= icx->block_size) From b4764d8a9d41de6850f82a05cc01822082c284f4 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 12:36:28 +0000 Subject: [PATCH 092/328] remove unused max_depth field from ntfs_index_context --- include/ntfs-3g/index.h | 1 - libntfs-3g/index.c | 1 - 2 files changed, 2 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 97044938..aa81b0b4 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -98,7 +98,6 @@ typedef struct { int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ VCN ib_vcn; VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ - int max_depth; /* number of the parent nodes */ int pindex; /* maximum it's the number of the parent nodes */ BOOL ib_dirty; u32 block_size; diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 247fbd12..52fcf28b 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -789,7 +789,6 @@ done: icx->entry = ie; icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); icx->data_len = le16_to_cpu(ie->key_length); - icx->max_depth = icx->pindex; ntfs_log_trace("Done.\n"); if (err) { errno = err; From a7b27505ea06669925531b1e96f4a803e69fbb67 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 13:14:17 +0000 Subject: [PATCH 093/328] fix: file/dir removal/rename may have been denied as "Operation not supported" --- libntfs-3g/index.c | 90 +++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 52fcf28b..62fbd1b0 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -4,7 +4,7 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -1085,7 +1085,7 @@ static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, src->index.index_length = cpu_to_le32(((char *)ie - ies_start) + le16_to_cpu(ie->length) + le32_to_cpu(src->index.entries_offset)); - if (ntfs_ib_write(icx, icx->parent_vcn[icx->pindex + 1], src)) + if (ntfs_ib_write(icx, sle64_to_cpu(((INDEX_BLOCK *)src)->index_block_vcn), src)) return STATUS_ERROR; return STATUS_OK; @@ -1384,7 +1384,7 @@ err_out: } /** - * ntfs_ib_split - Split index allocation attribute + * ntfs_ib_split - Split an index block * * On success return STATUS_OK or STATUS_KEEP_SEARCHING. * On error return is STATUS_ERROR. @@ -1415,8 +1415,6 @@ static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) else ret = ntfs_ib_insert(icx, median, new_vcn); - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - if (ret != STATUS_OK) { ntfs_ibm_clear(icx, new_vcn); return ret; @@ -1581,9 +1579,6 @@ static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) /* Not fatal error */ ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); - - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - ntfs_index_ctx_reinit(icx); } /** @@ -1661,7 +1656,7 @@ out: static int ntfs_index_rm_node(ntfs_index_context *icx) { - int entry_pos; + int entry_pos, pindex; VCN vcn; INDEX_BLOCK *ib = NULL; INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; @@ -1683,6 +1678,7 @@ static int ntfs_index_rm_node(ntfs_index_context *icx) ie_succ = ntfs_ie_get_next(icx->entry); entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; descend: vcn = ntfs_ie_get_vcn(ie_succ); if (ntfs_ib_read(icx, vcn, ib)) @@ -1724,18 +1720,18 @@ descend: new_size = le32_to_cpu(ih->index_length) + delta; if (delta > 0) { if (icx->is_in_root) { - if (ntfs_ir_truncate(icx, new_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to truncate INDEX ROOT during entry removal"); + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) goto out2; - } ih = &icx->ir->index; entry = ntfs_ie_get_by_pos(ih, entry_pos); } else if (new_size > le32_to_cpu(ih->allocated_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to split INDEX BLOCK during entry removal"); + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; goto out2; } } @@ -1746,7 +1742,6 @@ descend: if (icx->is_in_root) { if (ntfs_ir_truncate(icx, new_size)) goto out2; - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); } else if (ntfs_icx_ib_write(icx)) goto out2; @@ -1781,7 +1776,7 @@ out: int ntfs_index_rm(ntfs_index_context *icx) { INDEX_HEADER *ih; - int err; + int err, ret = STATUS_OK; ntfs_log_trace("Entering\n"); @@ -1797,8 +1792,7 @@ int ntfs_index_rm(ntfs_index_context *icx) if (icx->entry->ie_flags & INDEX_ENTRY_NODE) { - if (ntfs_index_rm_node(icx)) - goto err_out; + ret = ntfs_index_rm_node(icx); } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { @@ -1815,43 +1809,51 @@ int ntfs_index_rm(ntfs_index_context *icx) if (ntfs_index_rm_leaf(icx)) goto err_out; } - - ntfs_index_ctx_reinit(icx); - ntfs_log_trace("Done.\n"); - return 0; +out: + return ret; err_out: - err = errno; - ntfs_index_ctx_reinit(icx); - errno = err; - ntfs_log_trace("Failed.\n"); - return -1; + ret = STATUS_ERROR; + goto out; } int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen) { - int ret = -1; - ntfs_index_context *ctx; + int ret = STATUS_ERROR; + ntfs_index_context *icx; - ctx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); - if (!ctx) + icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!icx) return -1; - if (ntfs_index_lookup(key, keylen, ctx)) - goto err_out; + while (1) { + + if (ntfs_index_lookup(key, keylen, icx)) + goto err_out; - if (((FILE_NAME_ATTR *)ctx->data)->file_attributes & - FILE_ATTR_REPARSE_POINT) { - errno = EOPNOTSUPP; - goto err_out; + if (((FILE_NAME_ATTR *)icx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + goto err_out; + } + + ret = ntfs_index_rm(icx); + if (ret == STATUS_ERROR) + goto err_out; + else if (ret == STATUS_OK) + break; + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); } - - if (ntfs_index_rm(ctx)) - goto err_out; - ret = 0; -err_out: - ntfs_index_ctx_put(ctx); + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); +out: + ntfs_index_ctx_put(icx); return ret; +err_out: + ret = STATUS_ERROR; + ntfs_log_perror("Delete failed"); + goto out; } /** From 6066e9f04de29ffc8e5dbad46445cb9a1fd65fa3 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 13:19:00 +0000 Subject: [PATCH 094/328] fix: empty index block is EIO, not EOPNOTSUPP --- libntfs-3g/index.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 62fbd1b0..53b0d4b3 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1696,9 +1696,8 @@ descend: goto descend; if (ntfs_ih_zero_entry(&ib->index)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Failed to find any entry in an index block. " - "Please run chkdsk."); + errno = EIO; + ntfs_log_perror("Empty index block"); goto out; } From a723dc0bb6042372f1e80cf28c89146c9fce3e47 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 14:46:08 +0000 Subject: [PATCH 095/328] ntfs_ib_write(): remove redundant vcn argument --- libntfs-3g/index.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 53b0d4b3..a5b83a75 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -78,14 +78,14 @@ static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) return pos >> icx->vcn_size_bits; } -static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf) +static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) { - s64 ret; + s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); ntfs_log_trace("vcn: %lld\n", vcn); ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), - 1, icx->block_size, buf); + 1, icx->block_size, ib); if (ret != 1) { ntfs_log_perror("Failed to write index block %lld, inode %llu", (long long)vcn, (unsigned long long)icx->ni->mft_no); @@ -97,7 +97,7 @@ static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf) static int ntfs_icx_ib_write(ntfs_index_context *icx) { - if (ntfs_ib_write(icx, icx->ib_vcn, icx->ib)) + if (ntfs_ib_write(icx, icx->ib)) return STATUS_ERROR; icx->ib_dirty = FALSE; @@ -155,7 +155,7 @@ static void ntfs_index_ctx_free(ntfs_index_context *icx) if (icx->ib_dirty) { /* FIXME: Error handling!!! */ - ntfs_ib_write(icx, icx->ib_vcn, icx->ib); + ntfs_ib_write(icx, icx->ib); } free(icx->ib); @@ -1059,13 +1059,13 @@ static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, dst->index.index_length = cpu_to_le32(tail_size + le32_to_cpu(dst->index.entries_offset)); - ret = ntfs_ib_write(icx, new_vcn, dst); + ret = ntfs_ib_write(icx, dst); free(dst); return ret; } -static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, +static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *ib, INDEX_ENTRY *ie) { char *ies_start, *ies_end; @@ -1073,8 +1073,8 @@ static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, ntfs_log_trace("Entering\n"); - ies_start = (char *)ntfs_ie_get_first(&src->index); - ies_end = (char *)ntfs_ie_get_end(&src->index); + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); if (ie_last->ie_flags & INDEX_ENTRY_NODE) @@ -1082,10 +1082,10 @@ static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); - src->index.index_length = cpu_to_le32(((char *)ie - ies_start) + - le16_to_cpu(ie->length) + le32_to_cpu(src->index.entries_offset)); + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); - if (ntfs_ib_write(icx, sle64_to_cpu(((INDEX_BLOCK *)src)->index_block_vcn), src)) + if (ntfs_ib_write(icx, ib)) return STATUS_ERROR; return STATUS_OK; @@ -1147,7 +1147,7 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) goto clear_bmp; } - if (ntfs_ib_write(icx, new_ib_vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto clear_bmp; ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); @@ -1374,7 +1374,7 @@ static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) goto err_out; - if (ntfs_ib_write(icx, old_vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto err_out; err = STATUS_OK; @@ -1549,7 +1549,7 @@ static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) ntfs_inode_mark_dirty(icx->actx->ntfs_ino); else - if (ntfs_ib_write(icx, ntfs_icx_parent_vcn(icx), ib)) + if (ntfs_ib_write(icx, ib)) goto out; ntfs_index_ctx_reinit(icx); @@ -1751,7 +1751,7 @@ descend: if (ntfs_index_rm_leaf(icx)) goto out2; } else - if (ntfs_ib_write(icx, vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto out2; ret = STATUS_OK; From 5ef093294dcf2eff37c10ff62bd9994c8458883f Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 14:51:40 +0000 Subject: [PATCH 096/328] remove unused ib_vcn field from ntfs_index_context --- include/ntfs-3g/index.h | 6 ++---- libntfs-3g/index.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index aa81b0b4..26fcb1b0 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -48,7 +48,6 @@ * @actx: attribute search context if in root or NULL otherwise * @ia: index block if @is_in_root is FALSE or NULL otherwise * @ia_na: opened INDEX_ALLOCATION attribute - * @ib_vcn: VCN from which @ia where read from * @parent_pos: parent entries' positions in the index block * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT * @new_vcn: new VCN if we need to create a new index block @@ -64,11 +63,11 @@ * simply points into @entry. This is probably what the user is interested in. * * If @is_in_root is TRUE, @entry is in the index root attribute @ir described - * by the attribute search context @actx and inode @ni. @ia, @ib_vcn and + * by the attribute search context @actx and inode @ni. @ia and * @ib_dirty are undefined in this case. * * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia - * and @ib_vcn point to the index allocation block and VCN where it's placed, + * point to the index allocation block and VCN where it's placed, * respectively. @ir and @actx are NULL in this case. @ia_na is opened * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and * FALSE otherwise. @@ -96,7 +95,6 @@ typedef struct { INDEX_BLOCK *ib; ntfs_attr *ia_na; int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ - VCN ib_vcn; VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ int pindex; /* maximum it's the number of the parent nodes */ BOOL ib_dirty; diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index a5b83a75..0e46c950 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -763,7 +763,7 @@ descend_into_child_node: /* STATUS_OK or STATUS_NOT_FOUND */ icx->is_in_root = FALSE; icx->ib = ib; - icx->parent_vcn[icx->pindex] = icx->ib_vcn = vcn; + icx->parent_vcn[icx->pindex] = vcn; goto done; } From 490f7e49b83419960864af656866b09a72012bc2 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 15:27:34 +0000 Subject: [PATCH 097/328] ntfs_ir_lookup(): set attribute search context pointer to NULL on error paths to prevent potential double free()'s --- libntfs-3g/index.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 0e46c950..ec3b2c16 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -451,8 +451,10 @@ static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); err_out: - if (!ir) + if (!ir) { ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } return ir; } @@ -659,8 +661,7 @@ static int ntfs_icx_parent_dec(ntfs_index_context *icx) * the call to ntfs_index_ctx_put() to ensure that the changes are written * to disk. */ -int ntfs_index_lookup(const void *key, const int key_len, - ntfs_index_context *icx) +int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) { VCN old_vcn, vcn; ntfs_inode *ni = icx->ni; From dc1677b58e6df634ebed968b3ab3f72325300577 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 16:43:05 +0000 Subject: [PATCH 098/328] ntfs_index_lookup(): fix potential double free() on error paths --- libntfs-3g/index.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index ec3b2c16..eb8a7476 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -668,7 +668,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; - ntfs_attr_search_ctx *actx; int ret, err = 0; ntfs_log_trace("Entering\n"); @@ -679,7 +678,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic return -1; } - ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &actx); + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); if (!ir) { if (errno == ENOENT) errno = EIO; @@ -718,7 +717,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic goto err_out; } - icx->actx = actx; icx->ir = ir; if (ret != STATUS_KEEP_SEARCHING) { @@ -782,8 +780,6 @@ err_out: free(ib); if (!err) err = EIO; - if (actx) - ntfs_attr_put_search_ctx(actx); errno = err; return -1; done: From 4d9c861176f85bf688c2860c1e41801293b384cf Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 16:55:04 +0000 Subject: [PATCH 099/328] ntfs_index_lookup(): fix potential double free()'s on error paths --- libntfs-3g/index.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index eb8a7476..d7359eb8 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -147,18 +147,14 @@ static void ntfs_index_ctx_free(ntfs_index_context *icx) if (icx->actx) ntfs_attr_put_search_ctx(icx->actx); - if (icx->is_in_root) { - if (icx->ia_na) - ntfs_attr_close(icx->ia_na); - return; - } - - if (icx->ib_dirty) { - /* FIXME: Error handling!!! */ - ntfs_ib_write(icx, icx->ib); + if (!icx->is_in_root) { + if (icx->ib_dirty) { + /* FIXME: Error handling!!! */ + ntfs_ib_write(icx, icx->ib); + } + free(icx->ib); } - free(icx->ib); ntfs_attr_close(icx->ia_na); } @@ -775,8 +771,6 @@ descend_into_child_node: goto descend_into_child_node; err_out: - if (icx->ia_na) - ntfs_attr_close(icx->ia_na); free(ib); if (!err) err = EIO; From 56e2ae17928f24293953ec9f2f9abf24aba6ed2d Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 2 Feb 2008 16:58:47 +0000 Subject: [PATCH 100/328] release 1.2202-BETA --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index c5c4b971..3dd1ee71 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2129],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=21 +AC_INIT([ntfs-3g],[1.2202-BETA],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=22 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 65996b036a4a99f1fcd82e97652e4c8a8c3c10d0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 3 Feb 2008 16:35:25 +0000 Subject: [PATCH 101/328] add 'remove_hiberfile' mount option to be able rw mount hibernated volumes --- include/ntfs-3g/volume.h | 5 ++++- libntfs-3g/volume.c | 15 +++++++++------ src/ntfs-3g.c | 22 ++++++++++++++++++++-- src/utils.c | 11 ++++++++--- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index c00e5d56..fc2973aa 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -4,7 +4,7 @@ * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -56,6 +56,8 @@ #define MS_FORCE 0x10000000 #endif +#define MS_IGNORE_HIBERFILE 0x20000000 + /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; @@ -233,6 +235,7 @@ extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); extern int ntfs_umount(ntfs_volume *vol, const BOOL force); extern int ntfs_version_is_supported(ntfs_volume *vol); +extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); extern int ntfs_logfile_reset(ntfs_volume *vol); extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index b90a48e6..d159494e 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -2,7 +2,7 @@ * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -675,7 +675,7 @@ out: * Return: 0 if Windows isn't hibernated for sure * -1 otherwise and errno is set to the appropriate value */ -static int ntfs_volume_check_hiberfile(ntfs_volume *vol) +int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) { ntfs_inode *ni; ntfs_attr *na = NULL; @@ -705,13 +705,15 @@ static int ntfs_volume_check_hiberfile(ntfs_volume *vol) goto out; } if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { - ntfs_log_error("Hibernated non-system partition, refused to " - "mount.\n"); + if (verbose) + ntfs_log_error("Hibernated non-system partition, " + "refused to mount.\n"); errno = EPERM; goto out; } if (memcmp(buf, "hibr", 4) == 0) { - ntfs_log_error("Windows is hibernated, refused to mount.\n"); + if (verbose) + ntfs_log_error("Windows is hibernated, refused to mount.\n"); errno = EPERM; goto out; } @@ -1098,7 +1100,8 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * We care only about read-write mounts. */ if (!(flags & MS_RDONLY)) { - if (ntfs_volume_check_hiberfile(vol) < 0) + if (!(flags & MS_IGNORE_HIBERFILE) && + ntfs_volume_check_hiberfile(vol, 1) < 0) goto error_exit; if (ntfs_volume_check_logfile(vol) < 0) { if (!(flags & MS_FORCE)) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index f932773c..d40a22ca 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -127,6 +127,7 @@ typedef struct { BOOL show_sys_files; BOOL silent; BOOL force; + BOOL hiberfile; BOOL debug; BOOL no_detach; BOOL blkdev; @@ -159,8 +160,9 @@ static const char *usage_msg = "\n" "Usage: %s [-o option[,...]]\n" "\n" -"Options: ro, force, locale=, uid=, gid=, umask=, fmask=, dmask=,\n" -" streams_interface=. Please see details in the manual.\n" +"Options: ro (read-only mount), force, remove_hiberfile, locale=,\n" +" uid=, gid=, umask=, fmask=, dmask=, streams_interface=.\n" +" Please see the details in the manual.\n" "\n" "Example: ntfs-3g /dev/sda1 /mnt/win -o force\n" "\n" @@ -1633,6 +1635,8 @@ static int ntfs_open(const char *device) flags |= MS_RDONLY; if (ctx->force) flags |= MS_FORCE; + if (ctx->hiberfile) + flags |= MS_IGNORE_HIBERFILE; ctx->vol = ntfs_mount(device, flags); if (!ctx->vol) { @@ -1652,6 +1656,13 @@ static int ntfs_open(const char *device) goto err_out; } + if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) { + if (errno != EPERM) + goto err_out; + if (ntfs_fuse_rm("/hiberfil.sys")) + goto err_out; + } + errno = 0; err_out: return ntfs_volume_error(errno); @@ -1809,6 +1820,13 @@ static char *parse_mount_options(const char *orig_opts) goto err_exit; } ctx->force = TRUE; + } else if (!strcmp(opt, "remove_hiberfile")) { + if (val) { + ntfs_log_error("'remove_hiberfile' option " + "should not have value.\n"); + goto err_exit; + } + ctx->hiberfile = TRUE; } else if (!strcmp(opt, "locale")) { if (!val) { ntfs_log_error("'locale' option should have " diff --git a/src/utils.c b/src/utils.c index 2627d83f..734d952c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -4,7 +4,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2003-2006 Anton Altaparmakov * Copyright (c) 2003 Lode Leroy - * Copyright (c) 2005-2007 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * A set of shared functions for ntfs utilities * @@ -62,7 +62,12 @@ static const char *corrupt_volume_msg = static const char *hibernated_volume_msg = "The NTFS partition is hibernated. Please resume and shutdown Windows\n" -"properly, so mounting could be done safely.\n"; +"properly, or mount the volume read-only with the 'ro' mount option, or\n" +"mount the volume read-write with the 'remove_hiberfile' mount option.\n" +"For example type on the command line:\n" +"\n" +" mount -t ntfs-3g %s %s -o remove_hiberfile\n" +"\n"; static const char *unclean_journal_msg = "Mount is denied because NTFS is marked to be in use. Choose one action:\n" @@ -124,7 +129,7 @@ void utils_mount_error(const char *volume, const char *mntpoint, int err) ntfs_log_error("%s", corrupt_volume_msg); break; case NTFS_VOLUME_HIBERNATED: - ntfs_log_error("%s", hibernated_volume_msg); + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); break; case NTFS_VOLUME_UNCLEAN_UNMOUNT: ntfs_log_error(unclean_journal_msg); From 2636da890bd750c508303108816f61ebe4a0c840 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 3 Feb 2008 16:39:13 +0000 Subject: [PATCH 102/328] fix: create mount.ntfs-3g symlink always in /sbin where mount(8) needs it --- src/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index af22dadc..c7da069c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,12 +39,12 @@ endif install-exec-local: $(INSTALL) -d "$(DESTDIR)$(rootsbindir)" - $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)$(rootsbindir)/mount.ntfs-3g" + $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)/sbin/mount.ntfs-3g" install-data-local: $(INSTALL) -d "$(DESTDIR)$(man8dir)" $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" uninstall-local: - $(RM) -f "$(DESTDIR)$(rootsbindir)/mount.ntfs-3g" + $(RM) -f "$(DESTDIR)/sbin/mount.ntfs-3g" $(RM) -f "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" From 0c9f67f315efbb3a0ee059464b04f1691113438a Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 5 Feb 2008 22:18:39 +0000 Subject: [PATCH 103/328] remove never used fuse code related to the 'nonempty' mount option --- libfuse-lite/fusermount.c | 7 ------- libfuse-lite/mount.c | 9 --------- libfuse-lite/mount_util.c | 32 -------------------------------- libfuse-lite/mount_util.h | 2 -- 4 files changed, 50 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 06a226c1..fbe9abb6 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -378,7 +378,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, char *subtype = NULL; char *source = NULL; char *type = NULL; - int check_empty = 1; int blkdev = 0; optbuf = (char *) malloc(strlen(opts) + 128); @@ -400,8 +399,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, goto err; } else if (opt_eq(s, len, "blkdev")) { blkdev = 1; - } else if (opt_eq(s, len, "nonempty")) { - check_empty = 0; } else if (!begins_with(s, "fd=") && !begins_with(s, "rootmode=") && !begins_with(s, "user_id=") && @@ -451,10 +448,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i", fd, rootmode, getuid(), getgid()); - if (check_empty && - fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1) - goto err; - source = malloc((fsname ? strlen(fsname) : 0) + (subtype ? strlen(subtype) : 0) + strlen(dev) + 32); diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 2c95e5eb..b011b3df 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -44,7 +44,6 @@ struct mount_opts { int allow_root; int ishelp; int flags; - int nonempty; int blkdev; char *fsname; char *subtype; @@ -59,13 +58,11 @@ struct mount_opts { static const struct fuse_opt fuse_mount_opts[] = { FUSE_MOUNT_OPT("allow_other", allow_other), FUSE_MOUNT_OPT("allow_root", allow_root), - FUSE_MOUNT_OPT("nonempty", nonempty), FUSE_MOUNT_OPT("blkdev", blkdev), FUSE_MOUNT_OPT("fsname=%s", fsname), FUSE_MOUNT_OPT("subtype=%s", subtype), FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), - FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT), @@ -244,12 +241,6 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, return -1; } - if (!mo->nonempty) { - res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode, stbuf.st_size); - if (res == -1) - return -1; - } - fd = open(devname, O_RDWR); if (fd == -1) { if (errno == ENODEV || errno == ENOENT) diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c index 05098293..ddd81f7b 100644 --- a/libfuse-lite/mount_util.c +++ b/libfuse-lite/mount_util.c @@ -191,38 +191,6 @@ char *fuse_mnt_resolve_path(const char *progname, const char *orig) return dst; } -int fuse_mnt_check_empty(const char *progname, const char *mnt, - mode_t rootmode, off_t rootsize) -{ - int isempty = 1; - - if (S_ISDIR(rootmode)) { - struct dirent *ent; - DIR *dp = opendir(mnt); - if (dp == NULL) { - fprintf(stderr, "%s: failed to open mountpoint for reading: %s\n", - progname, strerror(errno)); - return -1; - } - while ((ent = readdir(dp)) != NULL) { - if (strcmp(ent->d_name, ".") != 0 && - strcmp(ent->d_name, "..") != 0) { - isempty = 0; - break; - } - } - closedir(dp); - } else if (rootsize) - isempty = 0; - - if (!isempty) { - fprintf(stderr, "%s: mountpoint is not empty\n", progname); - fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname); - return -1; - } - return 0; -} - int fuse_mnt_check_fuseblk(void) { char buf[256]; diff --git a/libfuse-lite/mount_util.h b/libfuse-lite/mount_util.h index b7350213..a7559675 100644 --- a/libfuse-lite/mount_util.h +++ b/libfuse-lite/mount_util.h @@ -12,8 +12,6 @@ int fuse_mnt_add_mount(const char *progname, const char *fsname, const char *mnt, const char *type, const char *opts); int fuse_mnt_umount(const char *progname, const char *mnt, int lazy); char *fuse_mnt_resolve_path(const char *progname, const char *orig); -int fuse_mnt_check_empty(const char *progname, const char *mnt, - mode_t rootmode, off_t rootsize); int fuse_mnt_check_fuseblk(void); int fusermount(int unmount, int quiet, int lazy, const char *opts, From 434ae034a058b7c7d2431b9d9ad5610b95329006 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 5 Feb 2008 22:22:29 +0000 Subject: [PATCH 104/328] do_mount(): remove unused rootsize argument --- libfuse-lite/fusermount.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index fbe9abb6..c88955e9 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -366,7 +366,7 @@ static int get_string_opt(const char *s, unsigned len, const char *opt, static int do_mount(const char *mnt, char **typep, mode_t rootmode, int fd, const char *opts, const char *dev, char **sourcep, - char **mnt_optsp, off_t rootsize) + char **mnt_optsp) { int res; int flags = MS_NOSUID | MS_NODEV; @@ -717,7 +717,7 @@ static int mount_fuse(const char *mnt, const char *opts) restore_privs(); if (res != -1) res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, - dev, &source, &mnt_opts, stbuf.st_size); + dev, &source, &mnt_opts); } else restore_privs(); From f56ee252c76b5466fb4ffe7a466c1c5a8e0b047a Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 5 Feb 2008 23:05:34 +0000 Subject: [PATCH 105/328] remove unused fuse subtype support --- libfuse-lite/fusermount.c | 30 ++++------------------- libfuse-lite/helper.c | 32 ------------------------- libfuse-lite/mount.c | 50 ++++----------------------------------- 3 files changed, 8 insertions(+), 104 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index c88955e9..8c351e95 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -375,7 +375,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, const char *s; char *d; char *fsname = NULL; - char *subtype = NULL; char *source = NULL; char *type = NULL; int blkdev = 0; @@ -389,14 +388,10 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, for (s = opts, d = optbuf; *s;) { unsigned len; const char *fsname_str = "fsname="; - const char *subtype_str = "subtype="; for (len = 0; s[len] && s[len] != ','; len++); if (begins_with(s, fsname_str)) { if (!get_string_opt(s, len, fsname_str, &fsname)) goto err; - } else if (begins_with(s, subtype_str)) { - if (!get_string_opt(s, len, subtype_str, &subtype)) - goto err; } else if (opt_eq(s, len, "blkdev")) { blkdev = 1; } else if (!begins_with(s, "fd=") && @@ -448,38 +443,22 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i", fd, rootmode, getuid(), getgid()); - source = malloc((fsname ? strlen(fsname) : 0) + - (subtype ? strlen(subtype) : 0) + strlen(dev) + 32); + source = malloc((fsname ? strlen(fsname) : 0) + strlen(dev) + 32); - type = malloc((subtype ? strlen(subtype) : 0) + 32); + type = malloc(32); if (!type || !source) { fprintf(stderr, "%s: failed to allocate memory\n", progname); goto err; } - if (subtype) - sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype); - else - strcpy(type, blkdev ? "fuseblk" : "fuse"); + strcpy(type, blkdev ? "fuseblk" : "fuse"); if (fsname) strcpy(source, fsname); else - strcpy(source, subtype ? subtype : dev); + strcpy(source, dev); res = mount(source, mnt, type, flags, optbuf); - if (res == -1 && errno == ENODEV && subtype) { - /* Probably missing subtype support */ - strcpy(type, blkdev ? "fuseblk" : "fuse"); - if (fsname) { - if (!blkdev) - sprintf(source, "%s#%s", subtype, fsname); - } else { - strcpy(source, type); - } - - res = mount(source, mnt, type, flags, optbuf); - } if (res == -1 && errno == EINVAL) { /* It could be an old version not supporting group_id */ sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid()); @@ -507,7 +486,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, err: free(fsname); - free(subtype); free(source); free(type); free(mnt_opts); diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c index b0b6775c..3029e173 100644 --- a/libfuse-lite/helper.c +++ b/libfuse-lite/helper.c @@ -40,7 +40,6 @@ static const struct fuse_opt fuse_helper_opts[] = { FUSE_HELPER_OPT("-f", foreground), FUSE_HELPER_OPT("-s", singlethread), FUSE_HELPER_OPT("fsname=", nodefault_subtype), - FUSE_HELPER_OPT("subtype=", nodefault_subtype), FUSE_OPT_KEY("-h", KEY_HELP), FUSE_OPT_KEY("--help", KEY_HELP), @@ -50,7 +49,6 @@ static const struct fuse_opt fuse_helper_opts[] = { FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), - FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), FUSE_OPT_END }; @@ -118,27 +116,6 @@ static int fuse_helper_opt_proc(void *data, const char *arg, int key, } } -static int add_default_subtype(const char *progname, struct fuse_args *args) -{ - int res; - char *subtype_opt; - const char *prog_basename = strrchr(progname, '/'); - if (prog_basename == NULL) - prog_basename = progname; - else if (prog_basename[1] != '\0') - prog_basename++; - - subtype_opt = (char *) malloc(strlen(prog_basename) + 64); - if (subtype_opt == NULL) { - fprintf(stderr, "fuse: memory allocation failed\n"); - return -1; - } - sprintf(subtype_opt, "-osubtype=%s", prog_basename); - res = fuse_opt_add_arg(args, subtype_opt); - free(subtype_opt); - return res; -} - int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, int *multithreaded, int *foreground) { @@ -150,11 +127,6 @@ int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, if (res == -1) return -1; - if (!hopts.nodefault_subtype) { - res = add_default_subtype(args->argv[0], args); - if (res == -1) - goto err; - } if (mountpoint) *mountpoint = hopts.mountpoint; else @@ -165,10 +137,6 @@ int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, if (foreground) *foreground = hopts.foreground; return 0; - - err: - free(hopts.mountpoint); - return -1; } int fuse_daemonize(int foreground) diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index b011b3df..ad0d8813 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -46,8 +46,6 @@ struct mount_opts { int flags; int blkdev; char *fsname; - char *subtype; - char *subtype_opt; char *mtab_opts; char *fusermount_opts; char *kernel_opts; @@ -60,12 +58,10 @@ static const struct fuse_opt fuse_mount_opts[] = { FUSE_MOUNT_OPT("allow_root", allow_root), FUSE_MOUNT_OPT("blkdev", blkdev), FUSE_MOUNT_OPT("fsname=%s", fsname), - FUSE_MOUNT_OPT("subtype=%s", subtype), FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), - FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT), FUSE_OPT_KEY("large_read", KEY_KERN_OPT), FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), @@ -159,9 +155,6 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key, case KEY_FUSERMOUNT_OPT: return fuse_opt_add_opt(&mo->fusermount_opts, arg); - case KEY_SUBTYPE_OPT: - return fuse_opt_add_opt(&mo->subtype_opt, arg); - case KEY_MTAB_OPT: return fuse_opt_add_opt(&mo->mtab_opts, arg); @@ -260,35 +253,18 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, goto out_close; source = malloc((mo->fsname ? strlen(mo->fsname) : 0) + - (mo->subtype ? strlen(mo->subtype) : 0) + strlen(devname) + 32); - type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32); + type = malloc(32); if (!type || !source) { fprintf(stderr, "fuse: failed to allocate memory\n"); goto out_close; } - strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); - if (mo->subtype) { - strcat(type, "."); - strcat(type, mo->subtype); - } - strcpy(source, - mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname)); + strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); + strcpy(source, mo->fsname ? mo->fsname : devname); res = mount(source, mnt, type, mo->flags, mo->kernel_opts); - if (res == -1 && errno == ENODEV && mo->subtype) { - /* Probably missing subtype support */ - strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); - if (mo->fsname) { - if (!mo->blkdev) - sprintf(source, "%s#%s", mo->subtype, mo->fsname); - } else { - strcpy(source, type); - } - res = mount(source, mnt, type, mo->flags, mo->kernel_opts); - } if (res == -1) { /* * Maybe kernel doesn't support unprivileged mounts, in this @@ -381,30 +357,12 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1) goto out; - if (mo.subtype) { - char *tmp_opts = NULL; - - res = -1; - if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 || - fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) { - free(tmp_opts); - goto out; - } - - res = fuse_mount_fusermount(mountpoint, tmp_opts); - free(tmp_opts); - if (res == -1) - res = fuse_mount_fusermount(mountpoint, mnt_opts); - } else { - res = fuse_mount_fusermount(mountpoint, mnt_opts); - } + res = fuse_mount_fusermount(mountpoint, mnt_opts); } out: free(mnt_opts); free(mo.fsname); - free(mo.subtype); free(mo.fusermount_opts); - free(mo.subtype_opt); free(mo.kernel_opts); free(mo.mtab_opts); return res; From 7b17f7daf495a995afa20d10f68e41b989263ad1 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 9 Feb 2008 14:46:48 +0000 Subject: [PATCH 106/328] remove user= option handling because fuse-lite will take care about it --- src/ntfs-3g.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index d40a22ca..c0439bdf 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -69,7 +69,6 @@ #include #include #include -#include #ifdef HAVE_SETXATTR #include @@ -1682,7 +1681,6 @@ static char *parse_mount_options(const char *orig_opts) * +1 null-terminator * +21 ,blkdev,blksize=65536 * +20 ,default_permissions - * +70 ,user= * +PATH_MAX resolved realpath() device name */ ret = ntfs_malloc(strlen(def_opts) + strlen(orig_opts) + 256 + PATH_MAX); @@ -2188,27 +2186,6 @@ static void set_fuseblk_options(char *parsed_options) strcat(parsed_options, options); } -static void set_user_mount_option(char *parsed_options, uid_t uid) -{ - struct passwd *pw; - char option[64]; - - if (!uid) - return; - - errno = 0; - pw = getpwuid(uid); - if (!pw || !pw->pw_name) { - ntfs_log_perror("WARNING: could not get username for uid %lld, " - "unprivileged unmount may fail", (long long)uid); - return; - } - - /* parsed_options already allocated enough space. */ - snprintf(option, sizeof(option), ",user=%s", pw->pw_name); - strcat(parsed_options, option); -} - #ifndef FUSE_INTERNAL static int set_uid(uid_t uid) { @@ -2349,11 +2326,9 @@ int main(int argc, char *argv[]) if (err) goto err_out; - if (ctx->blkdev) { - /* Must do after ntfs_open() to set the right blksize. */ + /* We must do this after ntfs_open() to be able to set the blksize */ + if (ctx->blkdev) set_fuseblk_options(parsed_options); - set_user_mount_option(parsed_options, getuid()); - } fh = mount_fuse(parsed_options); if (!fh) { From de7a19352ed423fcb59ce7f7ca98f05a42240dc2 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 9 Feb 2008 15:18:25 +0000 Subject: [PATCH 107/328] ntfs_log_set_handler(): log as ntfs-3g --- libntfs-3g/logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index c34bc2b0..beb96548 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -291,7 +291,7 @@ void ntfs_log_set_handler(ntfs_log_handler *handler) ntfs_log.handler = handler; #ifdef HAVE_SYSLOG_H if (handler == ntfs_log_handler_syslog) - openlog("libntfs", LOG_PID, LOG_USER); + openlog("ntfs-3g", LOG_PID, LOG_USER); #endif } else ntfs_log.handler = ntfs_log_handler_null; From 9a457b16818f2d6ecef94539e71006cbe9d6f86d Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 10 Feb 2008 13:34:14 +0000 Subject: [PATCH 108/328] forced_mount_msg: drop 'defaults' as many people mistyped it to 'defualts' --- src/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.c b/src/utils.c index 734d952c..9fe1aa64 100644 --- a/src/utils.c +++ b/src/utils.c @@ -100,7 +100,7 @@ static const char *forced_mount_msg = "\n" " Or add the option to the relevant row in the /etc/fstab file:\n" "\n" -" %s %s ntfs-3g defaults,force 0 0\n"; +" %s %s ntfs-3g force 0 0\n"; /** * utils_set_locale From 8f278649ac0ad78e89b87bb8badfa43327357bbd Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 10 Feb 2008 14:35:44 +0000 Subject: [PATCH 109/328] document remove_hiberfile mount option --- src/ntfs-3g.8.in | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 8664859b..9e86541d 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 2006-2008 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G 8 "January 2008" "ntfs-3g @VERSION@" +.TH NTFS-3G 8 "February 2008" "ntfs-3g @VERSION@" .SH NAME ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS @@ -113,6 +113,14 @@ Force the mounting even if the NTFS logfile is unclean. The logfile will be unconditionally cleared. Use this option with caution and for your own responsibility. .TP +.B remove_hiberfile +Unlike in case of read-only mount, the read-write mount is denied if +the NTFS volume is hibernated. One needs either to resume Windows and +shutdown it properly, or use this option which will remove the Windows +hibernation file. Please note, this means that the saved Windows +session will be completely lost. Use this option for your own +responsibility. +.TP .B atime, noatime, relatime The .B atime From 7b638b31d67a5dec6b167ead0e0bb1090a36efed Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 10 Feb 2008 14:37:29 +0000 Subject: [PATCH 110/328] explain how certain mount denials can be overcome by mount options --- src/ntfs-3g.probe.8.in | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.probe.8.in b/src/ntfs-3g.probe.8.in index 3e8a2678..73198ef5 100644 --- a/src/ntfs-3g.probe.8.in +++ b/src/ntfs-3g.probe.8.in @@ -1,7 +1,7 @@ .\" Copyright (c) 2008 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G.PROBE 8 "January 2008" "ntfs-3g.probe @VERSION@" +.TH NTFS-3G.PROBE 8 "February 2008" "ntfs-3g.probe @VERSION@" .SH NAME ntfs-3g.probe \- Probe an NTFS volume mountability .SH SYNOPSIS @@ -43,9 +43,14 @@ The volume doesn't have a valid NTFS. Inconsistent NTFS, hardware or device driver fault, or unsetup SoftRAID/FakeRAID hardware. .IP 14 -The NTFS partition is hibernated. +The NTFS partition is hibernated. Use the 'remove_hiberfile' +.B ntfs-3g +mount option to be able to read-write mount such volumes. .IP 15 -The volume was not cleanly unmounted. +The volume was not cleanly unmounted. The 'force' +.B ntfs-3g +mount option can overcome this scenario by resetting the NTFS +journal file. .IP 16 The volume is already exclusively opened and in use by a kernel driver or software. From 86b8a18d07e62aeac2a65092a3f6bfbd5de2f61c Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 10 Feb 2008 21:37:16 +0000 Subject: [PATCH 111/328] remove redundant fuse_mount_fusermount() --- libfuse-lite/mount.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index ad0d8813..52cf96bb 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -201,16 +201,6 @@ void fuse_kern_unmount(const char *mountpoint, int fd) fusermount(1, 0, 1, "", mountpoint); } -static int fuse_mount_fusermount(const char *mountpoint, const char *opts) -{ - if (!mountpoint) { - fprintf(stderr, "fuse: missing mountpoint\n"); - return -1; - } - - return fusermount(0, 0, 0, opts ? opts : "", mountpoint); -} - static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, const char *mnt_opts) { @@ -357,7 +347,7 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1) goto out; - res = fuse_mount_fusermount(mountpoint, mnt_opts); + res = fusermount(0, 0, 0, mnt_opts ? mnt_opts : "", mountpoint); } out: free(mnt_opts); From 3f2f5b34311fbb3f7a41b21d72cb506e8559061b Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 10 Feb 2008 21:40:43 +0000 Subject: [PATCH 112/328] fix command line parsing --- src/ntfs-3g.c | 273 +++++++++++++++++++------------------------- src/ntfs-3g.probe.c | 3 +- 2 files changed, 117 insertions(+), 159 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index c0439bdf..dab35970 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1668,74 +1668,98 @@ 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 = realloc(*dest, size_dest + size_append + 1); + if (!p) { + ntfs_log_perror("%s: Memory realloction failed", EXEC_NAME); + return -1; + } + + *dest = p; + strcpy(*dest + size_dest, append); + + return 0; +} + +static int bogus_option_value(char *val, 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, 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; + char *options, *s, *opt, *val, *ret = NULL; BOOL no_def_opts = FALSE; int default_permissions = 0; - /* - * FIXME: This is not pretty ... - * +7 fsname= - * +1 comma - * +1 null-terminator - * +21 ,blkdev,blksize=65536 - * +20 ,default_permissions - * +PATH_MAX resolved realpath() device name - */ - ret = ntfs_malloc(strlen(def_opts) + strlen(orig_opts) + 256 + PATH_MAX); - if (!ret) - return NULL; - - *ret = 0; - options = strdup(orig_opts); + options = strdup(orig_opts ? orig_opts : ""); if (!options) { - ntfs_log_perror("strdup failed"); + ntfs_log_perror("%s: strdup failed", EXEC_NAME); return NULL; } ctx->silent = TRUE; - - ctx->atime = ATIME_RELATIVE; + ctx->atime = ATIME_RELATIVE; s = options; while (s && *s && (val = strsep(&s, ","))) { opt = strsep(&val, "="); if (!strcmp(opt, "ro")) { /* Read-only mount. */ - if (val) { - ntfs_log_error("'ro' option should not have " - "value.\n"); + if (bogus_option_value(val, "ro")) goto err_exit; - } ctx->ro = TRUE; - strcat(ret, "ro,"); - } else if (!strcmp(opt, "noatime")) { - if (val) { - ntfs_log_error("'noatime' option should not " - "have value.\n"); + 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 (val) { - ntfs_log_error("'atime' option should not " - "have value.\n"); + if (bogus_option_value(val, "atime")) goto err_exit; - } ctx->atime = ATIME_ENABLED; } else if (!strcmp(opt, "relatime")) { - if (val) { - ntfs_log_error("'relatime' option should not " - "have value.\n"); + if (bogus_option_value(val, "relatime")) goto err_exit; - } ctx->atime = ATIME_RELATIVE; } else if (!strcmp(opt, "fake_rw")) { - if (val) { - ntfs_log_error("'fake_rw' option should not " - "have value.\n"); + if (bogus_option_value(val, "fake_rw")) goto err_exit; - } ctx->ro = TRUE; } else if (!strcmp(opt, "fsname")) { /* Filesystem name. */ /* @@ -1745,100 +1769,64 @@ static char *parse_mount_options(const char *orig_opts) ntfs_log_error("'fsname' is unsupported option.\n"); goto err_exit; } else if (!strcmp(opt, "no_def_opts")) { - if (val) { - ntfs_log_error("'no_def_opts' option should " - "not have value.\n"); + 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 (!val) { - ntfs_log_error("'umask' option should have " - "value.\n"); + if (missing_option_value(val, "umask")) goto err_exit; - } sscanf(val, "%o", &ctx->fmask); ctx->dmask = ctx->fmask; if (ctx->fmask) default_permissions = 1; } else if (!strcmp(opt, "fmask")) { - if (!val) { - ntfs_log_error("'fmask' option should have " - "value.\n"); + if (missing_option_value(val, "fmask")) goto err_exit; - } sscanf(val, "%o", &ctx->fmask); if (ctx->fmask) default_permissions = 1; } else if (!strcmp(opt, "dmask")) { - if (!val) { - ntfs_log_error("'dmask' option should have " - "value.\n"); + if (missing_option_value(val, "dmask")) goto err_exit; - } sscanf(val, "%o", &ctx->dmask); if (ctx->dmask) default_permissions = 1; } else if (!strcmp(opt, "uid")) { - if (!val) { - ntfs_log_error("'uid' option should have " - "value.\n"); + if (missing_option_value(val, "uid")) goto err_exit; - } sscanf(val, "%i", &ctx->uid); default_permissions = 1; } else if (!strcmp(opt, "gid")) { - if (!val) { - ntfs_log_error("'gid' option should have " - "value.\n"); + if (missing_option_value(val, "gid")) goto err_exit; - } sscanf(val, "%i", &ctx->gid); default_permissions = 1; } else if (!strcmp(opt, "show_sys_files")) { - if (val) { - ntfs_log_error("'show_sys_files' option should " - "not have value.\n"); + if (bogus_option_value(val, "show_sys_files")) goto err_exit; - } ctx->show_sys_files = TRUE; } else if (!strcmp(opt, "silent")) { - if (val) { - ntfs_log_error("'silent' option should " - "not have value.\n"); + if (bogus_option_value(val, "silent")) goto err_exit; - } ctx->silent = TRUE; } else if (!strcmp(opt, "force")) { - if (val) { - ntfs_log_error("'force' option should not " - "have value.\n"); + if (bogus_option_value(val, "force")) goto err_exit; - } ctx->force = TRUE; } else if (!strcmp(opt, "remove_hiberfile")) { - if (val) { - ntfs_log_error("'remove_hiberfile' option " - "should not have value.\n"); + if (bogus_option_value(val, "remove_hiberfile")) goto err_exit; - } ctx->hiberfile = TRUE; } else if (!strcmp(opt, "locale")) { - if (!val) { - ntfs_log_error("'locale' option should have " - "value.\n"); + if (missing_option_value(val, "locale")) goto err_exit; - } if (!setlocale(LC_ALL, val)) ntfs_log_error(locale_msg, val); } else if (!strcmp(opt, "streams_interface")) { - if (!val) { - ntfs_log_error("'streams_interface' option " - "should have value.\n"); + 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")) @@ -1853,20 +1841,14 @@ static char *parse_mount_options(const char *orig_opts) } else if (!strcmp(opt, "noauto")) { /* Don't pass noauto option to fuse. */ } else if (!strcmp(opt, "debug")) { - if (val) { - ntfs_log_error("'debug' option should not have " - "value.\n"); + 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 (val) { - ntfs_log_error("'no_detach' option should not " - "have value.\n"); + 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." @@ -1877,28 +1859,35 @@ static char *parse_mount_options(const char *orig_opts) ntfs_log_info("WARNING: blksize option is ignored " "because ntfs-3g must calculate it.\n"); } else { /* Probably FUSE option. */ - strcat(ret, opt); + if (strappend(&ret, opt)) + goto err_exit; if (val) { - strcat(ret, "="); - strcat(ret, val); + if (strappend(&ret, "=")) + goto err_exit; + if (strappend(&ret, val)) + goto err_exit; } - strcat(ret, ","); + if (strappend(&ret, ",")) + goto err_exit; } } - if (!no_def_opts) - strcat(ret, def_opts); - if (default_permissions) - strcat(ret, "default_permissions,"); + if (!no_def_opts && strappend(&ret, def_opts)) + goto err_exit; + if (default_permissions && strappend(&ret, "default_permissions,")) + goto err_exit; + s = "noatime,"; if (ctx->atime == ATIME_RELATIVE) - strcat(ret, "relatime,"); + s = "relatime,"; else if (ctx->atime == ATIME_ENABLED) - strcat(ret, "atime,"); - else - strcat(ret, "noatime,"); + s = "atime,"; + if (strappend(&ret, s)) + goto err_exit; - strcat(ret, "fsname="); - strcat(ret, opts.device); + if (strappend(&ret, "fsname=")) + goto err_exit; + if (strappend(&ret, opts.device)) + goto err_exit; exit: free(options); return ret; @@ -1924,35 +1913,6 @@ static char *realpath(const char *path, char *resolved_path) } #endif -static int strappend(char **dest, const char *append) -{ - char *p; - size_t size; - - if (!dest) - return -1; - if (!append) - return 0; - - size = strlen(append) + 1; - if (*dest) - size += strlen(*dest); - - p = realloc(*dest, size); - if (!p) { - ntfs_log_perror("Memory realloction failed"); - return -1; - } - - if (*dest) - strcat(p, append); - else - strcpy(p, append); - *dest = p; - - return 0; -} - /** * parse_options - Read and validate the programs command line * Read the command line, verify the syntax and parse the options. @@ -1981,18 +1941,14 @@ static int parse_options(int argc, char *argv[]) if (!opts.device) return -1; - /* We don't want relative path in /etc/mtab. */ - if (optarg[0] != '/') { - if (!realpath(optarg, opts.device)) { - ntfs_log_perror("%s: " - "Cannot mount '%s'", - EXEC_NAME, optarg); - free(opts.device); - opts.device = NULL; - return -1; - } - } else - strcpy(opts.device, optarg); + /* Canonicalize device name (mtab, etc) */ + if (!realpath(optarg, opts.device)) { + ntfs_log_perror("%s: realpath failed " + "for '%s'", EXEC_NAME, optarg); + free(opts.device); + opts.device = NULL; + return -1; + } } else if (!opts.mnt_point) { opts.mnt_point = optarg; } else { @@ -2168,7 +2124,7 @@ free_args: } -static void set_fuseblk_options(char *parsed_options) +static int set_fuseblk_options(char **parsed_options) { char options[64]; long pagesize; @@ -2181,9 +2137,10 @@ static void set_fuseblk_options(char *parsed_options) if (blksize > (u32)pagesize) blksize = pagesize; - /* parsed_options already allocated enough space. */ snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize); - strcat(parsed_options, options); + if (strappend(parsed_options, options)) + return -1; + return 0; } #ifndef FUSE_INTERNAL @@ -2299,7 +2256,7 @@ int main(int argc, char *argv[]) if (ntfs_fuse_init()) return NTFS_VOLUME_OUT_OF_MEMORY; - parsed_options = parse_mount_options(opts.options ? opts.options : ""); + parsed_options = parse_mount_options(opts.options); if (!parsed_options) { err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; @@ -2327,8 +2284,8 @@ int main(int argc, char *argv[]) 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); + if (ctx->blkdev && set_fuseblk_options(&parsed_options)) + goto err_out; fh = mount_fuse(parsed_options); if (!fh) { diff --git a/src/ntfs-3g.probe.c b/src/ntfs-3g.probe.c index a4088038..8981c064 100644 --- a/src/ntfs-3g.probe.c +++ b/src/ntfs-3g.probe.c @@ -111,7 +111,8 @@ static int parse_options(int argc, char *argv[]) if (!opts.device) return -1; - strcpy(opts.device, optarg); + strncpy(opts.device, optarg, PATH_MAX); + opts.device[PATH_MAX] = 0; } else { ntfs_log_error("%s: You must specify exactly " "one device\n", EXEC_NAME); From 6b52c58799a143edaade8d491a1ccb2dc01382a0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 16 Feb 2008 12:42:30 +0000 Subject: [PATCH 113/328] remove archaic FUSE kernel driver support --- libfuse-lite/fusermount.c | 81 ++++++++------------------------------- 1 file changed, 15 insertions(+), 66 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 8c351e95..bd80c1cc 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -27,9 +27,7 @@ #include #include -#define FUSE_DEV_OLD "/proc/fs/fuse/dev" #define FUSE_DEV_NEW "/dev/fuse" -#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version" #define FUSE_CONF "/etc/fuse.conf" #ifndef MS_DIRSYNC @@ -408,14 +406,16 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, if (res == 0 && sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 && (kmaj > 2 || (kmaj == 2 && kmin > 4))) { - fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin); + fprintf(stderr, "%s: note: 'large_read' mount option is " + "deprecated for %i.%i kernels\n", progname, kmaj, kmin); skip_option = 1; } } if (getuid() != 0 && !user_allow_other && (opt_eq(s, len, "allow_other") || opt_eq(s, len, "allow_root"))) { - fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s); + fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other'" + " is set in /etc/fuse.conf\n", progname, len, s); goto err; } if (!skip_option) { @@ -493,36 +493,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, return -1; } -static int check_version(const char *dev) -{ - int res; - int majorver; - int minorver; - const char *version_file; - FILE *vf; - - if (strcmp(dev, FUSE_DEV_OLD) != 0) - return 0; - - version_file = FUSE_VERSION_FILE_OLD; - vf = fopen(version_file, "r"); - if (vf == NULL) { - fprintf(stderr, "%s: kernel interface too old\n", progname); - return -1; - } - res = fscanf(vf, "%i.%i", &majorver, &minorver); - fclose(vf); - if (res != 2) { - fprintf(stderr, "%s: error reading %s\n", progname, version_file); - return -1; - } - if (majorver < 3) { - fprintf(stderr, "%s: kernel interface too old\n", progname); - return -1; - } - return 0; -} - static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, int *mountpoint_fd) { @@ -607,7 +577,7 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, return 0; } -static int try_open(const char *dev, char **devp, int silent) +static int try_open(const char *dev, char **devp) { int fd = open(dev, O_RDWR); if (fd != -1) { @@ -620,39 +590,22 @@ static int try_open(const char *dev, char **devp, int silent) } else if (errno == ENODEV || errno == ENOENT) /* check for ENOENT too, for the udev case */ return -2; - else if (!silent) { + else { fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev, strerror(errno)); } return fd; } -static int try_open_fuse_device(char **devp) -{ - int fd; - int err; - - drop_privs(); - fd = try_open(FUSE_DEV_NEW, devp, 0); - restore_privs(); - if (fd >= 0) - return fd; - - err = fd; - fd = try_open(FUSE_DEV_OLD, devp, 1); - if (fd >= 0) - return fd; - - return err; -} - static int open_fuse_device(char **devp) { - int fd = try_open_fuse_device(devp); + int fd; + + fd = try_open(FUSE_DEV_NEW, devp); if (fd >= -1) return fd; - fprintf(stderr, "%s: fuse device not found, try 'modprobe fuse' first\n", + fprintf(stderr, "%s: fuse device is missing, try 'modprobe fuse' as root\n", progname); return -1; @@ -689,15 +642,11 @@ static int mount_fuse(const char *mnt, const char *opts) } } - res = check_version(dev); - if (res != -1) { - res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd); - restore_privs(); - if (res != -1) - res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, - dev, &source, &mnt_opts); - } else - restore_privs(); + res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd); + restore_privs(); + if (res != -1) + res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, dev, + &source, &mnt_opts); if (currdir_fd != -1) { fchdir(currdir_fd); From fca849d3656c60bd1f2609c6d76c5c2b9cbea629 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 16 Feb 2008 14:25:23 +0000 Subject: [PATCH 114/328] fix unprivileged mount/unmount --- include/ntfs-3g/volume.h | 3 +- libfuse-lite/fusermount.c | 220 ++++++++++++++++++++------------------ libfuse-lite/mount.c | 119 +-------------------- src/ntfs-3g.c | 68 ++++++------ src/ntfs-3g.probe.8.in | 2 + src/utils.c | 6 +- 6 files changed, 163 insertions(+), 255 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index fc2973aa..67ca94f6 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -92,7 +92,8 @@ typedef enum { NTFS_VOLUME_UNKNOWN_REASON = 18, NTFS_VOLUME_NO_PRIVILEGE = 19, NTFS_VOLUME_OUT_OF_MEMORY = 20, - NTFS_VOLUME_FUSE_ERROR = 21 + NTFS_VOLUME_FUSE_ERROR = 21, + NTFS_VOLUME_INSECURE = 22 } ntfs_volume_status; /** diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index bd80c1cc..1bede1b5 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -26,6 +26,7 @@ #include #include #include +#include #define FUSE_DEV_NEW "/dev/fuse" #define FUSE_CONF "/etc/fuse.conf" @@ -50,23 +51,85 @@ static const char *get_user_name(void) } } -static uid_t oldfsuid; -static gid_t oldfsgid; - -static void drop_privs(void) +int drop_privs(void) { - if (getuid() != 0) { - oldfsuid = setfsuid(getuid()); - oldfsgid = setfsgid(getgid()); - } + if (!geteuid()) { + if (setgroups(0, NULL) < 0) { + perror("priv drop: setgroups failed"); + return -1; + } + } + + if (!getegid()) { + + gid_t new_gid = getgid(); + + if (setresgid(-1, new_gid, getegid()) < 0) { + perror("priv drop: setresgid failed"); + return -1; + } + if (getegid() != new_gid){ + perror("dropping group privilege failed"); + return -1; + } + } + + if (!geteuid()) { + + uid_t new_uid = getuid(); + + if (setresuid(-1, new_uid, geteuid()) < 0) { + perror("priv drop: setresuid failed"); + return -1; + } + if (geteuid() != new_uid){ + perror("dropping user privilege failed"); + return -1; + } + } + + return 0; } -static void restore_privs(void) +int restore_privs(void) { - if (getuid() != 0) { - setfsuid(oldfsuid); - setfsgid(oldfsgid); - } + if (geteuid()) { + + uid_t ruid, euid, suid; + + if (getresuid(&ruid, &euid, &suid) < 0) { + perror("priv restore: getresuid failed"); + return -1; + } + if (setresuid(-1, suid, -1) < 0) { + perror("priv restore: setresuid failed"); + return -1; + } + if (geteuid() != suid) { + perror("restoring privilege failed"); + return -1; + } + } + + if (getegid()) { + + gid_t rgid, egid, sgid; + + if (getresgid(&rgid, &egid, &sgid) < 0) { + perror("priv restore: getresgid failed"); + return -1; + } + if (setresgid(-1, sgid, -1) < 0) { + perror("priv restore: setresgid failed"); + return -1; + } + if (getegid() != sgid){ + perror("restoring group privilege failed"); + return -1; + } + } + + return 0; } #ifndef IGNORE_MTAB @@ -76,66 +139,6 @@ static int add_mount(const char *source, const char *mnt, const char *type, return fuse_mnt_add_mount(progname, source, mnt, type, opts); } -static int unmount_fuse(const char *mnt, int quiet, int lazy) -{ - if (getuid() != 0) { - struct mntent *entp; - FILE *fp; - const char *user = NULL; - char uidstr[32]; - unsigned uidlen = 0; - int found; - const char *mtab = _PATH_MOUNTED; - - user = get_user_name(); - if (user == NULL) - return -1; - - fp = setmntent(mtab, "r"); - if (fp == NULL) { - fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, - strerror(errno)); - return -1; - } - - uidlen = sprintf(uidstr, "%u", getuid()); - - found = 0; - while ((entp = getmntent(fp)) != NULL) { - if (!found && strcmp(entp->mnt_dir, mnt) == 0 && - (strcmp(entp->mnt_type, "fuse") == 0 || - strcmp(entp->mnt_type, "fuseblk") == 0 || - strncmp(entp->mnt_type, "fuse.", 5) == 0 || - strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) { - char *p = strstr(entp->mnt_opts, "user="); - if (p && (p == entp->mnt_opts || *(p-1) == ',') && - strcmp(p + 5, user) == 0) { - found = 1; - break; - } - /* /etc/mtab is a link pointing to /proc/mounts: */ - else if ((p = strstr(entp->mnt_opts, "user_id=")) && - (p == entp->mnt_opts || *(p-1) == ',') && - strncmp(p + 8, uidstr, uidlen) == 0 && - (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0')) { - found = 1; - break; - } - } - } - endmntent(fp); - - if (!found) { - if (!quiet) - fprintf(stderr, "%s: entry for %s not found in %s\n", progname, - mnt, mtab); - return -1; - } - } - - return fuse_mnt_umount(progname, mnt, lazy); -} - static int count_fuse_fs(void) { struct mntent *entp; @@ -172,11 +175,6 @@ static int add_mount(const char *source, const char *mnt, const char *type, (void) opts; return 0; } - -static int unmount_fuse(const char *mnt, int quiet, int lazy) -{ - return fuse_mnt_umount(progname, mnt, lazy); -} #endif /* IGNORE_MTAB */ static void strip_line(char *line) @@ -458,12 +456,19 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, else strcpy(source, dev); + if (restore_privs()) + goto err; + res = mount(source, mnt, type, flags, optbuf); if (res == -1 && errno == EINVAL) { /* It could be an old version not supporting group_id */ sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid()); res = mount(source, mnt, type, flags, optbuf); } + + if (drop_privs()) + goto err; + if (res == -1) { int errno_save = errno; if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) @@ -471,8 +476,9 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, else { fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save)); if (errno_save == EPERM) - fprintf(stderr, "No privilege to mount. Please see " - "http://ntfs-3g.org/support.html#useroption\n"); + fprintf(stderr, "User doesn't have privilege to mount. " + "For more information\nplease see: " + "http://ntfs-3g.org/support.html#unprivileged\n"); } goto err; } else { @@ -629,24 +635,20 @@ static int mount_fuse(const char *mnt, const char *opts) if (fd == -1) return -1; - drop_privs(); read_conf(); if (getuid() != 0 && mount_max != -1) { - int mount_count = count_fuse_fs(); - if (mount_count >= mount_max) { + if (count_fuse_fs() >= mount_max) { fprintf(stderr, "%s: too many FUSE filesystems mounted; " "mount_max=N can be set in /etc/fuse.conf\n", progname); - close(fd); - return -1; + goto err; } } res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd); - restore_privs(); if (res != -1) - res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, dev, - &source, &mnt_opts); + res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, dev, + &source, &mnt_opts); if (currdir_fd != -1) { fchdir(currdir_fd); @@ -655,59 +657,69 @@ static int mount_fuse(const char *mnt, const char *opts) if (mountpoint_fd != -1) close(mountpoint_fd); - if (res == -1) { - close(fd); - return -1; - } + if (res == -1) + goto err; + if (restore_privs()) + goto err; + if (geteuid() == 0) { res = add_mount(source, mnt, type, mnt_opts); if (res == -1) { umount2(mnt, 2); /* lazy umount */ - close(fd); - return -1; + drop_privs(); + goto err; } } + if (drop_privs()) + goto err; +out: free(source); free(type); free(mnt_opts); free(dev); return fd; +err: + close(fd); + fd = -1; + goto out; } int fusermount(int unmount, int quiet, int lazy, const char *opts, const char *origmnt) { - int res; + int res = -1; char *mnt; mode_t old_umask; - if (lazy && !unmount) { - fprintf(stderr, "%s: -z can only be used with -u\n", progname); - return -1; - } - - drop_privs(); mnt = fuse_mnt_resolve_path(progname, origmnt); - restore_privs(); if (mnt == NULL) return -1; old_umask = umask(033); + if (unmount) { + + if (restore_privs()) + goto out; + if (geteuid() == 0) - res = unmount_fuse(mnt, quiet, lazy); + res = fuse_mnt_umount(progname, mnt, lazy); else { res = umount2(mnt, lazy ? 2 : 0); if (res == -1 && !quiet) fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt, strerror(errno)); } + + if (drop_privs()) + res = -1; + } else res = mount_fuse(mnt, opts); - +out: umask(old_umask); return res; } diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 52cf96bb..712cdb53 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -189,114 +189,9 @@ void fuse_kern_unmount(const char *mountpoint, int fd) } close(fd); - if (geteuid() == 0) { - fuse_mnt_umount("fuse", mountpoint, 1); - return; - } - - res = umount2(mountpoint, 2); - if (res == 0) - return; - fusermount(1, 0, 1, "", mountpoint); } -static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, - const char *mnt_opts) -{ - char tmp[128]; - const char *devname = "/dev/fuse"; - char *source = NULL; - char *type = NULL; - struct stat stbuf; - int fd; - int res; - - if (!mnt) { - fprintf(stderr, "fuse: missing mountpoint\n"); - return -1; - } - - res = lstat(mnt, &stbuf); - if (res == -1) { - fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n", - mnt, strerror(errno)); - return -1; - } - - fd = open(devname, O_RDWR); - if (fd == -1) { - if (errno == ENODEV || errno == ENOENT) - fprintf(stderr, - "fuse: device not found, try 'modprobe fuse' first\n"); - else - fprintf(stderr, "fuse: failed to open %s: %s\n", devname, - strerror(errno)); - return -1; - } - - snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i", fd, - stbuf.st_mode & S_IFMT, getuid(), getgid()); - - res = fuse_opt_add_opt(&mo->kernel_opts, tmp); - if (res == -1) - goto out_close; - - source = malloc((mo->fsname ? strlen(mo->fsname) : 0) + - strlen(devname) + 32); - - type = malloc(32); - if (!type || !source) { - fprintf(stderr, "fuse: failed to allocate memory\n"); - goto out_close; - } - - strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); - strcpy(source, mo->fsname ? mo->fsname : devname); - - res = mount(source, mnt, type, mo->flags, mo->kernel_opts); - if (res == -1) { - /* - * Maybe kernel doesn't support unprivileged mounts, in this - * case try falling back to fusermount - */ - if (errno == EPERM) { - res = -2; - } else { - int errno_save = errno; - if (mo->blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) - fprintf(stderr, "fuse: 'fuseblk' support missing\n"); - else - fprintf(stderr, "fuse: mount failed: %s\n", - strerror(errno_save)); - } - - goto out_close; - } - - if (geteuid() == 0) { - char *newmnt = fuse_mnt_resolve_path("fuse", mnt); - res = -1; - if (!newmnt) - goto out_umount; - - res = fuse_mnt_add_mount("fuse", source, newmnt, type, mnt_opts); - free(newmnt); - if (res == -1) - goto out_umount; - } - - return fd; - - out_umount: - umount2(mnt, 2); /* lazy umount */ - out_close: - free(type); - free(source); - close(fd); - return res; -} - static int get_mnt_flag_opts(char **mnt_optsp, int flags) { int i; @@ -340,16 +235,12 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) goto out; if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1) goto out; + if (mo.fusermount_opts && fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) < 0) + goto out; - res = fuse_mount_sys(mountpoint, &mo, mnt_opts); - if (res == -2) { - if (mo.fusermount_opts && - fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1) - goto out; - - res = fusermount(0, 0, 0, mnt_opts ? mnt_opts : "", mountpoint); - } - out: + res = fusermount(0, 0, 0, mnt_opts ? mnt_opts : "", mountpoint); + +out: free(mnt_opts); free(mo.fsname); free(mo.fusermount_opts); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index dab35970..7fa73bd9 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -167,6 +167,24 @@ static const char *usage_msg = "\n" "%s"; +#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 an\n" +"external FUSE library. Either remove the setuid/setgid bit from the binary\n" +"or rebuild NTFS-3G with integrated FUSE support.\n"; +#endif + + /** * ntfs_fuse_is_named_data_stream - check path to be to named data stream * @path: path to check @@ -2143,35 +2161,11 @@ static int set_fuseblk_options(char **parsed_options) return 0; } -#ifndef FUSE_INTERNAL -static int set_uid(uid_t uid) -{ - if (setuid(uid)) { - ntfs_log_perror("Failed to set uid to %d", uid); - return NTFS_VOLUME_NO_PRIVILEGE; - } - return NTFS_VOLUME_OK; -} -#endif - static struct fuse *mount_fuse(char *parsed_options) { struct fuse *fh = NULL; struct fuse_args args = FUSE_ARGS_INIT(0, NULL); -#ifndef FUSE_INTERNAL - uid_t uid, euid; - /* - * We must raise privilege if possible, otherwise the user[s] fstab - * option doesn't work because mount(8) always drops privilege what - * the blkdev option requires. - */ - uid = getuid(); - euid = geteuid(); - - if (set_uid(euid)) - return NULL; -#endif /* Libfuse can't always find fusermount, so let's help it. */ if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) ntfs_log_perror("WARNING: Failed to set $PATH\n"); @@ -2194,15 +2188,6 @@ static struct fuse *mount_fuse(char *parsed_options) if (fuse_set_signal_handlers(fuse_get_session(fh))) goto err_destory; - /* - * We can't drop privilege if internal FUSE is used because internal - * unmount needs it. Kernel 2.6.25 may include unprivileged full - * mount/unmount support. - */ -#ifndef FUSE_INTERNAL - if (set_uid(uid)) - goto err_destory; -#endif out: fuse_opt_free_args(&args); return fh; @@ -2245,6 +2230,15 @@ int main(int argc, char *argv[]) struct stat sbuf; int err; +#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; + utils_set_locale(); ntfs_log_set_handler(ntfs_log_handler_stderr); @@ -2264,10 +2258,18 @@ int main(int argc, char *argv[]) #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)) { diff --git a/src/ntfs-3g.probe.8.in b/src/ntfs-3g.probe.8.in index 73198ef5..9ba4da6d 100644 --- a/src/ntfs-3g.probe.8.in +++ b/src/ntfs-3g.probe.8.in @@ -64,6 +64,8 @@ Not enough privilege to mount. Out of memory. .IP 21 Unclassified FUSE error. +.IP 22 +Security hazard. Execution is denied because it would be exploitable. .SH KNOWN ISSUES Please see .RS diff --git a/src/utils.c b/src/utils.c index 9fe1aa64..b57b0714 100644 --- a/src/utils.c +++ b/src/utils.c @@ -90,9 +90,9 @@ static const char *fakeraid_msg = "to mount NTFS. Please see the 'dmraid' documentation for help.\n"; static const char *access_denied_msg = -"Please check the device and the ntfs-3g binary permissions, the mounting\n" -"user and group ID, and the mount options. You can find more explanation\n" -"at http://ntfs-3g.org/support.html#useroption\n"; +"Please check the volume and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://ntfs-3g.org/support.html#unprivileged\n"; static const char *forced_mount_msg = "\n" From 6ab2973445d0e80836c58c972cd0c5bb971d16a2 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 16 Feb 2008 15:21:56 +0000 Subject: [PATCH 115/328] explain unprivileged block device mount failure with external FUSE --- src/ntfs-3g.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 7fa73bd9..08bdf7df 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -179,9 +179,16 @@ 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 an\n" +"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.\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 @@ -2247,8 +2254,10 @@ int main(int argc, char *argv[]) return NTFS_VOLUME_SYNTAX_ERROR; } - if (ntfs_fuse_init()) - return NTFS_VOLUME_OUT_OF_MEMORY; + if (ntfs_fuse_init()) { + err = NTFS_VOLUME_OUT_OF_MEMORY; + goto err2; + } parsed_options = parse_mount_options(opts.options); if (!parsed_options) { @@ -2281,6 +2290,12 @@ int main(int argc, char *argv[]) if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) ctx->blkdev = TRUE; +#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; @@ -2311,6 +2326,7 @@ int main(int argc, char *argv[]) fuse_destroy(fh); err_out: utils_mount_error(opts.device, opts.mnt_point, err); +err2: ntfs_close(); free(ctx); free(parsed_options); From d5d8acb2140bcd72160e3d856d2d5c38a8581a85 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 16 Feb 2008 16:01:52 +0000 Subject: [PATCH 116/328] don't obfuscate error message with usage help; explain better realpath error --- src/ntfs-3g.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 08bdf7df..9fd2a098 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1968,8 +1968,8 @@ static int parse_options(int argc, char *argv[]) /* Canonicalize device name (mtab, etc) */ if (!realpath(optarg, opts.device)) { - ntfs_log_perror("%s: realpath failed " - "for '%s'", EXEC_NAME, optarg); + ntfs_log_perror("%s: Failed to access " + "volume '%s'", EXEC_NAME, optarg); free(opts.device); opts.device = NULL; return -1; @@ -2250,7 +2250,8 @@ int main(int argc, char *argv[]) ntfs_log_set_handler(ntfs_log_handler_stderr); if (parse_options(argc, argv)) { - usage(); + ntfs_log_error("Please type '%s --help' for more " + "information.\n", argv[0]); return NTFS_VOLUME_SYNTAX_ERROR; } From 5617176383009e3b56632942a83271f9d9915a96 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 16 Feb 2008 17:05:38 +0000 Subject: [PATCH 117/328] fix compiler warnings --- libfuse-lite/fusermount.c | 3 +++ libntfs-3g/index.c | 2 +- src/ntfs-3g.c | 15 +++++++-------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 1bede1b5..43db8e16 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -40,6 +40,9 @@ static const char *progname = "ntfs-3g-mount"; static int user_allow_other = 0; static int mount_max = 1000; +int drop_privs(void); +int restore_privs(void); + static const char *get_user_name(void) { struct passwd *pw = getpwuid(getuid()); diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index d7359eb8..0057fb3f 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1763,7 +1763,7 @@ out: * * Return 0 on success or -1 on error with errno set to the error code. */ -int ntfs_index_rm(ntfs_index_context *icx) +static int ntfs_index_rm(ntfs_index_context *icx) { INDEX_HEADER *ih; int err, ret = STATUS_OK; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 9fd2a098..683e61f3 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1728,7 +1728,7 @@ static int strappend(char **dest, const char *append) return 0; } -static int bogus_option_value(char *val, char *s) +static int bogus_option_value(char *val, const char *s) { if (val) { ntfs_log_error("'%s' option shouldn't have value.\n", s); @@ -1737,7 +1737,7 @@ static int bogus_option_value(char *val, char *s) return 0; } -static int missing_option_value(char *val, char *s) +static int missing_option_value(char *val, const char *s) { if (!val) { ntfs_log_error("'%s' option should have a value.\n", s); @@ -1901,12 +1901,11 @@ static char *parse_mount_options(const char *orig_opts) if (default_permissions && strappend(&ret, "default_permissions,")) goto err_exit; - s = "noatime,"; - if (ctx->atime == ATIME_RELATIVE) - s = "relatime,"; - else if (ctx->atime == ATIME_ENABLED) - s = "atime,"; - if (strappend(&ret, s)) + 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 (strappend(&ret, "noatime,")) goto err_exit; if (strappend(&ret, "fsname=")) From 4ce572b79dabcfe712a573aaf55d0901614339fa Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 16 Feb 2008 17:12:44 +0000 Subject: [PATCH 118/328] release 1.2216 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 3dd1ee71..3489dc80 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2202-BETA],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=22 +AC_INIT([ntfs-3g],[1.2216],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=23 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From df0177485b975d32a02bc8ccc6d19a7148906b2b Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 8 Mar 2008 12:04:25 +0000 Subject: [PATCH 119/328] fix incorrect data and allocation sizes in index and inode FILE_NAME attributes for some resident unnamed $DATA attributes and for which CHKDSK reported minor inconsistencies. --- libntfs-3g/attrib.c | 4 ++++ libntfs-3g/dir.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 19b1b69f..e2c6790d 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2726,6 +2726,10 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, goto put_err_out; } } + if (type == AT_DATA && name == AT_UNNAMED) { + ni->data_size = size; + ni->allocated_size = (size + 7) & ~7; + } ntfs_inode_mark_dirty(ni); ntfs_attr_put_search_ctx(ctx); return offset; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index bed0d181..5e7b3131 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1203,6 +1203,8 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); fn->last_access_time = utc2ntfs(ni->last_access_time); + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { From 6ad4be98f66231118e39a35538bfda956dc85d9b Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 8 Mar 2008 13:08:12 +0000 Subject: [PATCH 120/328] ntfs_device_mount(): cleanup & more verbose error reporting --- libntfs-3g/volume.c | 68 +++++++++------------------------------------ 1 file changed, 13 insertions(+), 55 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index d159494e..e82d2480 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -426,10 +426,6 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) ntfs_volume *vol; NTFS_BOOT_SECTOR *bs; int eo; -#ifdef DEBUG - const char *OK = "OK\n"; - const char *FAILED = "FAILED\n"; -#endif if (!dev || !dev->d_ops || !dev->d_name) { errno = EINVAL; @@ -469,7 +465,6 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) /* Now read the bootsector. */ br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); if (br != sizeof(NTFS_BOOT_SECTOR)) { - ntfs_log_debug(FAILED); if (br != -1) errno = EINVAL; if (!br) @@ -755,10 +750,6 @@ out: ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) { s64 l; -#ifdef DEBUG - const char *OK = "OK\n"; - const char *FAILED = "FAILED\n"; -#endif ntfs_volume *vol; u8 *m = NULL, *m2 = NULL; ntfs_attr_search_ctx *ctx = NULL; @@ -802,7 +793,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) } vol->mftmirr_size = l; } - ntfs_log_debug("Comparing $MFTMirr to $MFT... "); + ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); for (i = 0; i < vol->mftmirr_size; ++i) { MFT_RECORD *mrec, *mrec2; const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", @@ -820,14 +811,12 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); if (mrec->flags & MFT_RECORD_IN_USE) { if (ntfs_is_baad_recordp(mrec)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFT error: Incomplete multi " "sector transfer detected in " "'%s'.\n", s); goto io_error_exit; } if (!ntfs_is_mft_recordp(mrec)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFT error: Invalid mft " "record for '%s'.\n", s); goto io_error_exit; @@ -836,62 +825,53 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); if (mrec2->flags & MFT_RECORD_IN_USE) { if (ntfs_is_baad_recordp(mrec2)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFTMirr error: Incomplete " "multi sector transfer " "detected in '%s'.\n", s); goto io_error_exit; } if (!ntfs_is_mft_recordp(mrec2)) { - ntfs_log_debug("FAILED\n"); ntfs_log_error("$MFTMirr error: Invalid mft " "record for '%s'.\n", s); goto io_error_exit; } } if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { - ntfs_log_debug(FAILED); ntfs_log_error("$MFTMirr does not match $MFT (record " "%d).\n", i); goto io_error_exit; } } - ntfs_log_debug(OK); free(m2); free(m); m = m2 = NULL; /* Now load the bitmap from $Bitmap. */ - ntfs_log_debug("Loading $Bitmap... "); + ntfs_log_debug("Loading $Bitmap...\n"); vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); if (!vol->lcnbmp_ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); + ntfs_log_perror("Failed to open inode FILE_Bitmap"); goto error_exit; } /* Get an ntfs attribute for $Bitmap/$DATA. */ vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); if (!vol->lcnbmp_na) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } /* Done with the $Bitmap mft record. */ - ntfs_log_debug(OK); /* Now load the upcase table from $UpCase. */ - ntfs_log_debug("Loading $UpCase... "); + ntfs_log_debug("Loading $UpCase...\n"); ni = ntfs_inode_open(vol, FILE_UpCase); if (!ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); + ntfs_log_perror("Failed to open inode FILE_UpCase"); goto error_exit; } /* Get an ntfs attribute for $UpCase/$DATA. */ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } @@ -902,7 +882,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * characters. */ if (na->data_size & ~0x1ffffffffULL) { - ntfs_log_debug(FAILED); ntfs_log_error("Error: Upcase table is too big (max 32-bit " "allowed).\n"); errno = EINVAL; @@ -913,15 +892,12 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) /* Throw away default table. */ free(vol->upcase); vol->upcase = ntfs_malloc(na->data_size); - if (!vol->upcase) { - ntfs_log_debug(FAILED); + if (!vol->upcase) goto error_exit; - } } /* Read in the $DATA attribute value into the buffer. */ l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); if (l != na->data_size) { - ntfs_log_debug(FAILED); ntfs_log_error("Failed to read $UpCase, unexpected length " "(%lld != %lld).\n", (long long)l, (long long)na->data_size); @@ -929,7 +905,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; } /* Done with the $UpCase mft record. */ - ntfs_log_debug(OK); ntfs_attr_close(na); if (ntfs_inode_close(ni)) { ntfs_log_perror("Failed to close $UpCase"); @@ -940,11 +915,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * Now load $Volume and set the version information and flags in the * vol structure accordingly. */ - ntfs_log_debug("Loading $Volume... "); + ntfs_log_debug("Loading $Volume...\n"); vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); if (!vol->vol_ni) { - ntfs_log_debug(FAILED); - ntfs_log_perror("Failed to open inode"); + ntfs_log_perror("Failed to open inode FILE_Volume"); goto error_exit; } /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ @@ -955,7 +929,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) /* Find the $VOLUME_INFORMATION attribute. */ if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_log_debug(FAILED); ntfs_log_perror("$VOLUME_INFORMATION attribute not found in " "$Volume"); goto error_exit; @@ -963,7 +936,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) a = ctx->attr; /* Has to be resident. */ if (a->non_resident) { - ntfs_log_debug(FAILED); ntfs_log_error("Attribute $VOLUME_INFORMATION must be " "resident but it isn't.\n"); errno = EIO; @@ -976,7 +948,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) le32_to_cpu(ctx->mrec->bytes_in_use) || le16_to_cpu(a->value_offset) + le32_to_cpu( a->value_length) > le32_to_cpu(a->length)) { - ntfs_log_debug(FAILED); ntfs_log_error("$VOLUME_INFORMATION in $Volume is corrupt.\n"); errno = EIO; goto error_exit; @@ -994,7 +965,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { if (errno != ENOENT) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to lookup of $VOLUME_NAME in " "$Volume failed"); goto error_exit; @@ -1005,16 +975,13 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * had zero length. */ vol->vol_name = ntfs_malloc(1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); + if (!vol->vol_name) goto error_exit; - } vol->vol_name[0] = '\0'; } else { a = ctx->attr; /* Has to be resident. */ if (a->non_resident) { - ntfs_log_debug(FAILED); ntfs_log_error("$VOLUME_NAME must be resident.\n"); errno = EIO; goto error_exit; @@ -1033,10 +1000,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) ntfs_log_debug("Forcing name into ASCII by replacing " "non-ASCII characters with underscores.\n"); vol->vol_name = ntfs_malloc(u + 1); - if (!vol->vol_name) { - ntfs_log_debug(FAILED); + if (!vol->vol_name) goto error_exit; - } + for (j = 0; j < (s32)u; j++) { ntfschar uc = le16_to_cpu(vname[j]); if (uc > 0xff) @@ -1046,27 +1012,23 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) vol->vol_name[u] = '\0'; } } - ntfs_log_debug(OK); ntfs_attr_put_search_ctx(ctx); ctx = NULL; /* Now load the attribute definitions from $AttrDef. */ - ntfs_log_debug("Loading $AttrDef... "); + ntfs_log_debug("Loading $AttrDef...\n"); ni = ntfs_inode_open(vol, FILE_AttrDef); if (!ni) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open $AttrDef"); goto error_exit; } /* Get an ntfs attribute for $AttrDef/$DATA. */ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { - ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } /* Check we don't overflow 32-bits. */ if (na->data_size > 0xffffffffLL) { - ntfs_log_debug(FAILED); ntfs_log_error("Attribute definition table is too big (max " "32-bit allowed).\n"); errno = EINVAL; @@ -1074,14 +1036,11 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) } vol->attrdef_len = na->data_size; vol->attrdef = ntfs_malloc(na->data_size); - if (!vol->attrdef) { - ntfs_log_debug(FAILED); + if (!vol->attrdef) goto error_exit; - } /* Read in the $DATA attribute value into the buffer. */ l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); if (l != na->data_size) { - ntfs_log_debug(FAILED); ntfs_log_error("Failed to read $AttrDef, unexpected length " "(%lld != %lld).\n", (long long)l, (long long)na->data_size); @@ -1089,7 +1048,6 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; } /* Done with the $AttrDef mft record. */ - ntfs_log_debug(OK); ntfs_attr_close(na); if (ntfs_inode_close(ni)) { ntfs_log_perror("Failed to close $AttrDef"); From 5fde1785fd7042fa1bf018436b3ec9a085631e5b Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 8 Mar 2008 15:35:01 +0000 Subject: [PATCH 121/328] fix driver crash when trying to open files having at least twice as long file name as allowed by NTFS (crash reported by Bernhard Kaindl) --- libntfs-3g/dir.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 5e7b3131..b4acc1f2 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -446,9 +446,8 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, } } - unicode = ntfs_calloc(MAX_PATH); ascii = strdup(pathname); - if (!unicode || !ascii) { + if (!ascii) { ntfs_log_debug("Out of memory.\n"); err = ENOMEM; goto close; @@ -466,7 +465,7 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, q++; } - len = ntfs_mbstoucs(p, &unicode, MAX_PATH); + len = ntfs_mbstoucs(p, &unicode, 0); if (len < 0) { ntfs_log_debug("Couldn't convert name to Unicode: %s.\n", p); err = errno; @@ -499,6 +498,9 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, goto close; } + free(unicode); + unicode = NULL; + p = q; while (p && *p && *p == PATH_SEP) p++; From 7cff8993b81f4bf787a1202ac4a2f0f550c693e4 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 8 Mar 2008 19:18:20 +0000 Subject: [PATCH 122/328] ntfs_mbstoucs(): remove unused and broken outs_len --- include/ntfs-3g/unistr.h | 2 +- libntfs-3g/dir.c | 2 +- libntfs-3g/unistr.c | 49 ++++++++++++---------------------------- libntfs-3g/volume.c | 2 +- src/ntfs-3g.c | 16 ++++++------- 5 files changed, 25 insertions(+), 46 deletions(-) diff --git a/include/ntfs-3g/unistr.h b/include/ntfs-3g/unistr.h index b45101e9..6de4bacc 100644 --- a/include/ntfs-3g/unistr.h +++ b/include/ntfs-3g/unistr.h @@ -57,7 +57,7 @@ extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, int outs_len); -extern int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len); +extern int ntfs_mbstoucs(const char *ins, ntfschar **outs); extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index b4acc1f2..d1829bbf 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -465,7 +465,7 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, q++; } - len = ntfs_mbstoucs(p, &unicode, 0); + len = ntfs_mbstoucs(p, &unicode); if (len < 0) { ntfs_log_debug("Couldn't convert name to Unicode: %s.\n", p); err = errno; diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index 4eb9dee9..5a16633c 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -491,17 +491,16 @@ err_out: * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string * @ins: input multibyte string buffer * @outs: on return contains the (allocated) output Unicode string - * @outs_len: length of output buffer in Unicode characters * * Convert the input multibyte string @ins, from the current locale into the * corresponding little endian, 2-byte Unicode string. * - * If *@outs is NULL, the function allocates the string and the caller is - * responsible for calling free(*@outs); when finished with it. + * The function allocates the string and the caller is responsible for calling + * free(*@outs); when finished with it. * * On success the function returns the number of Unicode characters written to * the output string *@outs (>= 0), not counting the terminating Unicode NULL - * character. If the output string buffer was allocated, *@outs is set to it. + * character. * * On error, -1 is returned, and errno is set to the error code. The following * error codes can be expected: @@ -511,7 +510,7 @@ err_out: * ENAMETOOLONG Destination buffer is too small for input string. * ENOMEM Not enough memory to allocate destination buffer. */ -int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) +int ntfs_mbstoucs(const char *ins, ntfschar **outs) { ntfschar *ucs; const char *s; @@ -525,12 +524,7 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) errno = EINVAL; return -1; } - ucs = *outs; - ucs_len = outs_len; - if (ucs && !ucs_len) { - errno = ENAMETOOLONG; - return -1; - } + /* Determine the size of the multi-byte string in bytes. */ ins_size = strlen(ins); /* Determine the length of the multi-byte string. */ @@ -562,31 +556,21 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) } /* Add the NULL terminator. */ ins_len++; - if (!ucs) { - ucs_len = ins_len; - ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); - if (!ucs) - return -1; - } + ucs_len = ins_len; + ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); + if (!ucs) + return -1; #ifdef HAVE_MBSINIT memset(&mbstate, 0, sizeof(mbstate)); #else mbtowc(NULL, NULL, 0); #endif for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { - /* Reallocate memory if necessary or abort. */ + /* Reallocate memory if necessary. */ if (o >= ucs_len) { ntfschar *tc; - if (ucs == *outs) { - errno = ENAMETOOLONG; - return -1; - } - /* - * We will never get here but hey, it's only a bit of - * extra code... - */ ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; - tc = (ntfschar*)realloc(ucs, ucs_len); + tc = realloc(ucs, ucs_len); if (!tc) goto err_out; ucs = tc; @@ -626,15 +610,10 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len) #endif /* Now write the NULL character. */ ucs[o] = cpu_to_le16(L'\0'); - if (*outs != ucs) - *outs = ucs; + *outs = ucs; return o; err_out: - if (ucs != *outs) { - int eo = errno; - free(ucs); - errno = eo; - } + free(ucs); return -1; } @@ -725,7 +704,7 @@ ntfschar *ntfs_str2ucs(const char *s, int *len) { ntfschar *ucs = NULL; - if (s && ((*len = ntfs_mbstoucs(s, &ucs, 0)) == -1)) { + if (s && ((*len = ntfs_mbstoucs(s, &ucs)) == -1)) { ntfs_log_perror("Couldn't convert '%s' to Unicode", s); return NULL; } diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e82d2480..43f085a3 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -632,7 +632,7 @@ static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) return NULL; } - unicode_len = ntfs_mbstoucs(hiberfile, &unicode, 0); + unicode_len = ntfs_mbstoucs(hiberfile, &unicode); if (unicode_len < 0) { ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); goto out; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 683e61f3..5a807ffd 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -321,7 +321,7 @@ static int ntfs_fuse_parse_path(const char *org_path, char **path, *path = strsep(&stream_name_mbs, ":"); if (stream_name_mbs) { *stream_name = NULL; - res = ntfs_mbstoucs(stream_name_mbs, stream_name, 0); + res = ntfs_mbstoucs(stream_name_mbs, stream_name); if (res < 0) return -errno; return res; @@ -827,7 +827,7 @@ static int ntfs_fuse_create(const char *org_path, dev_t type, dev_t dev, /* Generate unicode filename. */ name = strrchr(path, '/'); name++; - uname_len = ntfs_mbstoucs(name, &uname, 0); + uname_len = ntfs_mbstoucs(name, &uname); if (uname_len < 0) { res = -errno; goto exit; @@ -847,7 +847,7 @@ static int ntfs_fuse_create(const char *org_path, dev_t type, dev_t dev, dev); break; case S_IFLNK: - utarget_len = ntfs_mbstoucs(target, &utarget, 0); + utarget_len = ntfs_mbstoucs(target, &utarget); if (utarget_len < 0) { res = -errno; goto exit; @@ -985,7 +985,7 @@ static int ntfs_fuse_link(const char *old_path, const char *new_path) /* Generate unicode filename. */ name = strrchr(path, '/'); name++; - uname_len = ntfs_mbstoucs(name, &uname, 0); + uname_len = ntfs_mbstoucs(name, &uname); if (uname_len < 0) { res = -errno; goto exit; @@ -1039,7 +1039,7 @@ static int ntfs_fuse_rm(const char *org_path) /* Generate unicode filename. */ name = strrchr(path, '/'); name++; - uname_len = ntfs_mbstoucs(name, &uname, 0); + uname_len = ntfs_mbstoucs(name, &uname); if (uname_len < 0) { res = -errno; goto exit; @@ -1456,7 +1456,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, ni = ntfs_pathname_to_inode(vol, NULL, path); if (!ni) return -errno; - lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename, 0); + lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename); if (lename_len == -1) { res = -errno; goto exit; @@ -1504,7 +1504,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, ni = ntfs_pathname_to_inode(vol, NULL, path); if (!ni) return -errno; - lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename, 0); + lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename); if (lename_len == -1) { res = -errno; goto exit; @@ -1562,7 +1562,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) ni = ntfs_pathname_to_inode(vol, NULL, path); if (!ni) return -errno; - lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename, 0); + lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename); if (lename_len == -1) { res = -errno; goto exit; From 0bafbaf9d3d681f417f8a92f3979560667bc8f7c Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 8 Mar 2008 20:21:13 +0000 Subject: [PATCH 123/328] more explanatory error messages when mount fails --- libntfs-3g/volume.c | 2 +- src/utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 43f085a3..c25e7442 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -456,7 +456,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) /* ...->open needs bracketing to compile with glibc 2.7 */ if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { - ntfs_log_perror("Error opening partition device"); + ntfs_log_perror("Error opening '%s'", dev->d_name); goto error_exit; } /* Attach the device to the volume. */ diff --git a/src/utils.c b/src/utils.c index b57b0714..cb01ac53 100644 --- a/src/utils.c +++ b/src/utils.c @@ -90,7 +90,7 @@ static const char *fakeraid_msg = "to mount NTFS. Please see the 'dmraid' documentation for help.\n"; static const char *access_denied_msg = -"Please check the volume and the ntfs-3g binary permissions,\n" +"Please check '%s' and the ntfs-3g binary permissions,\n" "and the mounting user ID. More explanation is provided at\n" "http://ntfs-3g.org/support.html#unprivileged\n"; From 4926692ea093aebbacd31f8220d7c00b07a7bdc7 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 8 Mar 2008 20:29:20 +0000 Subject: [PATCH 124/328] add missing COPYING.LIB, reported by Bernhard Kaindl --- COPYING.LIB | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 COPYING.LIB diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 00000000..161a3d1d --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,482 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! From 3d65fcca1ed2d29a00e4b28afbda36268b766117 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 8 Mar 2008 20:42:59 +0000 Subject: [PATCH 125/328] fix: unprivileged mount failed if user had rights for everything except the fuse device --- libfuse-lite/fusermount.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 43db8e16..c321ce29 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -588,7 +588,13 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, static int try_open(const char *dev, char **devp) { - int fd = open(dev, O_RDWR); + int fd; + + if (restore_privs()) + return -1; + fd = open(dev, O_RDWR); + if (drop_privs()) + return -1; if (fd != -1) { *devp = strdup(dev); if (*devp == NULL) { From e11c9971a68c12779312832b2fba4cba5c34e3ca Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 9 Mar 2008 13:08:17 +0000 Subject: [PATCH 126/328] rmdir returns ENOTEMPTY instead of EEXIST because glib/nautilus can't handle the latter standard errno value --- libntfs-3g/dir.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index d1829bbf..14716770 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1313,7 +1313,7 @@ int ntfs_check_empty_dir(ntfs_inode *ni) /* Non-empty directory? */ if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){ /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ - errno = EEXIST; + errno = ENOTEMPTY; ntfs_log_debug("Directory is not empty\n"); ret = -1; } @@ -1328,7 +1328,7 @@ static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) int ret; ret = ntfs_check_empty_dir(ni); - if (!ret || errno != EEXIST) + if (!ret || errno != ENOTEMPTY) return ret; /* * Directory is non-empty, so we can unlink only if there is more than @@ -1336,7 +1336,7 @@ static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) */ if ((link_count == 1) || (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) { - errno = EEXIST; + errno = ENOTEMPTY; ntfs_log_debug("Non-empty directory without hard links\n"); goto no_hardlink; } From 376bd8dfc812dc60264154d2aaae2e5249e14387 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 9 Mar 2008 13:11:25 +0000 Subject: [PATCH 127/328] fix 'dev' and 'suid' option handling and make them default for root mounts like other file systems do. They are always denied for setuid-root and unprivileged mounts (Bernhard Kaindl, Szabolcs Szakacsits) --- libfuse-lite/fusermount.c | 2 +- libfuse-lite/mount.c | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index c321ce29..b0f16c19 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -276,7 +276,7 @@ static int find_mount_flag(const char *s, unsigned len, int *on, int *flag) *flag = mount_flags[i].flag; if (!mount_flags[i].safe && getuid() != 0) { *flag = 0; - fprintf(stderr, "%s: unsafe option %s ignored\n", + fprintf(stderr, "%s: unsafe option '%s' ignored\n", progname, opt); } return 1; diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 712cdb53..0bb3aee5 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -214,7 +214,8 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) char *mnt_opts = NULL; memset(&mo, 0, sizeof(mo)); - mo.flags = MS_NOSUID | MS_NODEV; + if (getuid()) + mo.flags = MS_NOSUID | MS_NODEV; if (args && fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) @@ -231,6 +232,10 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) res = -1; if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1) goto out; + if (!(mo.flags & MS_NODEV) && fuse_opt_add_opt(&mnt_opts, "dev") == -1) + goto out; + if (!(mo.flags & MS_NOSUID) && fuse_opt_add_opt(&mnt_opts, "suid") == -1) + goto out; if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1) goto out; if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1) From 18b507d9ab7df38cc123d21646f5bae5d859d60a Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 9 Mar 2008 13:16:25 +0000 Subject: [PATCH 128/328] remove unused code (Bernhard Kaindl) --- libfuse-lite/fuse_lowlevel.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index 847bd917..3df9d265 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -1305,16 +1305,3 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, return fuse_lowlevel_new_common(args, op, op_size, userdata); } - -struct fuse_ll_compat_conf { - unsigned max_read; - int set_max_read; -}; - -static const struct fuse_opt fuse_ll_opts_compat[] = { - { "max_read=", offsetof(struct fuse_ll_compat_conf, set_max_read), 1 }, - { "max_read=%u", offsetof(struct fuse_ll_compat_conf, max_read), 0 }, - FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP), - FUSE_OPT_END -}; - From 02c4a2efdea2293679258e15dad6d6ea3f2ed8ac Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 9 Mar 2008 13:29:29 +0000 Subject: [PATCH 129/328] mount_fuse(): remove obsolete workaround to be able to find fusermount. This piece of code resulted open source write NTFS. R.I.P. --- src/ntfs-3g.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 5a807ffd..1085b817 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2172,10 +2172,6 @@ static struct fuse *mount_fuse(char *parsed_options) struct fuse *fh = NULL; struct fuse_args args = FUSE_ARGS_INIT(0, NULL); - /* Libfuse can't always find fusermount, so let's help it. */ - if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) - ntfs_log_perror("WARNING: Failed to set $PATH\n"); - ctx->fc = try_fuse_mount(parsed_options); if (!ctx->fc) return NULL; From 72c32e0c30fc0797aeadf9f4dae94f6a13318e3d Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 9 Mar 2008 13:32:07 +0000 Subject: [PATCH 130/328] release 1.2310 --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 3489dc80..be8014d3 100644 --- a/configure.ac +++ b/configure.ac @@ -3,8 +3,8 @@ # compilation. # # Copyright (c) 2000-2006 Anton Altaparmakov -# Copyright (c) 2005-2007 Szabolcs Szakacsits -# Copyright (C) 2007 Alon Bar-Lev +# Copyright (c) 2005-2008 Szabolcs Szakacsits +# Copyright (C) 2007-2008 Alon Bar-Lev # # This program/include file is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as published @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2216],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=23 +AC_INIT([ntfs-3g],[1.2310],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=24 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From ca890bec0a4e632608a18c89e5f85e8508c56b1a Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 23 Mar 2008 14:02:04 +0000 Subject: [PATCH 131/328] fuse_lib_opendir(): fix missing pthread_mutex_destroy() in error path --- libfuse-lite/fuse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 1820f26b..1eeed443 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -1813,6 +1813,7 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, } } else { reply_err(req, err); + pthread_mutex_destroy(&dh->lock); free(dh); } free(path); From 6d8f0033360db1eb6e6c7644e1c9b414d0188080 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 23 Mar 2008 14:09:35 +0000 Subject: [PATCH 132/328] do_mount(): fix one time, fsname size memory leak --- libfuse-lite/fusermount.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index b0f16c19..3b494c12 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -489,17 +489,16 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, *typep = type; *mnt_optsp = mnt_opts; } - free(optbuf); - - return res; - - err: +out: free(fsname); + free(optbuf); + return res; +err: free(source); free(type); free(mnt_opts); - free(optbuf); - return -1; + res = -1; + goto out; } static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, From f2b8efc36a3abaa8ada37835b406b8f9e0af646a Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 23 Mar 2008 14:33:26 +0000 Subject: [PATCH 133/328] fusermount(): fix a mount and unmount time mountpoint size memory leak --- libfuse-lite/fusermount.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 3b494c12..0a36f1c9 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -729,5 +729,6 @@ int fusermount(int unmount, int quiet, int lazy, const char *opts, res = mount_fuse(mnt, opts); out: umask(old_umask); + free(mnt); return res; } From 3edc27d598529088a6ae02f04d455800a83d1e26 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 24 Mar 2008 21:08:31 +0000 Subject: [PATCH 134/328] add COPYING file explicitely because autogen.sh doesn't create it automatic since COPYING.LIB was added (Tom "spot" Callaway, Szabolcs Szakacsits) --- COPYING | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..623b6258 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. From c78280181d93f03357bd85fdccee6cae4d368f41 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 30 Mar 2008 20:00:38 +0000 Subject: [PATCH 135/328] ntfs_create_symlink(): log error details --- libntfs-3g/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 14716770..e6440723 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1288,7 +1288,8 @@ ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, ntfschar *target, u8 target_len) { if (!target || !target_len) { - ntfs_log_error("Invalid arguments.\n"); + ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, + target, target_len); return NULL; } return __ntfs_create(dir_ni, name, name_len, S_IFLNK, 0, From 2a3d44e3d6364b3d950b503d86252cb332eb014b Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 30 Mar 2008 20:10:56 +0000 Subject: [PATCH 136/328] fix: 'noatime' additionally and unconditionally was passed to fuse --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 1085b817..a78eda70 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1905,7 +1905,7 @@ static char *parse_mount_options(const char *orig_opts) goto err_exit; else if (ctx->atime == ATIME_ENABLED && strappend(&ret, "atime,")) goto err_exit; - else if (strappend(&ret, "noatime,")) + else if (ctx->atime == ATIME_DISABLED && strappend(&ret, "noatime,")) goto err_exit; if (strappend(&ret, "fsname=")) From c231d73e19f936a21cee32aa61b35a6a78f30ec3 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 30 Mar 2008 20:22:14 +0000 Subject: [PATCH 137/328] fix: hard link attribute update was delayed by one second --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a78eda70..f4059d68 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2178,7 +2178,7 @@ static struct fuse *mount_fuse(char *parsed_options) if (fuse_opt_add_arg(&args, "") == -1) goto err; - if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache") == -1) + if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=0") == -1) goto err; if (ctx->debug) if (fuse_opt_add_arg(&args, "-odebug") == -1) From d9b16355329e5cd6403f3647967f1779a5c003b9 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 2 Apr 2008 10:28:49 +0000 Subject: [PATCH 138/328] fix: symlink failed or the size was truncated at modulo 256 if the target was over 255 bytes --- include/ntfs-3g/dir.h | 2 +- libntfs-3g/dir.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index 33e1b2e1..cbf52683 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -73,7 +73,7 @@ extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, dev_t type, dev_t dev); extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, - ntfschar *name, u8 name_len, ntfschar *target, u8 target_len); + ntfschar *name, u8 name_len, ntfschar *target, int target_len); extern int ntfs_check_empty_dir(ntfs_inode *ni); extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len); diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index e6440723..affcca26 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1036,7 +1036,7 @@ err_out: */ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, dev_t type, dev_t dev, - ntfschar *target, u8 target_len) + ntfschar *target, int target_len) { ntfs_inode *ni; int rollback_data = 0, rollback_sd = 0; @@ -1285,7 +1285,7 @@ ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, } ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, - ntfschar *target, u8 target_len) + ntfschar *target, int target_len) { if (!target || !target_len) { ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, From b06ba6ed113d5d71176e1640849d6ce563c8e887 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 2 Apr 2008 11:46:38 +0000 Subject: [PATCH 139/328] ntfs_make_room_for_attr(): "no space" condition must be ENOSPC, not EINVAL --- libntfs-3g/attrib.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index e2c6790d..bed3755b 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2585,9 +2585,9 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) size = (size + 7) & ~7; /* Rigorous consistency checks. */ - if (!m || !pos || pos < (u8*)m || pos + size > - (u8*)m + le32_to_cpu(m->bytes_allocated)) { + if (!m || !pos || pos < (u8*)m) { errno = EINVAL; + ntfs_log_perror("%s: pos=%p m=%p", __FUNCTION__, pos, m); return -1; } /* The -8 is for the attribute terminator. */ @@ -2601,7 +2601,8 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) biu = le32_to_cpu(m->bytes_in_use); /* Do we have enough space? */ - if (biu + size > le32_to_cpu(m->bytes_allocated)) { + if (biu + size > le32_to_cpu(m->bytes_allocated) || + pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) { errno = ENOSPC; ntfs_log_trace("No enough space in the MFT record\n"); return -1; From e518ef07adc12a013f97f5f42f10ab3069802219 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 2 Apr 2008 17:20:23 +0000 Subject: [PATCH 140/328] ntfs_attr_add(): fix adding attribute which can be resident and non-resident --- libntfs-3g/attrib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index bed3755b..4e628020 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3047,6 +3047,7 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, u32 attr_rec_size; int err, i, offset; BOOL is_resident; + BOOL can_be_non_resident = FALSE; ntfs_inode *attr_ni; ntfs_attr *na; @@ -3089,7 +3090,8 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, ntfs_log_perror("Attribute is too big"); return -1; } - } + } else + can_be_non_resident = TRUE; /* * Determine resident or not will be new attribute. We add 8 to size in @@ -3162,6 +3164,8 @@ add_attr_record: offset = ntfs_resident_attr_record_add(attr_ni, type, name, name_len, val, size, 0); if (offset < 0) { + if (errno == ENOSPC && can_be_non_resident) + goto add_non_resident; err = errno; ntfs_log_perror("Failed to add resident attribute"); goto free_err_out; @@ -3169,6 +3173,7 @@ add_attr_record: return 0; } +add_non_resident: /* Add non resident attribute. */ offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, name_len, 0, 8, 0); From ec83e7062a0bce2aa9ed080b1736b09d6a4e4f80 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 2 Apr 2008 17:30:12 +0000 Subject: [PATCH 141/328] fix: symlink failed if the target was over 1024 bytes --- src/ntfs-3g.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index f4059d68..3bc23d2b 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -404,10 +404,11 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) * Check whether it's Interix symbolic link, block or * character device. */ - if (na->data_size <= sizeof(INTX_FILE_TYPES) + sizeof( - ntfschar) * MAX_PATH && na->data_size > - sizeof(INTX_FILE_TYPES) && - !stream_name_len) { + if (na->data_size <= sizeof(INTX_FILE_TYPES) + + sizeof(ntfschar) * PATH_MAX && + na->data_size > sizeof(INTX_FILE_TYPES) && + !stream_name_len) { + INTX_FILE *intx_file; intx_file = ntfs_malloc(na->data_size); @@ -501,7 +502,7 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size) goto exit; } if (na->data_size > sizeof(INTX_FILE_TYPES) + - sizeof(ntfschar) * MAX_PATH) { + sizeof(ntfschar) * PATH_MAX) { res = -ENAMETOOLONG; goto exit; } From f90aa79b2e5156447c43fe70bad461e6f045d5e0 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 9 Apr 2008 00:37:36 +0000 Subject: [PATCH 142/328] remove redundant user_allow_other support: mount is denied for unprivileged mounts unless ntfs-3g is suid-root in which case the user can control the access by the uid/gid and fmask/dmask/umask options. --- libfuse-lite/fusermount.c | 12 +----------- src/ntfs-3g.8.in | 15 +++------------ 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 0a36f1c9..21f02255 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -37,7 +37,6 @@ static const char *progname = "ntfs-3g-mount"; -static int user_allow_other = 0; static int mount_max = 1000; int drop_privs(void); @@ -195,9 +194,7 @@ static void strip_line(char *line) static void parse_line(char *line, int linenum) { int tmp; - if (strcmp(line, "user_allow_other") == 0) - user_allow_other = 1; - else if (sscanf(line, "mount_max = %i", &tmp) == 1) + if (sscanf(line, "mount_max = %i", &tmp) == 1) mount_max = tmp; else if(line[0]) fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n", @@ -412,13 +409,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, skip_option = 1; } } - if (getuid() != 0 && !user_allow_other && - (opt_eq(s, len, "allow_other") || - opt_eq(s, len, "allow_root"))) { - fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other'" - " is set in /etc/fuse.conf\n", progname, len, s); - goto err; - } if (!skip_option) { if (find_mount_flag(s, len, &on, &flag)) { if (on) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 9e86541d..8ccfe455 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 2006-2008 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G 8 "February 2008" "ntfs-3g @VERSION@" +.TH NTFS-3G 8 "April 2008" "ntfs-3g @VERSION@" .SH NAME ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS @@ -46,10 +46,7 @@ Windows users have full access to the files created by If .B ntfs-3g is set setuid-root then non-root users will -be also able to mount volumes and via /etc/fstab if the 'user' -or 'users' -.BR mount (8) -option is specified. +be also able to mount volumes. .SS Windows Filename Compatibility NTFS supports several filename namespaces: DOS, Win32 and POSIX. While the \fBntfs-3g\fR driver handles all of them, it always creates new files in the @@ -153,12 +150,6 @@ Furthermore, irrespectively of show_sys_files, all files are accessible by name, for example you can always do "ls \-l '$UpCase'". .TP -.B allow_other -This option overrides the security measure restricting file access -to the user mounting the filesystem. This option is only -allowed to root, but this restriction can be overridden by -the 'user_allow_other' option in the /etc/fuse.conf file. -.TP .BI max_read= value With this option the maximum size of read operations can be set. The default is infinite. Note that the size of read requests is @@ -169,7 +160,7 @@ Do nothing on chmod and chown operations, but do not return error. This option is on by default. .TP .B no_def_opts -By default ntfs-3g acts as "silent,allow_other" was passed to it, +By default ntfs-3g acts as "silent" was passed to it, this option cancel this behaviour. .TP .BI streams_interface= value From 52968a021ea3ca348d6ddff10290fbf70b81ca89 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 10 Apr 2008 23:35:17 +0000 Subject: [PATCH 143/328] remove buggy /etc/fuse.conf parsing ("reading /etc/fuse.conf: line X too long" error when '\n' is missing) since it has no real purpose anymore anyway --- libfuse-lite/fusermount.c | 54 --------------------------------------- 1 file changed, 54 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 21f02255..7d3903aa 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -29,7 +29,6 @@ #include #define FUSE_DEV_NEW "/dev/fuse" -#define FUSE_CONF "/etc/fuse.conf" #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 @@ -179,57 +178,6 @@ static int add_mount(const char *source, const char *mnt, const char *type, } #endif /* IGNORE_MTAB */ -static void strip_line(char *line) -{ - char *s = strchr(line, '#'); - if (s != NULL) - s[0] = '\0'; - for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--); - s[1] = '\0'; - for (s = line; isspace((unsigned char) *s); s++); - if (s != line) - memmove(line, s, strlen(s)+1); -} - -static void parse_line(char *line, int linenum) -{ - int tmp; - if (sscanf(line, "mount_max = %i", &tmp) == 1) - mount_max = tmp; - else if(line[0]) - fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n", - progname, FUSE_CONF, linenum, line); -} - -static void read_conf(void) -{ - FILE *fp = fopen(FUSE_CONF, "r"); - if (fp != NULL) { - int linenum = 1; - char line[256]; - int isnewline = 1; - while (fgets(line, sizeof(line), fp) != NULL) { - if (isnewline) { - if (line[strlen(line)-1] == '\n') { - strip_line(line); - parse_line(line, linenum); - } else { - fprintf(stderr, "%s: reading %s: line %i too long\n", - progname, FUSE_CONF, linenum); - isnewline = 0; - } - } else if(line[strlen(line)-1] == '\n') - isnewline = 1; - if (isnewline) - linenum ++; - } - fclose(fp); - } else if (errno != ENOENT) { - fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF, - strerror(errno)); - } -} - static int begins_with(const char *s, const char *beg) { if (strncmp(s, beg, strlen(beg)) == 0) @@ -633,8 +581,6 @@ static int mount_fuse(const char *mnt, const char *opts) if (fd == -1) return -1; - read_conf(); - if (getuid() != 0 && mount_max != -1) { if (count_fuse_fs() >= mount_max) { fprintf(stderr, "%s: too many FUSE filesystems mounted; " From 5de5fc1421049e9bd0f666ab87629a7ac6eadf22 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 10 Apr 2008 23:48:22 +0000 Subject: [PATCH 144/328] mount_fuse(): don't refer to non-used /etc/fuse.conf; mount_max is enforced by kernels with unprivileged mount support --- libfuse-lite/fusermount.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 7d3903aa..5fdcad9c 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -583,8 +583,8 @@ static int mount_fuse(const char *mnt, const char *opts) if (getuid() != 0 && mount_max != -1) { if (count_fuse_fs() >= mount_max) { - fprintf(stderr, "%s: too many FUSE filesystems mounted; " - "mount_max=N can be set in /etc/fuse.conf\n", progname); + fprintf(stderr, "%s: too many mounted FUSE filesystems (%d+)\n", + progname, mount_max); goto err; } } From 46b3ebbb16b246b676e753220e8ea22a60d7d102 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 11 Apr 2008 01:02:40 +0000 Subject: [PATCH 145/328] unprivileged mount may failed because suppl. groups were dropped too early --- libfuse-lite/fusermount.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 5fdcad9c..c7964f01 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -54,13 +54,6 @@ static const char *get_user_name(void) int drop_privs(void) { - if (!geteuid()) { - if (setgroups(0, NULL) < 0) { - perror("priv drop: setgroups failed"); - return -1; - } - } - if (!getegid()) { gid_t new_gid = getgid(); @@ -606,8 +599,14 @@ static int mount_fuse(const char *mnt, const char *opts) if (restore_privs()) goto err; - + if (geteuid() == 0) { + + if (setgroups(0, NULL) == -1) { + perror("priv drop: setgroups failed"); + goto err; + } + res = add_mount(source, mnt, type, mnt_opts); if (res == -1) { umount2(mnt, 2); /* lazy umount */ From 68b83a4181d3cbc87b015def11ca7bfacf424865 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 12 Apr 2008 14:05:43 +0000 Subject: [PATCH 146/328] fix: FUSE limited the total file system depth to 4095 characters and gave "No such file or directory" errors (Miklos Szeredi, Szabolcs Szakacsits) --- libfuse-lite/fuse.c | 55 ++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 1eeed443..9e850743 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -28,7 +28,6 @@ #include #include -#define FUSE_MAX_PATH 4096 #define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1 #define FUSE_UNKNOWN_INO 0xffffffff @@ -302,14 +301,28 @@ static struct node *find_node(struct fuse *f, fuse_ino_t parent, return node; } -static char *add_name(char *buf, char *s, const char *name) +static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name) { size_t len = strlen(name); - s -= len; - if (s <= buf) { - fprintf(stderr, "fuse: path too long: ...%s\n", s + len); - return NULL; + + if (s - len <= *buf) { + unsigned pathlen = *bufsize - (s - *buf); + unsigned newbufsize = *bufsize; + char *newbuf; + + while (newbufsize < pathlen + len + 1) + newbufsize *= 2; + + newbuf = realloc(*buf, newbufsize); + if (newbuf == NULL) + return NULL; + + *buf = newbuf; + s = newbuf + newbufsize - pathlen; + memmove(s, newbuf + *bufsize - pathlen, pathlen); + *bufsize = newbufsize; } + s -= len; strncpy(s, name, len); s--; *s = '/'; @@ -319,16 +332,22 @@ static char *add_name(char *buf, char *s, const char *name) static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name) { - char buf[FUSE_MAX_PATH]; - char *s = buf + FUSE_MAX_PATH - 1; + unsigned bufsize = 256; + char *buf; + char *s; struct node *node; + buf = malloc(bufsize); + if (buf == NULL) + return NULL; + + s = buf + bufsize - 1; *s = '\0'; if (name != NULL) { - s = add_name(buf, s, name); + s = add_name(&buf, &bufsize, s, name); if (s == NULL) - return NULL; + goto out_free; } pthread_mutex_lock(&f->lock); @@ -339,18 +358,24 @@ static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name) break; } - s = add_name(buf, s, node->name); + s = add_name(&buf, &bufsize, s, node->name); if (s == NULL) break; } pthread_mutex_unlock(&f->lock); if (node == NULL || s == NULL) - return NULL; - else if (*s == '\0') - return strdup("/"); + goto out_free; + + if (s[0]) + memmove(buf, s, bufsize - (s - buf)); else - return strdup(s); + strcpy(buf, "/"); + return buf; + +out_free: + free(buf); + return NULL; } static char *get_path(struct fuse *f, fuse_ino_t nodeid) From 04e82fd5496bd951acbd00ba69359a3e20cec258 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 12 Apr 2008 14:14:57 +0000 Subject: [PATCH 147/328] release 1.2412 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index be8014d3..304d7402 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2310],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=24 +AC_INIT([ntfs-3g],[1.2412],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=26 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From a6ec7d343b08328caf5681d7d0d0fc3ef3e253ef Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 5 May 2008 15:36:58 +0000 Subject: [PATCH 148/328] fix: multi-block allocation could allocate or free used blocks in certain rare circumstances. Because FUSE write(2) size was restricted to 4 KB until recently and the most widely used NTFS block size is also 4 KB thus allocations were typically single block size therefore the bug wasn't visible and couldn't be easily reproduced. However MFT allocations are multi-block size and they could lead to data corruption typically in close to full disk utilization. --- libntfs-3g/lcnalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index 81d80cd5..88fb4730 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -278,7 +278,7 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, bmp_pos &= ~7; writeback = 0; - while (1) { + while (lcn < buf_size) { byte = buf + (lcn >> 3); bit = 1 << (lcn & 7); if (has_guess) { From 68e382fc9d00cfa10b55fbb1b7d9baa45de36199 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 5 May 2008 15:53:40 +0000 Subject: [PATCH 149/328] ntfs_boot_sector_parse: hints on error (Ralph Corderoy, Szabolcs Szakacsits) --- libntfs-3g/bootsect.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index f13a11cd..f3ffb5d1 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -151,6 +151,13 @@ not_ntfs: return ret; } +static const char *last_sector_error = +"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n" +" or it was not setup correctly (e.g. by not using mdadm --build ...),\n" +" or a wrong device is tried to be mounted,\n" +" or the partition table is corrupt (partition is smaller than NTFS),\n" +" or the NTFS boot sector is corrupt (NTFS size is not valid).\n"; + /** * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector * @vol: ntfs_volume to setup @@ -198,9 +205,7 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) SEEK_SET) == -1) { ntfs_log_perror("Failed to read last sector (%lld)", (long long)sectors); - ntfs_log_error("Perhaps the volume is a RAID/LDM but it wasn't " - "setup yet, or the\nwrong device was used, " - "or the partition table is incorrect.\n" ); + ntfs_log_error("%s", last_sector_error); return -1; } From 83e9aa0457ca2a7cc563def3eb8164fb18d8c273 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 5 May 2008 17:20:22 +0000 Subject: [PATCH 150/328] document missing and powered down device case --- src/ntfs-3g.probe.8.in | 2 +- src/utils.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ntfs-3g.probe.8.in b/src/ntfs-3g.probe.8.in index 9ba4da6d..231e804b 100644 --- a/src/ntfs-3g.probe.8.in +++ b/src/ntfs-3g.probe.8.in @@ -55,7 +55,7 @@ journal file. The volume is already exclusively opened and in use by a kernel driver or software. .IP 17 -Unsetup SoftRAID/FakeRAID hardware. +SoftRAID/FakeRAID hardware without setup, or missing, or powered down device. .IP 18 Unknown reason. .IP 19 diff --git a/src/utils.c b/src/utils.c index cb01ac53..f59683d0 100644 --- a/src/utils.c +++ b/src/utils.c @@ -85,9 +85,10 @@ static const char *opened_volume_msg = "could be identified for example by the help of the 'fuser' command.\n"; static const char *fakeraid_msg = -"You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n" -"different device under /dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1)\n" -"to mount NTFS. Please see the 'dmraid' documentation for help.\n"; +"Either the device is missing or it's powered down, or you have\n" +"SoftRAID hardware and must use an activated, different device under\n" +"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" +"Please see the 'dmraid' documentation for help.\n"; static const char *access_denied_msg = "Please check '%s' and the ntfs-3g binary permissions,\n" From fce56ad8075d4dc9708f38a186f4e85ba03b67d7 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 5 May 2008 17:22:18 +0000 Subject: [PATCH 151/328] release 1.2506 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 304d7402..209e4ecc 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2412],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=26 +AC_INIT([ntfs-3g],[1.2506],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION=28 AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From d73e646b33652c28913ddc7cc8c45a9cacbab2b7 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 19 May 2008 17:34:39 +0000 Subject: [PATCH 152/328] fix: support build in a separate directory (Alon Bar-Lev) --- Makefile.am | 28 ++++++++++++++-------------- include/Makefile.am | 2 +- include/fuse-lite/Makefile.am | 2 +- include/ntfs-3g/Makefile.am | 2 +- libfuse-lite/Makefile.am | 2 +- libntfs-3g/Makefile.am | 4 ++-- src/Makefile.am | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3cfc6525..1208f18c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,20 +4,20 @@ AUTOMAKE_OPTIONS = gnu EXTRA_DIST = AUTHORS CREDITS COPYING NEWS autogen.sh MAINTAINERCLEANFILES=\ - configure \ - Makefile.in \ - aclocal.m4 \ - compile \ - depcomp \ - install-sh \ - ltmain.sh \ - missing \ - config.guess \ - config.sub \ - config.h.in \ - config.h.in~ \ - INSTALL + $(srcdir)/configure \ + $(srcdir)/Makefile.in \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/compile \ + $(srcdir)/depcomp \ + $(srcdir)/install-sh \ + $(srcdir)/ltmain.sh \ + $(srcdir)/missing \ + $(srcdir)/config.guess \ + $(srcdir)/config.sub \ + $(srcdir)/config.h.in \ + $(srcdir)/config.h.in~ \ + $(srcdir)/INSTALL -SUBDIRS= include libfuse-lite libntfs-3g src +SUBDIRS = include libfuse-lite libntfs-3g src doc_DATA = README diff --git a/include/Makefile.am b/include/Makefile.am index 53bbe609..0fa17025 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,4 +1,4 @@ -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in SUBDIRS = ntfs-3g fuse-lite diff --git a/include/fuse-lite/Makefile.am b/include/fuse-lite/Makefile.am index 15a84bbf..7c18dea4 100644 --- a/include/fuse-lite/Makefile.am +++ b/include/fuse-lite/Makefile.am @@ -1,5 +1,5 @@ -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in noinst_HEADERS = \ fuse.h \ diff --git a/include/ntfs-3g/Makefile.am b/include/ntfs-3g/Makefile.am index 870ec2a5..89f46bf5 100644 --- a/include/ntfs-3g/Makefile.am +++ b/include/ntfs-3g/Makefile.am @@ -1,5 +1,5 @@ -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in if INSTALL_LIBRARY ntfs3ginclude_HEADERS = \ diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am index 9e73d4e8..baac0bb3 100644 --- a/libfuse-lite/Makefile.am +++ b/libfuse-lite/Makefile.am @@ -1,5 +1,5 @@ -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in if FUSE_INTERNAL noinst_LTLIBRARIES = libfuse-lite.la diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index f2df582d..6ac7eea6 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -1,5 +1,5 @@ -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in if INSTALL_LIBRARY rootlib_LTLIBRARIES=#Create directory @@ -14,7 +14,7 @@ libntfs_3g_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined if FUSE_INTERNAL -libntfs_3g_la_LIBADD = $(top_srcdir)/libfuse-lite/libfuse-lite.la +libntfs_3g_la_LIBADD = $(top_builddir)/libfuse-lite/libfuse-lite.la endif libntfs_3g_la_SOURCES = \ diff --git a/src/Makefile.am b/src/Makefile.am index c7da069c..ee4cd81d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in if FUSE_INTERNAL FUSE_CFLAGS = -I$(top_srcdir)/include/fuse-lite From 5a434924c23dfa7803cf05d91e89ec64ef8a3894 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 20 May 2008 19:31:49 +0000 Subject: [PATCH 153/328] fix packaging: always distribute headers (Alon Bar-Lev) --- include/ntfs-3g/Makefile.am | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/ntfs-3g/Makefile.am b/include/ntfs-3g/Makefile.am index 89f46bf5..a6550186 100644 --- a/include/ntfs-3g/Makefile.am +++ b/include/ntfs-3g/Makefile.am @@ -1,8 +1,7 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in -if INSTALL_LIBRARY -ntfs3ginclude_HEADERS = \ +headers = \ attrib.h \ attrlist.h \ bitmap.h \ @@ -32,5 +31,10 @@ ntfs3ginclude_HEADERS = \ unistr.h \ version.h \ volume.h + +if INSTALL_LIBRARY +ntfs3ginclude_HEADERS = $(headers) +else +noinst_HEADERS = $(headers) endif From 5a55fa18505ae9ea62225725418ad2c2912c677b Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 23 May 2008 13:25:28 +0000 Subject: [PATCH 154/328] fix: mount failed with invalid argument error if the mountpoint was a symlink (Peter Rabbitson, Miklos Szeredi) --- libfuse-lite/fusermount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index c7964f01..09a560ee 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -439,7 +439,7 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, const char *mnt = *mntp; const char *origmnt = mnt; - res = lstat(mnt, stbuf); + res = stat(mnt, stbuf); if (res == -1) { fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", progname, mnt, strerror(errno)); From 78915c1d3ccb380ed7c14231b860baf9a03de480 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 23 May 2008 20:13:21 +0000 Subject: [PATCH 155/328] fix build if --exec-prefix used without --sbindir=/sbin (Georg Lukas, Szabolcs Szakacsits) --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index ee4cd81d..422aaedd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,7 +38,7 @@ install-exec-hook: endif install-exec-local: - $(INSTALL) -d "$(DESTDIR)$(rootsbindir)" + $(INSTALL) -d "$(DESTDIR)/sbin" $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)/sbin/mount.ntfs-3g" install-data-local: From 9d6a1dc6176aae4e2a6778180877d7958a85fd44 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 23 May 2008 20:33:27 +0000 Subject: [PATCH 156/328] fix crash when both an mft attribute offset and the allocated bytes were corrupt --- include/ntfs-3g/mft.h | 3 +++ libntfs-3g/mft.c | 55 ++++++++++++++++++++++++++++++------------- libntfs-3g/volume.c | 18 ++++---------- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h index b6bf1875..9b17b26b 100644 --- a/include/ntfs-3g/mft.h +++ b/include/ntfs-3g/mft.h @@ -52,6 +52,9 @@ static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, return ntfs_mft_records_read(vol, mref, 1, b); } +extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m); + extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD **mrec, ATTR_RECORD **attr); diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index d4d280f8..7c633a98 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -209,6 +209,40 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, return -1; } +int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + int ret = -1; + + if (!ntfs_is_file_record(m->magic)) { + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), *(le32 *)m); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt allocation size " + "(%u <> %u)\n", (unsigned long long)MREF(mref), + vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); + if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + + ret = 0; +err_out: + if (ret) + errno = EIO; + return ret; +} + /** * ntfs_file_record_read - read a FILE record from the mft from disk * @vol: volume to read from @@ -244,14 +278,13 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD **mrec, ATTR_RECORD **attr) { MFT_RECORD *m; - ATTR_RECORD *a; - int err; if (!vol || !mrec) { errno = EINVAL; ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); return -1; } + m = *mrec; if (!m) { m = ntfs_malloc(vol->mft_record_size); @@ -259,36 +292,26 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, return -1; } if (ntfs_mft_record_read(vol, mref, m)) { - err = errno; ntfs_log_perror("ntfs_mft_record_read failed"); goto err_out; } - err = EIO; - if (!ntfs_is_file_record(m->magic)) { - ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", - (unsigned long long)MREF(mref), *(le32 *)m); + if (ntfs_mft_record_check(vol, mref, m)) goto err_out; - } + if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", (unsigned long long)MREF(mref), MSEQNO(mref), le16_to_cpu(m->sequence_number)); - goto err_out; - } - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size) { - ntfs_log_error("Record %llu is corrupt\n", - (unsigned long long)MREF(mref)); + errno = EIO; goto err_out; } *mrec = m; if (attr) - *attr = a; + *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); return 0; err_out: if (m != *mrec) free(m); - errno = err; return -1; } diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index c25e7442..fc680a6e 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -209,24 +209,14 @@ static int ntfs_mft_load(ntfs_volume *vol) ntfs_log_perror("Error reading $MFT"); goto error_exit; } - if (ntfs_is_baad_record(mb->magic)) { - ntfs_log_error("Incomplete multi sector transfer detected in " - "$MFT.\n"); - goto io_error_exit; - } - if (!ntfs_is_mft_record(mb->magic)) { - ntfs_log_error("$MFT has invalid magic.\n"); - goto io_error_exit; - } + + if (ntfs_mft_record_check(vol, 0, mb)) + goto error_exit; + ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); if (!ctx) goto error_exit; - if (p2n(ctx->attr) < p2n(mb) || - (char*)ctx->attr > (char*)mb + vol->mft_record_size) { - ntfs_log_error("$MFT is corrupt.\n"); - goto io_error_exit; - } /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { From 7705fc3fdf7cb900e71bc555c22d23905f3f02a7 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 23 May 2008 22:21:16 +0000 Subject: [PATCH 157/328] fix hanging mount if $Bitmap data size is corrupt --- libntfs-3g/volume.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index fc680a6e..c2cb4364 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -844,13 +844,19 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) ntfs_log_perror("Failed to open inode FILE_Bitmap"); goto error_exit; } - /* Get an ntfs attribute for $Bitmap/$DATA. */ + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); if (!vol->lcnbmp_na) { ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } - /* Done with the $Bitmap mft record. */ + + if (vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size) { + ntfs_log_error("Corrupt cluster map size (%lld > %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size); + goto io_error_exit; + } /* Now load the upcase table from $UpCase. */ ntfs_log_debug("Loading $UpCase...\n"); From 6a024394c6808f1d79d30f2283a29366727f1388 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 24 May 2008 14:38:36 +0000 Subject: [PATCH 158/328] fix mount/driver hangs/misbehaviours when the some attribute flags were corrupt --- libntfs-3g/attrib.c | 66 ++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 4e628020..3bc4b5ff 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -376,10 +376,9 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfs_attr_search_ctx *ctx; ntfs_attr *na; ATTR_RECORD *a; - int err; BOOL cs; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + ntfs_log_trace("Entering for inode %lld, attr 0x%x.\n", (unsigned long long)ni->mft_no, type); if (!ni || !ni->vol || !ni->mrec) { errno = EINVAL; @@ -397,16 +396,29 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, } ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; + if (!ctx) goto err_out; - } - if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) { - err = errno; + + if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) goto put_err_out; - } a = ctx->attr; + + if (!name) { + if (a->name_length) { + name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length); + if (!name) + goto put_err_out; + name_len = a->name_length; + } else { + name = AT_UNNAMED; + name_len = 0; + } + } + + __ntfs_attr_init(na, ni, type, name, name_len); + /* * Wipe the flags in case they are not zero for an attribute list * attribute. Windows does not complain about invalid flags and chkdsk @@ -414,22 +426,27 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, */ if (type == AT_ATTRIBUTE_LIST) a->flags = 0; + cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - if (!name) { - if (a->name_length) { - name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( - a->name_offset)), a->name_length); - if (!name) { - err = errno; - goto put_err_out; - } - name_len = a->name_length; - } else { - name = AT_UNNAMED; - name_len = 0; - } + + if ((!(a->flags & ATTR_IS_COMPRESSED) != !NAttrCompressed(na)) || + (!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na))) { + errno = EIO; + ntfs_log_perror("Inode %lld attr 0x%x has corrupt flags " + "(0x%x <> 0x%x)", (unsigned long long)ni->mft_no, + type, a->flags, na->ni->flags); + goto put_err_out; } - __ntfs_attr_init(na, ni, type, name, name_len); + + if ((NAttrCompressed(na) || NAttrSparse(na)) && !a->compression_unit) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%x has no " + "compression unit", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } + if (a->non_resident) { ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED, a->flags & ATTR_IS_ENCRYPTED, @@ -452,7 +469,6 @@ put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: free(na); - errno = err; return NULL; } @@ -791,8 +807,8 @@ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) } ntfs_log_trace("Entering for inode %lld attr 0x%x pos %lld count " - "%lld\n", (unsigned long long)na->ni->mft_no, - na->type, (long long)pos, (long long)count); + "%lld flags: 0x%x\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count, na->ni->flags); /* * If this is a compressed attribute it needs special treatment, but From 5e1684ee29c881a2579de5e0bd8fbd4d1bd11975 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 24 May 2008 15:49:56 +0000 Subject: [PATCH 159/328] autoconf cleanups: move conditional and conditions to the end of the file, so that the order of the AC_ARG_* will not matter. The LDCONFIG is now available at configure argument. Some minor style changes. (Alon Bar-Lev) --- configure.ac | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index 209e4ecc..f3a12182 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # Autoconf AC_PREREQ([2.59]) AC_INIT([ntfs-3g],[1.2506],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION=28 +LIBNTFS_3G_VERSION="28" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment @@ -64,7 +64,6 @@ AC_ARG_ENABLE( , [enable_really_static="no"] ) -AM_CONDITIONAL([REALLYSTATIC], [test "${enable_really_static}" = "yes"]) AC_ARG_ENABLE( [library], @@ -72,8 +71,6 @@ AC_ARG_ENABLE( , [enable_library="yes"] ) -test "${enable_really_static}" = "yes" && enable_library="no" -AM_CONDITIONAL(INSTALL_LIBRARY, test "$enable_library" = yes) AC_ARG_ENABLE( [ldconfig], @@ -81,9 +78,6 @@ AC_ARG_ENABLE( , [enable_ldconfig="yes"] ) -test "${enable_library}" = "no" && enable_ldconfig="no" -# --disable-library will disable to run ldconfig since no point to do so. -AM_CONDITIONAL(RUN_LDCONFIG, test "$enable_ldconfig" = yes) AC_ARG_ENABLE( [ldscript], @@ -106,20 +100,19 @@ AC_ARG_ENABLE( [enable_device_default_io_ops="yes"] ) -# Programs # pthread_rwlock_t requires _GNU_SOURCE AC_GNU_SOURCE + +# Programs AC_PROG_CC AC_PROG_LIBTOOL AC_PROG_LN_S AM_PROG_CC_C_O -# No need to check for ldconfig if --disable-ldconfig was given -if test "$enable_ldconfig" = yes; then - AC_PATH_PROG(LDCONFIG, ldconfig, true, [/sbin /usr/sbin $PATH]) -fi AC_PATH_PROG([MV], [mv]) AC_PATH_PROG([RM], [rm]) AC_PATH_PROG([SED], [sed]) +AC_ARG_VAR([LDCONFIG], [ldconfig utility]) +AC_PATH_PROG([LDCONFIG], [ldconfig], [true], [/sbin /usr/sbin $PATH]) # Environment AC_MSG_CHECKING([Windows OS]) @@ -291,9 +284,10 @@ test "${enable_device_default_io_ops}" = "no" && AC_DEFINE( [Don't use default IO ops] ) -if test "${enable_mtab}" = "no"; then - AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab]) -fi +test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab]) + +test "${enable_really_static}" = "yes" && enable_library="no" +test "${enable_library}" = "no" && enable_ldconfig="no" # Settings pkgconfigdir="\$(libdir)/pkgconfig" @@ -323,6 +317,9 @@ AM_CONDITIONAL([FUSE_INTERNAL], [test "${with_fuse}" = "internal"]) AM_CONDITIONAL([GENERATE_LDSCRIPT], [test "${enable_ldscript}" = "yes"]) AM_CONDITIONAL([WINDOWS], [test "${WINDOWS}" = "yes"]) AM_CONDITIONAL([NTFS_DEVICE_DEFAULT_IO_OPS], [test "${enable_device_default_io_ops}" = "yes"]) +AM_CONDITIONAL([RUN_LDCONFIG], [test "${enable_ldconfig}" = "yes"]) +AM_CONDITIONAL([REALLYSTATIC], [test "${enable_really_static}" = "yes"]) +AM_CONDITIONAL([INSTALL_LIBRARY], [test "${enable_library}" = "yes"]) # workaround for Date: Sat, 24 May 2008 17:37:46 +0000 Subject: [PATCH 160/328] fix parallel 'make install' (Alon Bar-Lev) --- src/Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 422aaedd..660e6f1f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -41,8 +41,7 @@ install-exec-local: $(INSTALL) -d "$(DESTDIR)/sbin" $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)/sbin/mount.ntfs-3g" -install-data-local: - $(INSTALL) -d "$(DESTDIR)$(man8dir)" +install-data-local: install-man8 $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" uninstall-local: From 70ecc5be0c8b73d098057497e639493f54106461 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 25 May 2008 01:21:29 +0000 Subject: [PATCH 161/328] new: --enable-mount-helper, --with-rootfs options and other build fixes: - add --enable-mount-helper which installs /sbin/mount.ntfs-3g to be able to mount via mount(8) and /etc/fstab on Linux. The default is enabled on Linux and disabled on others. - add --with-rootfs= where the driver, library and core utils will be installed. The default is "/" otherwise automounts could fail. - warn fuse none rootfs only if rootfs is "/" - parallel make install support - add DISTCHECK_HACK=1 environment which make "make distcheck" work (Alon Bar-Lev) --- configure.ac | 50 +++++++++++++++++++++++++++++++++---------------- src/Makefile.am | 10 +++++++--- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index f3a12182..1330331c 100644 --- a/configure.ac +++ b/configure.ac @@ -72,6 +72,18 @@ AC_ARG_ENABLE( [enable_library="yes"] ) +AC_ARG_ENABLE( + [mount-helper], + [AC_HELP_STRING([--enable-mount-helper], [install mount helper @<:@default=enabled for linux@:>@])], + , + [ + case "${target_os}" in + linux*) enable_mount_helper="yes" ;; + *) enable_mount_helper="no" ;; + esac + ] +) + AC_ARG_ENABLE( [ldconfig], [AC_HELP_STRING([--disable-ldconfig], [do not update dynamic linker cache using ldconfig])], @@ -95,11 +107,21 @@ AC_ARG_ENABLE( AC_ARG_ENABLE( [device-default-io-ops], - [AC_HELP_STRING([--disable-device-default-io-ops], [Install default IO ops])], + [AC_HELP_STRING([--disable-device-default-io-ops], [install default IO ops])], , [enable_device_default_io_ops="yes"] ) +# Driver executables should be installed to the root filesystem, otherwise +# automounting NTFS volumes can fail during boot if the driver binaries +# and their dependencies are on an unmounted partition. +AC_ARG_WITH( + [rootfs], + [AC_HELP_STRING([--with-rootfs=DIR], [rootfs location @<:@default=/@:>@])], + , + [with_rootfs="/"] +) + # pthread_rwlock_t requires _GNU_SOURCE AC_GNU_SOURCE @@ -289,22 +311,17 @@ test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc test "${enable_really_static}" = "yes" && enable_library="no" test "${enable_library}" = "no" && enable_ldconfig="no" -# Settings +if test "x${DISTCHECK_HACK}" != "x"; then + with_rootfs=`echo "${prefix}" | sed "s#NONE#${ac_default_prefix}#"` + enable_mount_helper="no" + enable_ldconfig="no" +fi + +rootbindir="${with_rootfs}/bin" +rootsbindir="${with_rootfs}/sbin" +rootlibdir="${with_rootfs}/lib${libdir##*/lib}" pkgconfigdir="\$(libdir)/pkgconfig" ntfs3gincludedir="\$(includedir)/ntfs-3g" -# Executables should be installed to the root filesystem, otherwise -# automounting NTFS volumes can fail during boot if the driver binaries -# and their dependencies are on an unmounted partition. Use --exec-prefix -# to override this. -if test "x${exec_prefix}" = "xNONE"; then - rootbindir="/bin" - rootsbindir="/sbin" - rootlibdir="/lib${libdir##*/lib}" -else - rootbindir="\$(bindir)" - rootsbindir="\$(sbindir)" - rootlibdir="\$(libdir)" -fi AC_SUBST([pkgconfigdir]) AC_SUBST([ntfs3gincludedir]) AC_SUBST([rootbindir]) @@ -320,6 +337,7 @@ AM_CONDITIONAL([NTFS_DEVICE_DEFAULT_IO_OPS], [test "${enable_device_default_io_o AM_CONDITIONAL([RUN_LDCONFIG], [test "${enable_ldconfig}" = "yes"]) AM_CONDITIONAL([REALLYSTATIC], [test "${enable_really_static}" = "yes"]) AM_CONDITIONAL([INSTALL_LIBRARY], [test "${enable_library}" = "yes"]) +AM_CONDITIONAL([ENABLE_MOUNT_HELPER], [test "${enable_mount_helper}" = "yes"]) # workaround for /dev/null; then cat < Date: Mon, 26 May 2008 23:46:43 +0000 Subject: [PATCH 162/328] use $(MKDIR_P) if available, $(mkdir_p) otherwise (Alon Bar-Lev) --- configure.ac | 5 +++++ src/Makefile.am | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1330331c..78455304 100644 --- a/configure.ac +++ b/configure.ac @@ -344,6 +344,11 @@ if test -z "${docdir}"; then docdir="\$(datarootdir)/doc/\$(PACKAGE_NAME)" AC_SUBST([docdir]) fi +# workaround for Date: Tue, 27 May 2008 13:36:55 +0000 Subject: [PATCH 163/328] release 1.2528 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 78455304..b035aa7c 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2506],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="28" +AC_INIT([ntfs-3g],[1.2528],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="30" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From d2f30b4d43c889c1b88358377375f0b9b61f2d48 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 28 May 2008 12:04:38 +0000 Subject: [PATCH 164/328] fix corrupt attribute flags and compression unit detection --- libntfs-3g/attrib.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 3bc4b5ff..68db3945 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -429,25 +429,25 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - if ((!(a->flags & ATTR_IS_COMPRESSED) != !NAttrCompressed(na)) || - (!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || - (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na))) { + if (na->type == AT_DATA && na->name == AT_UNNAMED && + ((!(a->flags & ATTR_IS_COMPRESSED) != !NAttrCompressed(na)) || + (!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { errno = EIO; - ntfs_log_perror("Inode %lld attr 0x%x has corrupt flags " - "(0x%x <> 0x%x)", (unsigned long long)ni->mft_no, - type, a->flags, na->ni->flags); + ntfs_log_perror("Inode %lld has corrupt attribute flags " + "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, + a->flags, na->ni->flags); goto put_err_out; } - if ((NAttrCompressed(na) || NAttrSparse(na)) && !a->compression_unit) { - errno = EIO; - ntfs_log_perror("Compressed inode %lld attr 0x%x has no " - "compression unit", - (unsigned long long)ni->mft_no, type); - goto put_err_out; - } - if (a->non_resident) { + if ((a->flags & ATTR_IS_COMPRESSED) && !a->compression_unit) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%x has " + "no compression unit", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED, a->flags & ATTR_IS_ENCRYPTED, a->flags & ATTR_IS_SPARSE, From 9d3cc4b907dfd3fab6ccee9164f39f754fb6a08a Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 28 May 2008 12:39:20 +0000 Subject: [PATCH 165/328] revert temporarily the --with-rootfs= configure option until we really need it and agree on the name, semantic, backward-compatibility (Bernhard Kaindl, Alon Bar-Lev, Szabolcs Szakacsits) --- configure.ac | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index b035aa7c..b0da22e9 100644 --- a/configure.ac +++ b/configure.ac @@ -112,16 +112,6 @@ AC_ARG_ENABLE( [enable_device_default_io_ops="yes"] ) -# Driver executables should be installed to the root filesystem, otherwise -# automounting NTFS volumes can fail during boot if the driver binaries -# and their dependencies are on an unmounted partition. -AC_ARG_WITH( - [rootfs], - [AC_HELP_STRING([--with-rootfs=DIR], [rootfs location @<:@default=/@:>@])], - , - [with_rootfs="/"] -) - # pthread_rwlock_t requires _GNU_SOURCE AC_GNU_SOURCE @@ -312,16 +302,26 @@ test "${enable_really_static}" = "yes" && enable_library="no" test "${enable_library}" = "no" && enable_ldconfig="no" if test "x${DISTCHECK_HACK}" != "x"; then - with_rootfs=`echo "${prefix}" | sed "s#NONE#${ac_default_prefix}#"` enable_mount_helper="no" enable_ldconfig="no" fi -rootbindir="${with_rootfs}/bin" -rootsbindir="${with_rootfs}/sbin" -rootlibdir="${with_rootfs}/lib${libdir##*/lib}" +# Settings pkgconfigdir="\$(libdir)/pkgconfig" ntfs3gincludedir="\$(includedir)/ntfs-3g" +# Executables should be installed to the root filesystem, otherwise +# automounting NTFS volumes can fail during boot if the driver binaries +# and their dependencies are on an unmounted partition. Use --exec-prefix +# to override this. +if test "x${exec_prefix}" = "xNONE"; then + rootbindir="/bin" + rootsbindir="/sbin" + rootlibdir="/lib${libdir##*/lib}" +else + rootbindir="\$(bindir)" + rootsbindir="\$(sbindir)" + rootlibdir="\$(libdir)" +fi AC_SUBST([pkgconfigdir]) AC_SUBST([ntfs3gincludedir]) AC_SUBST([rootbindir]) @@ -366,7 +366,7 @@ AC_CONFIG_FILES([ ]) AC_OUTPUT -if test "${with_fuse}" = "external" -a "${with_rootfs}" = "/"; then +if test "${with_fuse}" = "external"; then if ! echo "x$FUSE_LIB_PATH" | grep -- "x-L/lib" > /dev/null; then cat < Date: Wed, 28 May 2008 18:49:44 +0000 Subject: [PATCH 166/328] fix potential hangs if index entries were corrupt --- libntfs-3g/index.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 0057fb3f..fc8e4ac2 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -220,7 +220,7 @@ static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) static int ntfs_ie_end(INDEX_ENTRY *ie) { - return ie->ie_flags & INDEX_ENTRY_END; + return ie->ie_flags & INDEX_ENTRY_END || !ie->length; } /** @@ -301,11 +301,13 @@ static int ntfs_ih_numof_entries(INDEX_HEADER *ih) { int n; INDEX_ENTRY *ie; + u8 *end; ntfs_log_trace("Entering\n"); + end = ntfs_ie_get_end(ih); ie = ntfs_ie_get_first(ih); - for (n = 0; !ntfs_ie_end(ie); n++) + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) ie = ntfs_ie_get_next(ie); return n; } From 4173b0172ce151db9703f2b1733ddb234bff2e62 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 28 May 2008 19:04:59 +0000 Subject: [PATCH 167/328] release 1.2531 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b0da22e9..78cdefb5 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2528],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="30" +AC_INIT([ntfs-3g],[1.2531],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="31" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From b8228df2a2f4ebe83abacbcc8a10146715506de4 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Jun 2008 19:48:35 +0000 Subject: [PATCH 168/328] Fix 'PATH_MAX undeclared' (cross-)compilation error in some environments --- libfuse-lite/helper.c | 1 + libfuse-lite/mount_util.c | 1 + 2 files changed, 2 insertions(+) diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c index 3029e173..e2860433 100644 --- a/libfuse-lite/helper.c +++ b/libfuse-lite/helper.c @@ -18,6 +18,7 @@ #include #include #include +#include enum { KEY_HELP, diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c index ddd81f7b..e8154d21 100644 --- a/libfuse-lite/mount_util.c +++ b/libfuse-lite/mount_util.c @@ -19,6 +19,7 @@ #include #include #include +#include static int mtab_needs_update(const char *mnt) { From bf3b6d780d2abf2ed09e64bb3e11481e88708115 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 11 Jun 2008 20:33:02 +0000 Subject: [PATCH 169/328] fix potential hang for huge/corrupted directories (Miklos Szeredi, Szabolcs Szakacsits) --- libfuse-lite/fuse.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 9e850743..808cd315 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -310,8 +310,12 @@ static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name) unsigned newbufsize = *bufsize; char *newbuf; - while (newbufsize < pathlen + len + 1) - newbufsize *= 2; + while (newbufsize < pathlen + len + 1) { + if (newbufsize >= 0x80000000) + newbufsize = 0xffffffff; + else + newbufsize *= 2; + } newbuf = realloc(*buf, newbufsize); if (newbuf == NULL) @@ -1852,8 +1856,12 @@ static int extend_contents(struct fuse_dh *dh, unsigned minsize) unsigned newsize = dh->size; if (!newsize) newsize = 1024; - while (newsize < minsize) - newsize *= 2; + while (newsize < minsize) { + if (newsize >= 0x80000000) + newsize = 0xffffffff; + else + newsize *= 2; + } newptr = (char *) realloc(dh->contents, newsize); if (!newptr) { From 2848f06bad1ad576309c7a7f16e7892ee1913c99 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 17 Jun 2008 22:40:43 +0000 Subject: [PATCH 170/328] fix memory leak when removing streams or xattrs (Jean-Pierre Andre) --- libntfs-3g/attrib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 68db3945..d298d864 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3286,6 +3286,7 @@ int ntfs_attr_rm(ntfs_attr *na) } ntfs_attr_reinit_search_ctx(ctx); } + ntfs_attr_put_search_ctx(ctx); if (errno != ENOENT) { ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " "metadata.\n"); From f94a09b1519443deffd0356bbb2a4f26a8ec66e8 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 27 Jun 2008 12:37:02 +0000 Subject: [PATCH 171/328] fix: mount failed if /etc/mtab didn't exist or was on a read-only file system (Miklos Szeredi, Szabolcs Szakacsits) --- libfuse-lite/mount_util.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c index e8154d21..5e162bf6 100644 --- a/libfuse-lite/mount_util.c +++ b/libfuse-lite/mount_util.c @@ -23,17 +23,35 @@ static int mtab_needs_update(const char *mnt) { - struct stat stbuf; + int res; + struct stat stbuf; - /* If mtab is within new mount, don't touch it */ - if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && - _PATH_MOUNTED[strlen(mnt)] == '/') - return 0; + /* If mtab is within new mount, don't touch it */ + if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && + _PATH_MOUNTED[strlen(mnt)] == '/') + return 0; - if (lstat(_PATH_MOUNTED, &stbuf) != -1 && S_ISLNK(stbuf.st_mode)) - return 0; + /* + * Skip mtab update if /etc/mtab: + * + * - doesn't exist, + * - is a symlink, + * - is on a read-only filesystem. + */ + res = lstat(_PATH_MOUNTED, &stbuf); + if (res == -1) { + if (errno == ENOENT) + return 0; + } else { + if (S_ISLNK(stbuf.st_mode)) + return 0; - return 1; + res = access(_PATH_MOUNTED, W_OK); + if (res == -1 && errno == EROFS) + return 0; + } + + return 1; } int fuse_mnt_add_mount(const char *progname, const char *fsname, From 81488fad6e2b230db445f7c3d5153810445e0e6c Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 27 Jun 2008 18:59:33 +0000 Subject: [PATCH 172/328] add Bernhard Kaindl to project authors --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 242aea05..a56a1ef4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,6 +5,7 @@ Jean-Pierre Andre Alon Bar-Lev Dominique L Bouix Csaba Henk +Bernhard Kaindl Erik Larsson Alejandro Pulver Szabolcs Szakacsits From 7bcb40b37889c2e5c16cb54843125f69163b4eb8 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 29 Jun 2008 16:52:27 +0000 Subject: [PATCH 173/328] cleanup ntfs_attr_open() --- libntfs-3g/attrib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index d298d864..2e13a0f8 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -389,10 +389,8 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, return NULL; if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { name = ntfs_ucsndup(name, name_len); - if (!name) { - free(na); - return NULL; - } + if (!name) + goto err_out; } ctx = ntfs_attr_get_search_ctx(ni, NULL); From dec6162b636180bbed0f98d6aa4f3404bae41e5b Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 29 Jun 2008 17:43:56 +0000 Subject: [PATCH 174/328] cleanup ntfs_inode_open() --- libntfs-3g/inode.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 5bb2c4e1..9a8d61f1 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -154,7 +154,6 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) s64 l; ntfs_inode *ni; ntfs_attr_search_ctx *ctx; - int err = 0; STANDARD_INFORMATION *std_info; ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); @@ -168,7 +167,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) goto err_out; if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { - err = ENOENT; + errno = ENOENT; goto err_out; } ni->mft_no = MREF(mref); @@ -178,7 +177,6 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) /* Receive some basic information about inode. */ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - err = errno; ntfs_log_trace("Failed to receive STANDARD_INFORMATION " "attribute.\n"); goto put_err_out; @@ -203,7 +201,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (!l) goto put_err_out; if (l > 0x40000) { - err = EIO; + errno = EIO; goto put_err_out; } ni->attr_list_size = l; @@ -214,7 +212,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (!l) goto put_err_out; if (l != ni->attr_list_size) { - err = EIO; + errno = EIO; goto put_err_out; } get_size: @@ -241,14 +239,9 @@ get_size: ntfs_attr_put_search_ctx(ctx); return ni; put_err_out: - if (!err) - err = errno; ntfs_attr_put_search_ctx(ctx); err_out: - if (!err) - err = errno; __ntfs_inode_release(ni); - errno = err; return NULL; } From c6e1991749eb1e11547af540c7aeed01314b4b18 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 29 Jun 2008 22:06:38 +0000 Subject: [PATCH 175/328] new: use tabulated logging; add ntfs_log_enter() and ntfs_log_leave() and convert the most commonly used functions --- include/ntfs-3g/logging.h | 6 ++ include/ntfs-3g/mft.h | 14 ++- libntfs-3g/attrib.c | 193 +++++++++++++++++++++++--------------- libntfs-3g/bitmap.c | 17 +++- libntfs-3g/inode.c | 56 +++++++---- libntfs-3g/lcnalloc.c | 53 +++++++---- libntfs-3g/logging.c | 32 ++++++- libntfs-3g/mft.c | 19 +++- libntfs-3g/runlist.c | 78 ++++++++------- 9 files changed, 306 insertions(+), 162 deletions(-) diff --git a/include/ntfs-3g/logging.h b/include/ntfs-3g/logging.h index ae36210a..af25dbf1 100644 --- a/include/ntfs-3g/logging.h +++ b/include/ntfs-3g/logging.h @@ -76,6 +76,8 @@ int ntfs_log_redirect(const char *function, const char *file, int line, #define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ #define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ #define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ +#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */ +#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */ /* Logging style flags - Manage the style of the output */ #define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ @@ -103,9 +105,13 @@ int ntfs_log_redirect(const char *function, const char *file, int line, #ifdef DEBUG #define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) #define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) +#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS) +#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS) #else #define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) #define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0) #endif /* DEBUG */ #endif /* _LOGGING_H_ */ diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h index 9b17b26b..24e71495 100644 --- a/include/ntfs-3g/mft.h +++ b/include/ntfs-3g/mft.h @@ -49,7 +49,12 @@ extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD *b) { - return ntfs_mft_records_read(vol, mref, 1, b); + int ret; + + ntfs_log_enter("Entering for inode %lld\n", MREF(mref)); + ret = ntfs_mft_records_read(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; } extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, @@ -79,7 +84,12 @@ extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD *b) { - return ntfs_mft_records_write(vol, mref, 1, b); + int ret; + + ntfs_log_enter("Entering for inode %lld\n", MREF(mref)); + ret = ntfs_mft_records_write(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; } /** diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 2e13a0f8..a9b7207e 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -374,19 +374,20 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len) { ntfs_attr_search_ctx *ctx; - ntfs_attr *na; + ntfs_attr *na = NULL; ATTR_RECORD *a; BOOL cs; - ntfs_log_trace("Entering for inode %lld, attr 0x%x.\n", - (unsigned long long)ni->mft_no, type); + ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", + (unsigned long long)ni->mft_no, type); + if (!ni || !ni->vol || !ni->mrec) { errno = EINVAL; - return NULL; + goto out; } na = calloc(sizeof(ntfs_attr), 1); if (!na) - return NULL; + goto out; if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { name = ntfs_ucsndup(name, name_len); if (!name) @@ -462,12 +463,16 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, cs ? (l + 7) & ~7 : 0, 0); } ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); return na; + put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: free(na); - return NULL; + na = NULL; + goto out; } /** @@ -772,46 +777,16 @@ map_rl: } /** - * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure - * @na: ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @count: number of bytes to read - * @b: output data buffer - * - * This function will read @count bytes starting at offset @pos from the ntfs - * attribute @na into the data buffer @b. - * - * On success, return the number of successfully read bytes. If this number is - * lower than @count this means that the read reached end of file or that an - * error was encountered during the read so that the read is partial. 0 means - * end of file or nothing was read (also return 0 when @count is 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of ntfs_pread(), or to EINVAL in case of invalid - * arguments. - */ -s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) + * ntfs_attr_pread_i - see description at ntfs_attr_pread() + */ +static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) { s64 br, to_read, ofs, total, total2; ntfs_volume *vol; runlist_element *rl; - if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { - errno = EINVAL; - ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", - __FUNCTION__, na, b, (long long)pos, - (long long)count); - return -1; - } + /* Sanity checking arguments is done in ntfs_attr_pread(). */ - ntfs_log_trace("Entering for inode %lld attr 0x%x pos %lld count " - "%lld flags: 0x%x\n", (unsigned long long)na->ni->mft_no, - na->type, (long long)pos, (long long)count, na->ni->flags); - - /* - * If this is a compressed attribute it needs special treatment, but - * only if it is non-resident. - */ if (NAttrCompressed(na) && NAttrNonResident(na)) return ntfs_compressed_attr_pread(na, pos, count, b); /* @@ -959,6 +934,47 @@ rl_err_out: return -1; } +/** + * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes starting at offset @pos from the ntfs + * attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + int ret; + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", + __FUNCTION__, na, b, (long long)pos, + (long long)count); + return -1; + } + + ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " + "%lld\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count); + + ret = ntfs_attr_pread_i(na, pos, count, b); + + ntfs_log_leave("\n"); + return ret; +} + static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) { char *buf; @@ -1148,9 +1164,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) unsigned int undo_data_size : 1; } need_to = { 0, 0 }; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " "0x%llx.\n", na->ni->mft_no, na->type, (long long)pos, (long long)count); + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { errno = EINVAL; ntfs_log_perror("%s", __FUNCTION__); @@ -1387,9 +1404,11 @@ done: * FIXME: trying to recover by goto rl_err_out; * could cause driver hang by infinite looping. */ - return -1; + total = -1; + goto out; } out: + ntfs_log_leave("\n"); return total; rl_err_out: eo = errno; @@ -1900,7 +1919,7 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, ni = ctx->ntfs_ino; base_ni = ctx->base_ntfs_ino; - ntfs_log_trace("Entering for inode 0x%llx, attribute type 0x%x.\n", + ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", (unsigned long long)ni->mft_no, type); if (!base_ni) { /* First call happens with the base mft record. */ @@ -2299,22 +2318,29 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, { ntfs_volume *vol; ntfs_inode *base_ni; + int ret = -1; + ntfs_log_enter("Entering for attribute type 0x%x\n", type); + if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || !vol->upcase || !vol->upcase_len))) { errno = EINVAL; - return -1; + goto out; } + if (ctx->base_ntfs_ino) base_ni = ctx->base_ntfs_ino; else base_ni = ctx->ntfs_ino; if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) - return ntfs_attr_find(type, name, name_len, ic, val, val_len, - ctx); - return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, - val, val_len, ctx); + ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx); + else + ret = ntfs_external_attr_find(type, name, name_len, ic, + lowest_vcn, val, val_len, ctx); +out: + ntfs_log_leave("\n"); + return ret; } /** @@ -3617,10 +3643,8 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na, /* Start by allocating clusters to hold the attribute value. */ rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> vol->cluster_size_bits, -1, DATA_ZONE); - if (!rl) { - ntfs_log_perror("Failed to allocate clusters"); + if (!rl) return -1; - } } else rl = NULL; /* @@ -4233,31 +4257,9 @@ error: ret = -3; goto out; #define NTFS_VCN_DELETE_MARK -2 /** - * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute - * @na: non-resident ntfs open attribute for which we need update - * @from_vcn: update runlist starting this VCN - * - * Build mapping pairs from @na->rl and write them to the disk. Also, this - * function updates sparse bit, allocated and compressed size (allocates/frees - * space for this field if required). - * - * @na->allocated_size should be set to correct value for the new runlist before - * call to this function. Vice-versa @na->compressed_size will be calculated and - * set to correct value during this function. - * - * FIXME: This function does not update sparse bit and compressed size correctly - * if called with @from_vcn != 0. - * - * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * EINVAL - Invalid arguments passed. - * ENOMEM - Not enough memory to complete operation. - * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST - * or there is no free MFT records left to allocate. + * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs */ -int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) +static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn) { ntfs_attr_search_ctx *ctx; ntfs_inode *ni, *base_ni; @@ -4547,6 +4549,41 @@ put_err_out: } #undef NTFS_VCN_DELETE_MARK +/** + * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute + * @na: non-resident ntfs open attribute for which we need update + * @from_vcn: update runlist starting this VCN + * + * Build mapping pairs from @na->rl and write them to the disk. Also, this + * function updates sparse bit, allocated and compressed size (allocates/frees + * space for this field if required). + * + * @na->allocated_size should be set to correct value for the new runlist before + * call to this function. Vice-versa @na->compressed_size will be calculated and + * set to correct value during this function. + * + * FIXME: This function does not update sparse bit and compressed size correctly + * if called with @from_vcn != 0. + * + * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments passed. + * ENOMEM - Not enough memory to complete operation. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST + * or there is no free MFT records left to allocate. + */ +int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn); + ntfs_log_leave("\n"); + return ret; +} + /** * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute * @na: non-resident ntfs attribute to shrink @@ -4980,12 +5017,12 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, void *data, *ret = NULL; s64 size; - ntfs_log_trace("Entering\n"); + ntfs_log_enter("Entering\n"); na = ntfs_attr_open(ni, type, name, name_len); if (!na) { ntfs_log_perror("ntfs_attr_open failed"); - return NULL; + goto err_exit; } data = ntfs_malloc(na->data_size); if (!data) @@ -5002,6 +5039,8 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, *data_size = size; out: ntfs_attr_close(na); +err_exit: + ntfs_log_leave("\n"); return ret; } diff --git a/libntfs-3g/bitmap.c b/libntfs-3g/bitmap.c index 097386c2..e2b4f01c 100644 --- a/libntfs-3g/bitmap.c +++ b/libntfs-3g/bitmap.c @@ -265,7 +265,13 @@ free_err_out: */ int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) { - return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + int ret; + + ntfs_log_enter("Set from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + ntfs_log_leave("\n"); + return ret; } /** @@ -281,9 +287,12 @@ int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) */ int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) { - ntfs_log_trace("Dealloc from bit 0x%llx, count 0x%llx.\n", + int ret; + + ntfs_log_enter("Clear from bit %lld, count %lld\n", (long long)start_bit, (long long)count); - - return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ntfs_log_leave("\n"); + return ret; } diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 9a8d61f1..718ac3ec 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -152,18 +152,18 @@ static void __ntfs_inode_release(ntfs_inode *ni) ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) { s64 l; - ntfs_inode *ni; + ntfs_inode *ni = NULL; ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); + ntfs_log_enter("Entering for inode %lld\n", MREF(mref)); if (!vol) { errno = EINVAL; - return NULL; + goto out; } ni = __ntfs_inode_allocate(vol); if (!ni) - return NULL; + goto out; if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) goto err_out; if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { @@ -237,12 +237,16 @@ get_size: } } ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); return ni; + put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: __ntfs_inode_release(ni); - return NULL; + ni = NULL; + goto out; } /** @@ -269,17 +273,19 @@ err_out: */ int ntfs_inode_close(ntfs_inode *ni) { + int ret = -1; + if (!ni) return 0; - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long)ni->mft_no); + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); /* If we have dirty metadata, write it out. */ if (NInoDirty(ni) || NInoAttrListDirty(ni)) { if (ntfs_inode_sync(ni)) { if (errno != EIO) errno = EBUSY; - return -1; + goto err; } } /* Is this a base inode with mapped extent inodes? */ @@ -288,7 +294,7 @@ int ntfs_inode_close(ntfs_inode *ni) if (ntfs_inode_close(ni->extent_nis[0])) { if (errno != EIO) errno = EBUSY; - return -1; + goto err; } } } else if (ni->nr_extents == -1) { @@ -342,7 +348,10 @@ int ntfs_inode_close(ntfs_inode *ni) } __ntfs_inode_release(ni); - return 0; + ret = 0; +err: + ntfs_log_leave("\n"); + return ret; } /** @@ -373,7 +382,7 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) { u64 mft_no = MREF_LE(mref); - ntfs_inode *ni; + ntfs_inode *ni = NULL; ntfs_inode **extent_nis; int i; @@ -383,7 +392,7 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) return NULL; } - ntfs_log_trace("Opening extent inode 0x%llx (base mft record 0x%llx).\n", + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); @@ -404,15 +413,15 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) ntfs_log_perror("Found stale extent mft " "reference mft=%lld", (long long)ni->mft_no); - return NULL; + goto out; } - return ni; + goto out; } } /* Wasn't there, we need to load the extent inode. */ ni = __ntfs_inode_allocate(base_ni->vol); if (!ni) - return NULL; + goto out; if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) { ntfs_log_perror("ntfs_file_record_read failed #2"); @@ -436,10 +445,13 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) base_ni->extent_nis = extent_nis; } base_ni->extent_nis[base_ni->nr_extents++] = ni; +out: + ntfs_log_leave("\n"); return ni; err_out: __ntfs_inode_release(ni); - return NULL; + ni = NULL; + goto out; } /** @@ -656,6 +668,7 @@ err_out: */ int ntfs_inode_sync(ntfs_inode *ni) { + int ret = 0; int err = 0; if (!ni) { @@ -664,7 +677,7 @@ int ntfs_inode_sync(ntfs_inode *ni) return -1; } - ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); /* Update STANDARD_INFORMATION. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && @@ -775,10 +788,13 @@ sync_inode: } } - if (!err) - return 0; - errno = err; - return -1; + if (err) { + errno = err; + ret = -1; + } + + ntfs_log_leave("\n"); + return ret; } /** diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index 88fb4730..fbdda328 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -195,16 +195,17 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, u8 has_guess, used_zone_pos; int err = 0, rlpos, rlsize, buf_size; - ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, " + ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, " "zone = %s_ZONE.\n", (long long)count, (long long) start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); + if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { errno = EINVAL; ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld", __FUNCTION__, (long long)start_vcn, (long long)count, (long long)start_lcn); - return NULL; + goto out; } /* Return empty runlist if @count == 0 */ @@ -215,12 +216,12 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, rl[0].lcn = LCN_RL_NOT_MAPPED; rl[0].length = 0; } - return rl; + goto out; } buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); if (!buf) - return NULL; + goto out; /* * If no @start_lcn was requested, use the current zone * position otherwise use the requested @start_lcn. @@ -322,20 +323,30 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, * Coalesce with previous run if adjacent LCNs. * Otherwise, append a new run. */ - if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { + ntfs_log_debug("Cluster coalesce: prev_lcn: " + "%lld lcn: %lld bmp_pos: %lld " + "prev_run_len: %lld\n", prev_lcn, + lcn, bmp_pos, prev_run_len); rl[rlpos - 1].length = ++prev_run_len; - else { + } else { if (rlpos) rl[rlpos].vcn = rl[rlpos - 1].vcn + prev_run_len; - else + else { rl[rlpos].vcn = start_vcn; + ntfs_log_debug("Start_vcn: %lld\n", + start_vcn); + } rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; rl[rlpos].length = prev_run_len = 1; rlpos++; } + ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", + rl[rlpos - 1].vcn, rl[rlpos - 1].lcn, + rl[rlpos - 1].length); /* Done? */ if (!--clusters) { if (used_zone_pos) @@ -467,13 +478,15 @@ done_ret: goto err_ret; } done_err_ret: - ntfs_log_debug("At done_err_ret (follows done_ret).\n"); free(buf); - if (!err) - return rl; - ntfs_log_trace("Failed to allocate clusters (%d)", errno); - errno = err; - return NULL; + if (err) { + errno = err; + ntfs_log_perror("Failed to allocate clusters"); + rl = NULL; + } +out: + ntfs_log_leave("\n"); + return rl; wb_err_ret: ntfs_log_trace("At wb_err_ret.\n"); @@ -486,6 +499,7 @@ err_ret: rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; rl[rlpos].lcn = LCN_RL_NOT_MAPPED; rl[rlpos].length = 0; + ntfs_debug_runlist_dump(rl); ntfs_cluster_free_from_rl(vol, rl); free(rl); rl = NULL; @@ -563,22 +577,23 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) errno = EINVAL; return -1; } - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, (long long)count, (long long)start_vcn); rl = ntfs_attr_find_vcn(na, start_vcn); if (!rl) { if (errno == ENOENT) - return 0; - return -1; + ret = 0; + goto leave; } if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { errno = EIO; ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, (long long)rl->lcn); - return -1; + goto leave; } /* Find the starting cluster inside the run that needs freeing. */ @@ -593,7 +608,7 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) /* Do the actual freeing of the clusters in this run. */ if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, to_free)) - return -1; + goto leave; nr_freed = to_free; } @@ -652,5 +667,7 @@ out: ntfs_log_error("Too many free clusters (%lld > %lld)!", (long long)vol->free_clusters, (long long)vol->nr_clusters); +leave: + ntfs_log_leave("\n"); return ret; } diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index beb96548..7e88c8a4 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -58,6 +58,10 @@ static const char *col_red = "\e[01;31m"; static const char *col_redinv = "\e[01;07;31m"; static const char *col_end = "\e[0m"; +#ifdef DEBUG +static int tab; +#endif + /* Some gcc 3.x, 4.[01].X crash with internal compiler error. */ #if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1) # define BROKEN_GCC_FORMAT_ATTRIBUTE @@ -83,7 +87,8 @@ struct ntfs_logging { */ static struct ntfs_logging ntfs_log = { #ifdef DEBUG - NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | + NTFS_LOG_LEVEL_LEAVE | #endif NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | @@ -214,6 +219,8 @@ static FILE * ntfs_log_get_stream(u32 level) case NTFS_LOG_LEVEL_DEBUG: case NTFS_LOG_LEVEL_TRACE: + case NTFS_LOG_LEVEL_ENTER: + case NTFS_LOG_LEVEL_LEAVE: case NTFS_LOG_LEVEL_WARNING: case NTFS_LOG_LEVEL_ERROR: case NTFS_LOG_LEVEL_CRITICAL: @@ -413,6 +420,9 @@ out: int ntfs_log_handler_fprintf(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) { +#ifdef DEBUG + int i; +#endif int ret = 0; int olderr = errno; FILE *stream; @@ -423,6 +433,13 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, return 0; /* If it's NULL, we can't do anything. */ stream = (FILE*)data; +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_LEAVE) { + if (tab) + tab--; + return 0; + } +#endif if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) { /* Pick a colour determined by the log level */ switch (level) { @@ -449,7 +466,11 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, break; } } - + +#ifdef DEBUG + for (i = 0; i < tab; i++) + ret += fprintf(stream, " "); +#endif if (col_prefix) ret += fprintf(stream, col_prefix); @@ -467,7 +488,7 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, ret += fprintf(stream, "(%d) ", line); if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ - (level & NTFS_LOG_LEVEL_TRACE)) + (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) ret += fprintf(stream, "%s(): ", function); ret += vfprintf(stream, format, args); @@ -478,7 +499,10 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, if (col_suffix) ret += fprintf(stream, col_suffix); - +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_ENTER) + tab++; +#endif fflush(stream); errno = olderr; return ret; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 7c633a98..6310bf75 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -147,7 +147,6 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, void *bmirr = NULL; int cnt = 0, res = 0; - ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { errno = EINVAL; return -1; @@ -410,9 +409,11 @@ int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) MFT_RECORD *m; int ret = -1; + ntfs_log_enter("Entering\n"); + m = ntfs_calloc(vol->mft_record_size); if (!m) - return -1; + goto out; if (ntfs_mft_record_layout(vol, mref, m)) goto free_m; @@ -423,6 +424,8 @@ int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) ret = 0; free_m: free(m); +out: + ntfs_log_leave("\n"); return ret; } @@ -794,11 +797,14 @@ static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) ntfs_attr_search_ctx *ctx; ATTR_RECORD *a; int err; + int ret = -1; + ntfs_log_enter("Entering\n"); + mftbmp_na = vol->mftbmp_na; ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); if (!ctx) - return -1; + goto out; if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { @@ -825,7 +831,8 @@ static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) if (ll == 8) { ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); vol->free_mft_records += (8 * 8); - return 0; + ret = 0; + goto out; } ntfs_log_error("Failed to write to mft bitmap.\n"); err = errno; @@ -860,7 +867,9 @@ put_err_out: (long long)mftbmp_na->initialized_size); err_out: errno = err; - return -1; +out: + ntfs_log_leave("\n"); + return ret; } /** diff --git a/libntfs-3g/runlist.c b/libntfs-3g/runlist.c index 27d08b31..40050624 100644 --- a/libntfs-3g/runlist.c +++ b/libntfs-3g/runlist.c @@ -463,39 +463,10 @@ static runlist_element *ntfs_rl_split(runlist_element *dst, int dsize, /** - * ntfs_runlists_merge - merge two runlists into one - * @drl: original runlist to be worked on - * @srl: new runlist to be merged into @drl - * - * First we sanity check the two runlists @srl and @drl to make sure that they - * are sensible and can be merged. The runlist @srl must be either after the - * runlist @drl or completely within a hole (or unmapped region) in @drl. - * - * Merging of runlists is necessary in two cases: - * 1. When attribute lists are used and a further extent is being mapped. - * 2. When new clusters are allocated to fill a hole or extend a file. - * - * There are four possible ways @srl can be merged. It can: - * - be inserted at the beginning of a hole, - * - split the hole in two and be inserted between the two fragments, - * - be appended at the end of a hole, or it can - * - replace the whole hole. - * It can also be appended to the end of the runlist, which is just a variant - * of the insert case. - * - * On success, return a pointer to the new, combined, runlist. Note, both - * runlists @drl and @srl are deallocated before returning so you cannot use - * the pointers for anything any more. (Strictly speaking the returned runlist - * may be the same as @dst but this is irrelevant.) - * - * On error, return NULL, with errno set to the error code. Both runlists are - * left unmodified. The following error codes are defined: - * ENOMEM Not enough memory to allocate runlist array. - * EINVAL Invalid parameters were passed in. - * ERANGE The runlists overlap and cannot be merged. + * ntfs_runlists_merge_i - see ntfs_runlists_merge */ -runlist_element *ntfs_runlists_merge(runlist_element *drl, - runlist_element *srl) +static runlist_element *ntfs_runlists_merge_i(runlist_element *drl, + runlist_element *srl) { int di, si; /* Current index into @[ds]rl. */ int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ @@ -701,6 +672,49 @@ critical_error: return drl; } +/** + * ntfs_runlists_merge - merge two runlists into one + * @drl: original runlist to be worked on + * @srl: new runlist to be merged into @drl + * + * First we sanity check the two runlists @srl and @drl to make sure that they + * are sensible and can be merged. The runlist @srl must be either after the + * runlist @drl or completely within a hole (or unmapped region) in @drl. + * + * Merging of runlists is necessary in two cases: + * 1. When attribute lists are used and a further extent is being mapped. + * 2. When new clusters are allocated to fill a hole or extend a file. + * + * There are four possible ways @srl can be merged. It can: + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. + * It can also be appended to the end of the runlist, which is just a variant + * of the insert case. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @drl and @srl are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EINVAL Invalid parameters were passed in. + * ERANGE The runlists overlap and cannot be merged. + */ +runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl) +{ + runlist_element *rl; + + ntfs_log_enter("Entering\n"); + rl = ntfs_runlists_merge_i(drl, srl); + ntfs_log_leave("\n"); + return rl; +} + /** * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist * @vol: ntfs volume on which the attribute resides From f4b5e8fb92eaf41be1f29bfb5d5366e913ba1c6d Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 29 Jun 2008 23:08:55 +0000 Subject: [PATCH 176/328] copyright update --- include/ntfs-3g/attrib.h | 2 +- include/ntfs-3g/dir.h | 3 ++- include/ntfs-3g/index.h | 2 +- include/ntfs-3g/inode.h | 6 +++--- include/ntfs-3g/logging.h | 3 ++- include/ntfs-3g/mft.h | 2 +- libntfs-3g/attrib.c | 5 +++-- libntfs-3g/bitmap.c | 2 +- libntfs-3g/bootsect.c | 2 +- libntfs-3g/dir.c | 4 ++-- libntfs-3g/inode.c | 4 ++-- libntfs-3g/lcnalloc.c | 2 +- libntfs-3g/logging.c | 2 +- libntfs-3g/mft.c | 2 +- libntfs-3g/runlist.c | 2 +- libntfs-3g/unistr.c | 2 +- 16 files changed, 24 insertions(+), 21 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index 7656bf16..419937c1 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Yura Pakhuchiy - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2007 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index cbf52683..6d593c5c 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -1,9 +1,10 @@ /* * dir.h - Exports for directory handling. Originated from the Linux-NTFS project. * - * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2002 Anton Altaparmakov * Copyright (c) 2005-2006 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 26fcb1b0..d4c4055b 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -4,7 +4,7 @@ * Copyright (c) 2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index b8c4ec5b..370e02ac 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -1,10 +1,10 @@ /* * inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project. * - * Copyright (c) 2001,2002 Anton Altaparmakov - * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2001-2004 Anton Altaparmakov + * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/include/ntfs-3g/logging.h b/include/ntfs-3g/logging.h index af25dbf1..042ae762 100644 --- a/include/ntfs-3g/logging.h +++ b/include/ntfs-3g/logging.h @@ -1,7 +1,8 @@ /* * logging.h - Centralised logging. Originated from the Linux-NTFS project. * - * Copyright (c) 2005 Richard Russon + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2007-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h index 24e71495..2c739a14 100644 --- a/include/ntfs-3g/mft.h +++ b/include/ntfs-3g/mft.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2002 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index a9b7207e..a982988b 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3,8 +3,9 @@ * * Copyright (c) 2000-2005 Anton Altaparmakov * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2006 Yura Pakhuchiy + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2007-2008 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/libntfs-3g/bitmap.c b/libntfs-3g/bitmap.c index e2b4f01c..60cd4a89 100644 --- a/libntfs-3g/bitmap.c +++ b/libntfs-3g/bitmap.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2006 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index f3ffb5d1..ae5d60a9 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -2,7 +2,7 @@ * bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2003-2006 Szabolcs Szakacsits + * Copyright (c) 2003-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index affcca26..db5c1edd 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -3,8 +3,8 @@ * * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2004-2006 Szabolcs Szakacsits - * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005-2007 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 718ac3ec..b0b900a6 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -2,8 +2,8 @@ * inode.c - Inode handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2002-2005 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index fbdda328..6c07ab0d 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2004 Yura Pakhuchiy - * Copyright (c) 2004-2007 Szabolcs Szakacsits + * Copyright (c) 2004-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index 7e88c8a4..3330fc0d 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -2,7 +2,7 @@ * logging.c - Centralised logging. Originated from the Linux-NTFS project. * * Copyright (c) 2005 Richard Russon - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 6310bf75..cb199b01 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or diff --git a/libntfs-3g/runlist.c b/libntfs-3g/runlist.c index 40050624..6418c143 100644 --- a/libntfs-3g/runlist.c +++ b/libntfs-3g/runlist.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004 Yura Pakhuchiy * * This program/include file is free software; you can redistribute it and/or diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index 5a16633c..fac9ca56 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -2,7 +2,7 @@ * unistr.c - Unicode string handling. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2008 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published From cea1c5c7e889ceb4faf92e27d22048fe726bcfdf Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 29 Jun 2008 23:45:34 +0000 Subject: [PATCH 177/328] fix missing logging.h include --- include/ntfs-3g/mft.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h index 2c739a14..089a195d 100644 --- a/include/ntfs-3g/mft.h +++ b/include/ntfs-3g/mft.h @@ -27,6 +27,7 @@ #include "volume.h" #include "inode.h" #include "layout.h" +#include "logging.h" extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, const s64 count, MFT_RECORD *b); From ab75028fd503ae87a5330ebe501afb071dddd9db Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 30 Jun 2008 02:24:12 +0000 Subject: [PATCH 178/328] cleanup ntfs_volume_write_flags(): remove redundant error logging --- libntfs-3g/volume.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index c2cb4364..c117e518 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1431,10 +1431,9 @@ int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags) vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; /* Write them to disk. */ ntfs_inode_mark_dirty(vol->vol_ni); - if (ntfs_inode_sync(vol->vol_ni)) { - ntfs_log_perror("Error writing $Volume"); + if (ntfs_inode_sync(vol->vol_ni)) goto err_out; - } + ret = 0; /* success */ err_out: ntfs_attr_put_search_ctx(ctx); From c9c732c7fc043fe4fa3936272568f31ab8f875aa Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 30 Jun 2008 02:49:36 +0000 Subject: [PATCH 179/328] add tabulated logging for the most common ntfs_mft_* functions --- libntfs-3g/mft.c | 119 +++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index cb199b01..a57165a1 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -472,7 +472,10 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) u8 *buf, *byte; unsigned int size; u8 pass, b; + int ret = -1; + ntfs_log_enter("Entering\n"); + mftbmp_na = vol->mftbmp_na; /* * Set the end of the pass making sure we do not overflow the mft @@ -496,13 +499,13 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) /* This happens on a freshly formatted volume. */ if (data_pos >= pass_end) { errno = ENOSPC; - return -1; + goto leave; } } pass_start = data_pos; buf = ntfs_malloc(PAGE_SIZE); if (!buf) - return -1; + goto leave; ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, " "pass_end 0x%llx, data_pos 0x%llx.\n", pass, @@ -523,7 +526,7 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) if (ll < 0) { ntfs_log_perror("Failed to read $MFT bitmap"); free(buf); - return -1; + goto leave; } ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll); /* If we read at least one byte, search @buf for a zero bit. */ @@ -541,11 +544,13 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) byte = buf + (bit >> 3); if (*byte == 0xff) continue; + /* Note: ffz() result must be zero based. */ b = ntfs_ffz((unsigned long)*byte); if (b < 8 && b >= (bit & 7)) { free(buf); - return data_pos + (bit & ~7ull) + b; + ret = data_pos + (bit & ~7ull) + b; + goto leave; } } ntfs_log_debug("After inner for loop: size 0x%x, " @@ -580,21 +585,15 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) /* No free mft records in currently initialized mft bitmap. */ free(buf); errno = ENOSPC; - return -1; +leave: + ntfs_log_leave("\n"); + return ret; } /** - * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster - * @vol: volume on which to extend the mft bitmap attribute - * - * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. - * - * Note: Only changes allocated_size, i.e. does not touch initialized_size or - * data_size. - * - * Return 0 on success and -1 on error with errno set to the error code. + * ntfs_mft_bitmap_extend_allocation_i - see ntfs_mft_bitmap_extend_allocation */ -static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) +static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) { LCN lcn; s64 ll = 0; /* silence compiler warning */ @@ -778,6 +777,26 @@ undo_alloc: return -1; } +/** + * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_mft_bitmap_extend_allocation_i(vol); + ntfs_log_leave("\n"); + return ret; +} /** * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data * @vol: volume on which to extend the mft bitmap attribute @@ -896,10 +915,12 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) MFT_RECORD *m = NULL; /* silence compiler warning */ ATTR_RECORD *a = NULL; /* silence compiler warning */ int err, mp_size; + int ret = -1; u32 old_alen = 0; /* silence compiler warning */ BOOL mp_rebuilt = FALSE; - ntfs_log_debug("Extending mft data allocation.\n"); + ntfs_log_enter("Extending mft data allocation.\n"); + mft_na = vol->mft_na; /* * Determine the preferred allocation location, i.e. the last lcn of @@ -908,13 +929,15 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) */ rl = ntfs_attr_find_vcn(mft_na, (mft_na->allocated_size - 1) >> vol->cluster_size_bits); + if (!rl || !rl->length || rl->lcn < 0) { ntfs_log_error("Failed to determine last allocated " "cluster of mft data attribute.\n"); if (rl) errno = EIO; - return -1; + goto out; } + lcn = rl->lcn + rl->length; ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn); /* Minimum allocation is one mft record worth of clusters. */ @@ -925,18 +948,16 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; if (!nr) nr = min_nr; - ntfs_log_debug("Trying mft data allocation with default cluster count " - "%lli.\n", (long long)nr); + old_last_vcn = rl[1].vcn; do { rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE); if (rl2) break; if (errno != ENOSPC || nr == min_nr) { - ntfs_log_perror("Failed to allocate the minimal " - "number of clusters (%lli) for the " - "mft data attribute.\n", (long long)nr); - return -1; + ntfs_log_perror("Failed to allocate (%lld) clusters " + "for $MFT", (long long)nr); + goto out; } /* * There is not enough space to do the allocation, but there @@ -947,6 +968,9 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) ntfs_log_debug("Retrying mft data allocation with minimal cluster " "count %lli.\n", (long long)nr); } while (1); + + ntfs_log_debug("Allocated %lli clusters.\n", nr); + rl = ntfs_runlists_merge(mft_na->rl, rl2); if (!rl) { err = errno; @@ -957,10 +981,10 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) "from the mft data attribute.%s\n", es); free(rl2); errno = err; - return -1; + goto out; } mft_na->rl = rl; - ntfs_log_debug("Allocated %lli clusters.\n", nr); + /* Find the last run in the new runlist. */ for (; rl[1].length; rl++) ; @@ -1046,7 +1070,11 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); - return 0; + ret = 0; +out: + ntfs_log_leave("\n"); + return ret; + restore_undo_alloc: err = errno; ntfs_attr_reinit_search_ctx(ctx); @@ -1061,7 +1089,7 @@ restore_undo_alloc: * base attribute extent which chkdsk should be able to fix. */ errno = err; - return -1; + goto out; } m = ctx->mrec; a = ctx->attr; @@ -1090,7 +1118,7 @@ undo_alloc: if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; - return -1; + goto out; } @@ -1101,7 +1129,7 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) s64 old_data_initialized, old_data_size; ntfs_attr_search_ctx *ctx; - ntfs_log_trace("Entering\n"); + ntfs_log_enter("Entering\n"); /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ @@ -1186,13 +1214,12 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) // BUG_ON(mft_na->data_size > mft_na->allocated_size); /* Sync MFT to minimize data loss if there won't be clean unmount. */ - if (ntfs_inode_sync(mft_na->ni)) { - ntfs_log_error("Failed to sync $MFT."); + if (ntfs_inode_sync(mft_na->ni)) goto undo_data_init; - } ret = 0; out: + ntfs_log_leave("\n"); return ret; undo_data_init: @@ -1289,19 +1316,19 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) s64 ll, bit; ntfs_attr *mft_na, *mftbmp_na; MFT_RECORD *m; - ntfs_inode *ni; + ntfs_inode *ni = NULL; int err; u16 seq_no, usn; if (base_ni) - ntfs_log_trace("Entering (allocating an extent mft record for " - "base mft record 0x%llx).\n", - (long long)base_ni->mft_no); + ntfs_log_enter("Entering (allocating an extent mft record for " + "base mft record %lld).\n", + (long long)base_ni->mft_no); else - ntfs_log_trace("Entering (allocating a base mft record).\n"); + ntfs_log_enter("Entering (allocating a base mft record)\n"); if (!vol || !vol->mft_na || !vol->mftbmp_na) { errno = EINVAL; - return NULL; + goto out; } mft_na = vol->mft_na; @@ -1309,12 +1336,12 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) retry: bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); if (bit >= 0) { - ntfs_log_debug("Found free record (#1), bit 0x%llx.\n", + ntfs_log_debug("found free record (#1) at %lld\n", (long long)bit); goto found_free_rec; } if (errno != ENOSPC) - return NULL; + goto out; /* * No free mft records left. If the mft bitmap already covers more * than the currently used mft records, the next records are all free, @@ -1329,7 +1356,7 @@ retry: bit = ll; if (bit < 24) bit = 24; - ntfs_log_debug("Found free record (#2), bit 0x%llx.\n", + ntfs_log_debug("found free record (#2) at %lld\n", (long long)bit); goto found_free_rec; } @@ -1369,14 +1396,13 @@ retry: (long long)mftbmp_na->allocated_size, (long long)mftbmp_na->data_size, (long long)mftbmp_na->initialized_size); - ntfs_log_debug("Found free record (#3), bit 0x%llx.\n", (long long)bit); + ntfs_log_debug("found free record (#3) at %lld\n", (long long)bit); found_free_rec: /* @bit is the found free mft record, allocate it in the mft bitmap. */ if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); goto err_out; } - ntfs_log_debug("Set bit 0x%llx in mft bitmap.\n", (long long)bit); /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ ll = (bit + 1) << vol->mft_record_size_bits; @@ -1475,9 +1501,11 @@ found_free_rec: if (!base_ni) vol->mft_data_pos = bit + 1; /* Return the opened, allocated inode of the allocated mft record. */ - ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n", + ntfs_log_debug("allocated %sinode 0x%llx.\n", base_ni ? "extent " : "", (long long)bit); vol->free_mft_records--; +out: + ntfs_log_leave("\n"); return ni; undo_mftbmp_alloc: @@ -1488,7 +1516,8 @@ undo_mftbmp_alloc: err_out: if (!errno) errno = EIO; - return NULL; + ni = NULL; + goto out; } /** From e21cfaa809749f30d467e2824d6c748b58c09b0a Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 30 Jun 2008 11:26:16 +0000 Subject: [PATCH 180/328] version 1.2630 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 78cdefb5..1a8fad00 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2531],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="31" +AC_INIT([ntfs-3g],[1.2630],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="33" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From ff1e14e4fb2674cd5dba11ff7e658317dcc8173a Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 10 Jul 2008 22:30:35 +0000 Subject: [PATCH 181/328] open() API documentation update (Nikolaus Rath) --- include/fuse-lite/fuse.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h index 08d317da..8ea787a0 100644 --- a/include/fuse-lite/fuse.h +++ b/include/fuse-lite/fuse.h @@ -125,7 +125,8 @@ struct fuse_operations { /** File open operation * * No creation, or truncation flags (O_CREAT, O_EXCL, O_TRUNC) - * will be passed to open(). Open should check if the operation + * will be passed to open(). Unless the 'default_permissions' + * mount option is given, open should check if the operation * is permitted for the given flags. Optionally open may also * return an arbitrary filehandle in the fuse_file_info structure, * which will be passed to all file operations. From ed96bb15e5d685bc03e5f7cdeeb5086290c7f124 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 10 Jul 2008 22:55:50 +0000 Subject: [PATCH 182/328] fix: the driver didn't work if the standard I/O file descriptors were closed during mount, e.g. via some configuration of udev/hotplug (Ugo Riboni, laoGe) --- libfuse-lite/helper.c | 10 ---------- src/ntfs-3g.c | 12 +++++++++++- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c index e2860433..2827cf71 100644 --- a/libfuse-lite/helper.c +++ b/libfuse-lite/helper.c @@ -160,16 +160,6 @@ static struct fuse_chan *fuse_mount_common(const char *mountpoint, struct fuse_chan *ch; int 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); - fd = fuse_kern_mount(mountpoint, args); if (fd == -1) return NULL; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 3bc23d2b..f8b1b20b 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2231,7 +2231,17 @@ int main(int argc, char *argv[]) struct fuse *fh; fuse_fstype fstype = FSTYPE_UNKNOWN; struct stat sbuf; - int err; + 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())) { From 51b5d60facf791fc285183b96a6d9ff4d6901428 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 12 Jul 2008 12:29:53 +0000 Subject: [PATCH 183/328] ntfs_bitmap_set_bits_in_run(): log errors --- libntfs-3g/bitmap.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/bitmap.c b/libntfs-3g/bitmap.c index 60cd4a89..32363f4d 100644 --- a/libntfs-3g/bitmap.c +++ b/libntfs-3g/bitmap.c @@ -121,6 +121,8 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, if (!na || start_bit < 0 || count < 0) { errno = EINVAL; + ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)", + __FUNCTION__, na, (long long)start_bit, (long long)count); return -1; } @@ -172,7 +174,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, lastbyte_pos = ((count + 7) >> 3) + firstbyte; if (!lastbyte_pos) { // FIXME: Eeek! BUG! - ntfs_log_trace("Eeek! lastbyte is zero. Leaving " + ntfs_log_error("Lastbyte is zero. Leaving " "inconsistent metadata.\n"); err = EIO; goto free_err_out; @@ -186,9 +188,9 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, 3, 1, lastbyte_buf); if (br != 1) { // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Read of last byte " - "failed. Leaving " - "inconsistent metadata.\n"); + ntfs_log_perror("Reading of last byte " + "failed (%lld). Leaving inconsistent " + "metadata", (long long)br); err = EIO; goto free_err_out; } @@ -211,8 +213,9 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, br = ntfs_attr_pwrite(na, tmp, bufsize, buf); if (br != bufsize) { // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Failed to write buffer to bitmap. " - "Leaving inconsistent metadata.\n"); + ntfs_log_perror("Failed to write buffer to bitmap " + "(%lld != %lld). Leaving inconsistent metadata", + (long long)br, (long long)bufsize); err = EIO; goto free_err_out; } @@ -234,9 +237,9 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, if (lastbyte && count != 0) { // FIXME: Eeek! BUG! - ntfs_log_trace("Eeek! Last buffer but count is not zero (= " - "%lli). Leaving inconsistent metadata.\n", - (long long)count); + ntfs_log_error("Last buffer but count is not zero " + "(%lld). Leaving inconsistent metadata.\n", + (long long)count); err = EIO; goto free_err_out; } From d878ee30adc0fe5d7a89ecb9e8d31408e741cdd1 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 12 Jul 2008 12:38:56 +0000 Subject: [PATCH 184/328] ntfs_bitmap_set_bits_in_run(): cleanup error handling --- libntfs-3g/bitmap.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/libntfs-3g/bitmap.c b/libntfs-3g/bitmap.c index 32363f4d..96f2cb9b 100644 --- a/libntfs-3g/bitmap.c +++ b/libntfs-3g/bitmap.c @@ -117,7 +117,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, { s64 bufsize, br; u8 *buf, *lastbyte_buf; - int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err; + int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1; if (!na || start_bit < 0 || count < 0) { errno = EINVAL; @@ -176,7 +176,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, // FIXME: Eeek! BUG! ntfs_log_error("Lastbyte is zero. Leaving " "inconsistent metadata.\n"); - err = EIO; + errno = EIO; goto free_err_out; } /* and it is in the currently loaded bitmap window... */ @@ -191,7 +191,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, ntfs_log_perror("Reading of last byte " "failed (%lld). Leaving inconsistent " "metadata", (long long)br); - err = EIO; + errno = EIO; goto free_err_out; } /* and set/clear the appropriate bits in it. */ @@ -216,7 +216,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, ntfs_log_perror("Failed to write buffer to bitmap " "(%lld != %lld). Leaving inconsistent metadata", (long long)br, (long long)bufsize); - err = EIO; + errno = EIO; goto free_err_out; } @@ -240,19 +240,16 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, ntfs_log_error("Last buffer but count is not zero " "(%lld). Leaving inconsistent metadata.\n", (long long)count); - err = EIO; + errno = EIO; goto free_err_out; } } while (count > 0); - - /* Done! */ - free(buf); - return 0; - + + ret = 0; + free_err_out: free(buf); - errno = err; - return -1; + return ret; } /** From 6d97008d04009cafb7808ec1dc79fab0a2542d35 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 12 Jul 2008 12:52:50 +0000 Subject: [PATCH 185/328] ntfs_bitmap_set_bits_in_run(): report real reason, not EIO if read/write fails --- libntfs-3g/bitmap.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libntfs-3g/bitmap.c b/libntfs-3g/bitmap.c index 96f2cb9b..65162a29 100644 --- a/libntfs-3g/bitmap.c +++ b/libntfs-3g/bitmap.c @@ -149,9 +149,9 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, /* read it in... */ br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); if (br != 1) { - free(buf); - errno = EIO; - return -1; + if (br >= 0) + errno = EIO; + goto free_err_out; } /* and set or clear the appropriate bits in it. */ while ((bit & 7) && count--) { @@ -188,10 +188,11 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, 3, 1, lastbyte_buf); if (br != 1) { // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; ntfs_log_perror("Reading of last byte " "failed (%lld). Leaving inconsistent " "metadata", (long long)br); - errno = EIO; goto free_err_out; } /* and set/clear the appropriate bits in it. */ @@ -213,10 +214,11 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, br = ntfs_attr_pwrite(na, tmp, bufsize, buf); if (br != bufsize) { // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; ntfs_log_perror("Failed to write buffer to bitmap " "(%lld != %lld). Leaving inconsistent metadata", (long long)br, (long long)bufsize); - errno = EIO; goto free_err_out; } From 933ac7f05494230df590aeb68576dcf34a02f09b Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 12 Jul 2008 14:53:46 +0000 Subject: [PATCH 186/328] fix: build failed using --prefix=/ --- libntfs-3g/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index 6ac7eea6..825742dc 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -54,15 +54,15 @@ endif # And create ldscript or symbolic link from /usr install-exec-hook: install-rootlibLTLIBRARIES if INSTALL_LIBRARY - if [ "$(rootlibdir)" != "$(libdir)" ]; then \ + if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ $(MV) -f "$(DESTDIR)/$(libdir)"/libntfs-3g.so* "$(DESTDIR)/$(rootlibdir)"; \ fi if GENERATE_LDSCRIPT - if [ "$(rootlibdir)" != "$(libdir)" ]; then \ + if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ $(install_sh_PROGRAM) "libntfs-3g.script.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ fi else - if [ "$(rootlibdir)" != "$(libdir)" ]; then \ + if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ $(LN_S) "$(rootlibdir)/libntfs-3g.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ fi endif From 6ef25e0a4672b98595057716783adf096577bf33 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 12 Jul 2008 15:16:02 +0000 Subject: [PATCH 187/328] release 1.2712 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 1a8fad00..40a301b1 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ([2.59]) -AC_INIT([ntfs-3g],[1.2630],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="33" +AC_INIT([ntfs-3g],[1.2712],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="34" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From c46ce72177bb8790c5070de38716204b02001eab Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 13 Jul 2008 13:56:38 +0000 Subject: [PATCH 188/328] move unused, conditional MAX_PATH define to the Win32 specific code --- include/ntfs-3g/dir.h | 4 ---- libntfs-3g/win32_io.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index 6d593c5c..2f5f76e6 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -29,10 +29,6 @@ #define PATH_SEP '/' -#ifndef MAX_PATH -#define MAX_PATH 1024 -#endif - /* * We do not have these under DJGPP, so define our version that do not conflict * with other S_IFs defined under DJGPP. diff --git a/libntfs-3g/win32_io.c b/libntfs-3g/win32_io.c index 90a7bda2..ed9fa521 100644 --- a/libntfs-3g/win32_io.c +++ b/libntfs-3g/win32_io.c @@ -52,6 +52,10 @@ typedef struct ntfs_volume ntfs_volume; #include "types.h" #include "device.h" +#ifndef MAX_PATH +#define MAX_PATH 1024 +#endif + #ifndef NTFS_BLOCK_SIZE #define NTFS_BLOCK_SIZE 512 #define NTFS_BLOCK_SIZE_BITS 9 From 61842b0bacf4edc17a9fa6b3618b8603291c144a Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 13 Jul 2008 23:52:55 +0000 Subject: [PATCH 189/328] ntfs_attr_update_mapping_pairs(): fix last ntfs_attr_update_meta() errno --- libntfs-3g/attrib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index a982988b..b04448b2 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4347,7 +4347,7 @@ retry: switch (err) { case -1: return -1; case -2: goto retry; - case -3: goto put_err_out; + case -3: err = errno; goto put_err_out; } /* Get the size for the rest of mapping pairs array. */ From 90b0f9b4246d5b8f948bb01340b3b331fcfdee8b Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 14 Jul 2008 00:37:06 +0000 Subject: [PATCH 190/328] ntfs_attr_update_mapping_pairs(): error handling cleanup --- libntfs-3g/attrib.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index b04448b2..67b66bb9 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4319,7 +4319,7 @@ retry: */ first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); if (first_lcn == LCN_EINVAL) { - err = EIO; + errno = EIO; ntfs_log_perror("Bad runlist"); goto put_err_out; } @@ -4343,18 +4343,16 @@ retry: continue; } - err = ntfs_attr_update_meta(a, na, m, ctx); - switch (err) { + switch (ntfs_attr_update_meta(a, na, m, ctx)) { case -1: return -1; case -2: goto retry; - case -3: err = errno; goto put_err_out; + case -3: goto put_err_out; } /* Get the size for the rest of mapping pairs array. */ mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl, stop_vcn); if (mp_size <= 0) { - err = errno; ntfs_log_perror("%s: get MP size failed", __FUNCTION__); goto put_err_out; } @@ -4413,8 +4411,8 @@ retry: if (ntfs_attr_record_resize(m, a, le16_to_cpu(a->mapping_pairs_offset) + mp_size)) { + errno = EIO; ntfs_log_perror("Failed to resize attribute"); - err = EIO; goto put_err_out; } } @@ -4438,7 +4436,6 @@ retry: stop_vcn, &stop_vcn)) finished_build = TRUE; if (!finished_build && errno != ENOSPC) { - err = errno; ntfs_log_perror("Failed to build mapping pairs"); goto put_err_out; } @@ -4446,7 +4443,6 @@ retry: } /* Check whether error occurred. */ if (errno != ENOENT) { - err = errno; ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); goto put_err_out; } @@ -4462,14 +4458,12 @@ retry: continue; /* Remove unused attribute record. */ if (ntfs_attr_record_rm(ctx)) { - err = errno; ntfs_log_perror("Could not remove unused attr"); goto put_err_out; } ntfs_attr_reinit_search_ctx(ctx); } if (errno != ENOENT) { - err = errno; ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__); goto put_err_out; } @@ -4486,14 +4480,12 @@ retry: mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl, stop_vcn); if (mp_size <= 0) { - err = errno; ntfs_log_perror("%s: get mp size failed", __FUNCTION__); goto put_err_out; } /* Allocate new mft record. */ ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); if (!ni) { - err = errno; ntfs_log_perror("Could not allocate new MFT record"); goto put_err_out; } @@ -4518,6 +4510,7 @@ retry: ntfs_log_perror("Could not add attribute extent"); if (ntfs_mft_record_free(na->ni->vol, ni)) ntfs_log_perror("Could not free MFT record"); + errno = err; goto put_err_out; } a = (ATTR_RECORD*)((u8*)m + err); @@ -4530,6 +4523,7 @@ retry: ntfs_log_perror("Failed to build MP"); if (ntfs_mft_record_free(na->ni->vol, ni)) ntfs_log_perror("Couldn't free MFT record"); + errno = err; goto put_err_out; } a->highest_vcn = cpu_to_sle64(stop_vcn - 1); @@ -4545,7 +4539,6 @@ out: put_err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); - errno = err; goto out; } #undef NTFS_VCN_DELETE_MARK From 886cbb739fe40215ecf072f867786c61356a03de Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 14 Jul 2008 23:49:30 +0000 Subject: [PATCH 191/328] autoupdate fixes: AC_PREREQ, AM_CONFIG_HEADER->AC_CONFIG_HEADERS (Alon Bar-Lev) --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 40a301b1..04e60bb2 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ # Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Autoconf -AC_PREREQ([2.59]) +AC_PREREQ(2.59) AC_INIT([ntfs-3g],[1.2712],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="34" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) @@ -33,7 +33,7 @@ AC_CANONICAL_TARGET # Automake AM_INIT_AUTOMAKE([${PACKAGE_NAME}], [${PACKAGE_VERSION}]) -AM_CONFIG_HEADER([config.h]) +AC_CONFIG_HEADERS([config.h]) AM_MAINTAINER_MODE # Options From 371d18c5f0a138becd49638c9efd6f66ca06edf0 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 14 Jul 2008 23:54:37 +0000 Subject: [PATCH 192/328] autoupdate fixes: remove spaces (Alon Bar-Lev) --- configure.ac | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 04e60bb2..8184d72b 100644 --- a/configure.ac +++ b/configure.ac @@ -39,42 +39,42 @@ AM_MAINTAINER_MODE # Options AC_ARG_ENABLE( [debug], - [AC_HELP_STRING([--enable-debug], [enable debugging code and output])], + [AS_HELP_STRING([--enable-debug],[enable debugging code and output])], , [enable_debug="no"] ) AC_ARG_ENABLE( [warnings], - [AC_HELP_STRING([--enable-warnings], [enable lots of compiler warnings])], + [AS_HELP_STRING([--enable-warnings],[enable lots of compiler warnings])], , [enable_warnings="no"] ) AC_ARG_ENABLE( [pedantic], - [AC_HELP_STRING([--enable-pedantic], [enable compile pedantic mode])], + [AS_HELP_STRING([--enable-pedantic],[enable compile pedantic mode])], , [enable_pedantic="no"] ) AC_ARG_ENABLE( [really-static], - [AC_HELP_STRING([--enable-really-static], [create fully static binaries])], + [AS_HELP_STRING([--enable-really-static],[create fully static binaries])], , [enable_really_static="no"] ) AC_ARG_ENABLE( [library], - [AC_HELP_STRING([--disable-library], [do not install libntfs-3g but link it into ntfs-3g])], + [AS_HELP_STRING([--disable-library],[do not install libntfs-3g but link it into ntfs-3g])], , [enable_library="yes"] ) AC_ARG_ENABLE( [mount-helper], - [AC_HELP_STRING([--enable-mount-helper], [install mount helper @<:@default=enabled for linux@:>@])], + [AS_HELP_STRING([--enable-mount-helper],[install mount helper @<:@default=enabled for linux@:>@])], , [ case "${target_os}" in @@ -86,28 +86,28 @@ AC_ARG_ENABLE( AC_ARG_ENABLE( [ldconfig], - [AC_HELP_STRING([--disable-ldconfig], [do not update dynamic linker cache using ldconfig])], + [AS_HELP_STRING([--disable-ldconfig],[do not update dynamic linker cache using ldconfig])], , [enable_ldconfig="yes"] ) AC_ARG_ENABLE( [ldscript], - [AC_HELP_STRING([--enable-ldscript], [use ldscript instead of .so symlink])], + [AS_HELP_STRING([--enable-ldscript],[use ldscript instead of .so symlink])], , [enable_ldscript="no"] ) AC_ARG_ENABLE( [mtab], - [AC_HELP_STRING([--disable-mtab], [disable and ignore usage of /etc/mtab])], + [AS_HELP_STRING([--disable-mtab],[disable and ignore usage of /etc/mtab])], , [enable_mtab="yes"] ) AC_ARG_ENABLE( [device-default-io-ops], - [AC_HELP_STRING([--disable-device-default-io-ops], [install default IO ops])], + [AS_HELP_STRING([--disable-device-default-io-ops],[install default IO ops])], , [enable_device_default_io_ops="yes"] ) @@ -149,7 +149,7 @@ case "${target_os}" in linux*) AC_ARG_WITH( [fuse], - [AC_HELP_STRING([--with-fuse=], [Select FUSE library: internal or external @<:@default=internal@:>@])], + [AS_HELP_STRING([--with-fuse=],[Select FUSE library: internal or external @<:@default=internal@:>@])], , [with_fuse="internal"] ) From 53792aec38739d13dda258fe5a8cb4a8a66c07be Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 00:11:09 +0000 Subject: [PATCH 193/328] add libtool-2 support (Alon Bar-Lev) --- Makefile.am | 11 ++++++++++- configure.ac | 9 ++++++++- m4/.keep | 0 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 m4/.keep diff --git a/Makefile.am b/Makefile.am index 1208f18c..f1ff59cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,6 @@ AUTOMAKE_OPTIONS = gnu +ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = AUTHORS CREDITS COPYING NEWS autogen.sh @@ -16,8 +17,16 @@ MAINTAINERCLEANFILES=\ $(srcdir)/config.sub \ $(srcdir)/config.h.in \ $(srcdir)/config.h.in~ \ - $(srcdir)/INSTALL + $(srcdir)/INSTALL \ + $(srcdir)/m4/ltsugar.m4 \ + $(srcdir)/m4/libtool.m4 \ + $(srcdir)/m4/ltversion.m4 \ + $(srcdir)/m4/lt~obsolete.m4 \ + $(srcdir)/m4/ltoptions.m4 SUBDIRS = include libfuse-lite libntfs-3g src doc_DATA = README + +dist-hook: + $(MKDIR_P) "$(distdir)/m4" diff --git a/configure.ac b/configure.ac index 8184d72b..1006509b 100644 --- a/configure.ac +++ b/configure.ac @@ -34,6 +34,7 @@ AC_CANONICAL_TARGET # Automake AM_INIT_AUTOMAKE([${PACKAGE_NAME}], [${PACKAGE_VERSION}]) AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) AM_MAINTAINER_MODE # Options @@ -117,9 +118,15 @@ AC_GNU_SOURCE # Programs AC_PROG_CC -AC_PROG_LIBTOOL AC_PROG_LN_S AM_PROG_CC_C_O + +ifdef( + [LT_INIT], + [LT_INIT], + [AC_PROG_LIBTOOL] +) + AC_PATH_PROG([MV], [mv]) AC_PATH_PROG([RM], [rm]) AC_PATH_PROG([SED], [sed]) diff --git a/m4/.keep b/m4/.keep new file mode 100644 index 00000000..e69de29b From a1ca066aece168432e2048ccaa45cfba566bfa30 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 14:29:52 +0000 Subject: [PATCH 194/328] ntfs_ie_add(): #ifdef DEBUG debug code (Yura Pakhuchiy) --- libntfs-3g/index.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index fc8e4ac2..2e804514 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1421,14 +1421,16 @@ static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) { - char *fn; INDEX_HEADER *ih; int allocated_size, new_size; int ret = STATUS_ERROR; +#ifdef DEBUG + char *fn; fn = ntfs_ie_filename_get(ie); ntfs_log_trace("file: '%s'\n", fn); ntfs_attr_name_free(&fn); +#endif while (1) { From 82abdbde27eb7778ec65585184ac1a754867c486 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 14:44:20 +0000 Subject: [PATCH 195/328] remove dead code --- include/ntfs-3g/inode.h | 4 ---- include/ntfs-3g/volume.h | 5 ----- 2 files changed, 9 deletions(-) diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index 370e02ac..c1f5b216 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -127,10 +127,6 @@ struct _ntfs_inode { inode of the base mft record. */ }; - /* Temp: for directory handling */ - void *private_data; /* ntfs_dt containing this inode */ - int ref_count; - /* Below fields are valid only for base inode. */ s64 data_size; /* Data size stored in the filename index. */ s64 allocated_size; /* Allocated size stored in the filename diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 67ca94f6..f8cab845 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -217,11 +217,6 @@ struct _ntfs_volume { s64 free_clusters; /* Track the number of free clusters which greatly improves statfs() performance */ s64 free_mft_records; /* Same for free mft records (see above) */ - - /* Temp: for directory handling */ - void *private_data; /* ntfs_dir for . */ - void *private_bmp1; /* ntfs_bmp for $MFT/$BITMAP */ - void *private_bmp2; /* ntfs_bmp for $Bitmap */ }; extern ntfs_volume *ntfs_volume_alloc(void); From 45f3921b74363a1ba15dddeb96ca1ee6721d0c3b Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 15:15:24 +0000 Subject: [PATCH 196/328] reorganize --enable/disable configure options --- configure.ac | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 1006509b..a62c4c17 100644 --- a/configure.ac +++ b/configure.ac @@ -66,13 +66,6 @@ AC_ARG_ENABLE( [enable_really_static="no"] ) -AC_ARG_ENABLE( - [library], - [AS_HELP_STRING([--disable-library],[do not install libntfs-3g but link it into ntfs-3g])], - , - [enable_library="yes"] -) - AC_ARG_ENABLE( [mount-helper], [AS_HELP_STRING([--enable-mount-helper],[install mount helper @<:@default=enabled for linux@:>@])], @@ -85,6 +78,13 @@ AC_ARG_ENABLE( ] ) +AC_ARG_ENABLE( + [ldscript], + [AS_HELP_STRING([--enable-ldscript],[use ldscript instead of .so symlink])], + , + [enable_ldscript="no"] +) + AC_ARG_ENABLE( [ldconfig], [AS_HELP_STRING([--disable-ldconfig],[do not update dynamic linker cache using ldconfig])], @@ -93,10 +93,10 @@ AC_ARG_ENABLE( ) AC_ARG_ENABLE( - [ldscript], - [AS_HELP_STRING([--enable-ldscript],[use ldscript instead of .so symlink])], + [library], + [AS_HELP_STRING([--disable-library],[do not install libntfs-3g but link it into ntfs-3g])], , - [enable_ldscript="no"] + [enable_library="yes"] ) AC_ARG_ENABLE( From 59f002c253522012d2d5fdf97781e4a15e5de689 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 15:39:53 +0000 Subject: [PATCH 197/328] setup_logging(): fix printf NULL pointer crash on Solaris (Mark Phalan) --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index f8b1b20b..120cf907 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2221,7 +2221,7 @@ static void setup_logging(char *parsed_options) 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); + ntfs_log_info("Cmdline options: %s\n", opts.options ? opts.options : ""); ntfs_log_info("Mount options: %s\n", parsed_options); } From 64560ad1f4de4f253c4f0a4be34610a70fc26e0b Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 16:05:05 +0000 Subject: [PATCH 198/328] ntfs_attr_add(): fix ->attrs_offset conversion on error path (Yura Pakhuchiy) --- libntfs-3g/attrib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 67b66bb9..1df6c6f9 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3256,7 +3256,7 @@ rm_attr_err_out: free_err_out: /* Free MFT record, if it isn't contain attributes. */ if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - - le32_to_cpu(attr_ni->mrec->attrs_offset) == 8) + le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) ntfs_log_perror("Failed to free MFT record"); err_out: From d1f14ea75257aaeca135a227bcf39a5218992c83 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 16:52:10 +0000 Subject: [PATCH 199/328] ntfs_non_resident_attr_expand(): fix corruption during rollback (Yura Pakhuchiy) --- libntfs-3g/attrib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 1df6c6f9..2f8abbec 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4903,7 +4903,7 @@ rollback: ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); } else { /* Prepare to mapping pairs update. */ - na->allocated_size = org_alloc_size << vol->cluster_size_bits; + na->allocated_size = org_alloc_size; /* Restore mapping pairs. */ if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> vol->cluster_size_bits*/)) { From 5d2884b5781cbd22f4b78eb046a7a20b298d991f Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 15 Jul 2008 17:11:17 +0000 Subject: [PATCH 200/328] comment update, cleanup (Yura Pakhuchiy) --- include/ntfs-3g/attrib.h | 3 ++- include/ntfs-3g/inode.h | 13 ++++++++++++- libntfs-3g/attrib.c | 10 +++++----- libntfs-3g/inode.c | 2 ++ libntfs-3g/volume.c | 1 - 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index 419937c1..eafa165a 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -185,7 +185,8 @@ struct _ntfs_attr { }; /** - * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr structure + * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr + * structure */ typedef enum { NA_Initialized, /* 1: structure is initialized. */ diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index c1f5b216..c7f1186e 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -128,7 +128,13 @@ struct _ntfs_inode { }; /* Below fields are valid only for base inode. */ - s64 data_size; /* Data size stored in the filename index. */ + + /* + * These two fields are used to sync filename index and guaranteed to be + * correct, however value in index itself maybe wrong (windows itself + * do not update them properly). + */ + s64 data_size; /* Data size of unnamed DATA attribute. */ s64 allocated_size; /* Allocated size stored in the filename index. (NOTE: Equal to allocated size of the unnamed data attribute for normal or @@ -136,6 +142,11 @@ struct _ntfs_inode { of the unnamed data attribute for sparse or compressed files.) */ + /* + * These four fields are copy of relevant fields from + * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME + * attribute in the index. + */ time_t creation_time; time_t last_data_change_time; time_t last_mft_change_time; diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 2f8abbec..87aa2e18 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -500,7 +500,7 @@ void ntfs_attr_close(ntfs_attr *na) * @na: ntfs attribute for which to map (part of) a runlist * @vcn: map runlist part containing this vcn * - * Map the part of a runlist containing the @vcn of an the ntfs attribute @na. + * Map the part of a runlist containing the @vcn of the ntfs attribute @na. * * Return 0 on success and -1 on error with errno set to the error code. */ @@ -543,8 +543,8 @@ int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute * @na: ntfs attribute for which to map the runlist * - * Map the whole runlist of an the ntfs attribute @na. For an attribute made - * up of only one attribute extent this is the same as calling + * Map the whole runlist of the ntfs attribute @na. For an attribute made up + * of only one attribute extent this is the same as calling * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this * will map the runlist fragments from each of the extents thus giving access * to the entirety of the disk allocation of an attribute. @@ -2992,7 +2992,7 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) NInoAttrListClearDirty(base_ni); } - /* Free MFT record, if it isn't contain attributes. */ + /* Free MFT record, if it doesn't contain attributes. */ if (le32_to_cpu(ctx->mrec->bytes_in_use) - le16_to_cpu(ctx->mrec->attrs_offset) == 8) { if (ntfs_mft_record_free(ni->vol, ni)) { @@ -3254,7 +3254,7 @@ rm_attr_err_out: (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) ntfs_log_perror("Failed to remove just added attribute #2"); free_err_out: - /* Free MFT record, if it isn't contain attributes. */ + /* Free MFT record, if it doesn't contain attributes. */ if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index b0b900a6..e1c2e33f 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -262,6 +262,8 @@ err_out: * If it is an extent inode, we disconnect it from its base inode before we * destroy it. * + * It is OK to pass NULL to this function, it is just noop in this case. + * * Return 0 on success or -1 on error with errno set to the error code. On * error, @ni has not been freed. The user should attempt to handle the error * and call ntfs_inode_close() again. The following error codes are defined: diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index c117e518..07a4dfc0 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -79,7 +79,6 @@ ntfs_volume *ntfs_volume_alloc(void) return ntfs_calloc(sizeof(ntfs_volume)); } - static void ntfs_attr_free(ntfs_attr **na) { if (na && *na) { From d3c12f7153a46428bdecb38ac259f0cad9b0d591 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 16 Jul 2008 02:03:50 +0000 Subject: [PATCH 201/328] remove unused FUSE code --- include/fuse-lite/fuse.h | 53 ------- include/fuse-lite/fuse_common.h | 32 ---- libfuse-lite/Makefile.am | 2 - libfuse-lite/fuse.c | 15 +- libfuse-lite/fuse_i.h | 10 +- libfuse-lite/fuse_loop_mt.c | 222 --------------------------- libfuse-lite/fuse_lowlevel.c | 17 +-- libfuse-lite/fuse_mt.c | 25 --- libfuse-lite/helper.c | 260 +------------------------------- 9 files changed, 11 insertions(+), 625 deletions(-) delete mode 100644 libfuse-lite/fuse_loop_mt.c delete mode 100644 libfuse-lite/fuse_mt.c diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h index 8ea787a0..2b58bcd3 100644 --- a/include/fuse-lite/fuse.h +++ b/include/fuse-lite/fuse.h @@ -426,36 +426,6 @@ struct fuse_context { void *private_data; }; -/** - * Main function of FUSE. - * - * This is for the lazy. This is all that has to be called from the - * main() function. - * - * This function does the following: - * - parses command line options (-d -s and -h) - * - passes relevant mount options to the fuse_mount() - * - installs signal handlers for INT, HUP, TERM and PIPE - * - registers an exit handler to unmount the filesystem on program exit - * - creates a fuse handle - * - registers the operations - * - calls either the single-threaded or the multi-threaded event loop - * - * Note: this is currently implemented as a macro. - * - * @param argc the argument counter passed to the main() function - * @param argv the argument vector passed to the main() function - * @param op the file system operation - * @param user_data user data supplied in the context during the init() method - * @return 0 on success, nonzero on failure - */ -/* -int fuse_main(int argc, char *argv[], const struct fuse_operations *op, - void *user_data); -*/ -#define fuse_main(argc, argv, op, user_data) \ - fuse_main_real(argc, argv, op, sizeof(*(op)), user_data) - /* ----------------------------------------------------------- * * More detailed API * * ----------------------------------------------------------- */ @@ -504,21 +474,6 @@ int fuse_loop(struct fuse *f); */ void fuse_exit(struct fuse *f); -/** - * FUSE event loop with multiple threads - * - * Requests from the kernel are processed, and the appropriate - * operations are called. Request are processed in parallel by - * distributing them between multiple threads. - * - * Calling this function requires the pthreads library to be linked to - * the application. - * - * @param f the FUSE handle - * @return 0 if no error occurred, -1 otherwise - */ -int fuse_loop_mt(struct fuse *f); - /** * Get the current context * @@ -537,14 +492,6 @@ struct fuse_context *fuse_get_context(void); */ int fuse_interrupted(void); -/** - * The real main function - * - * Do not call this directly, use fuse_main() - */ -int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, - size_t op_size, void *user_data); - /* * Stacking API */ diff --git a/include/fuse-lite/fuse_common.h b/include/fuse-lite/fuse_common.h index ddc9ce30..20868cf8 100644 --- a/include/fuse-lite/fuse_common.h +++ b/include/fuse-lite/fuse_common.h @@ -133,38 +133,6 @@ struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args); */ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch); -/** - * Parse common options - * - * The following options are parsed: - * - * '-f' foreground - * '-d' '-odebug' foreground, but keep the debug option - * '-s' single threaded - * '-h' '--help' help - * '-ho' help without header - * '-ofsname=..' file system name, if not present, then set to the program - * name - * - * All parameters may be NULL - * - * @param args argument vector - * @param mountpoint the returned mountpoint, should be freed after use - * @param multithreaded set to 1 unless the '-s' option is present - * @param foreground set to 1 if one of the relevant options is present - * @return 0 on success, -1 on failure - */ -int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, - int *multithreaded, int *foreground); - -/** - * Go into the background - * - * @param foreground if true, stay in the foreground - * @return 0 on success, -1 on failure - */ -int fuse_daemonize(int foreground); - /** * Get the version of the library * diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am index baac0bb3..c09e8d7d 100644 --- a/libfuse-lite/Makefile.am +++ b/libfuse-lite/Makefile.am @@ -16,10 +16,8 @@ libfuse_lite_la_SOURCES = \ fuse_i.h \ fuse_kern_chan.c \ fuse_loop.c \ - fuse_loop_mt.c \ fuse_lowlevel.c \ fuse_misc.h \ - fuse_mt.c \ fuse_opt.c \ fuse_session.c \ fuse_signals.c \ diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 808cd315..5de1ffec 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -2677,9 +2677,9 @@ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, return fs; } -struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, - const struct fuse_operations *op, - size_t op_size, void *user_data) +struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *op, size_t op_size, + void *user_data) { struct fuse *f; struct node *root; @@ -2726,7 +2726,7 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, f->conf.readdir_ino = 1; #endif - f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f); + f->se = fuse_lowlevel_new(args, &llop, sizeof(llop), f); if (f->se == NULL) { goto out_free_fs; } @@ -2803,13 +2803,6 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, return NULL; } -struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, - const struct fuse_operations *op, size_t op_size, - void *user_data) -{ - return fuse_new_common(ch, args, op, op_size, user_data); -} - void fuse_destroy(struct fuse *f) { size_t i; diff --git a/libfuse-lite/fuse_i.h b/libfuse-lite/fuse_i.h index 6c1027db..9101fb58 100644 --- a/libfuse-lite/fuse_i.h +++ b/libfuse-lite/fuse_i.h @@ -19,15 +19,11 @@ struct fuse_cmd { struct fuse_chan *ch; }; -struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args, - const struct fuse_operations *op, - size_t op_size, void *user_data); - struct fuse_chan *fuse_kern_chan_new(int fd); -struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, - const struct fuse_lowlevel_ops *op, - size_t op_size, void *userdata); +struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata); void fuse_kern_unmount(const char *mountpoint, int fd); int fuse_kern_mount(const char *mountpoint, struct fuse_args *args); diff --git a/libfuse-lite/fuse_loop_mt.c b/libfuse-lite/fuse_loop_mt.c deleted file mode 100644 index f5082fd3..00000000 --- a/libfuse-lite/fuse_loop_mt.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. -*/ - -#include "config.h" -#include "fuse_lowlevel.h" -#include "fuse_misc.h" -#include "fuse_kernel.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -struct fuse_worker { - struct fuse_worker *prev; - struct fuse_worker *next; - pthread_t thread_id; - size_t bufsize; - char *buf; - struct fuse_mt *mt; -}; - -struct fuse_mt { - pthread_mutex_t lock; - int numworker; - int numavail; - struct fuse_session *se; - struct fuse_chan *prevch; - struct fuse_worker main; - sem_t finish; - int exit; - int error; -}; - -static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next) -{ - struct fuse_worker *prev = next->prev; - w->next = next; - w->prev = prev; - prev->next = w; - next->prev = w; -} - -static void list_del_worker(struct fuse_worker *w) -{ - struct fuse_worker *prev = w->prev; - struct fuse_worker *next = w->next; - prev->next = next; - next->prev = prev; -} - -static int fuse_start_thread(struct fuse_mt *mt); - -static void *fuse_do_work(void *data) -{ - struct fuse_worker *w = (struct fuse_worker *) data; - struct fuse_mt *mt = w->mt; - - while (!fuse_session_exited(mt->se)) { - int isforget = 0; - struct fuse_chan *ch = mt->prevch; - int res = fuse_chan_recv(&ch, w->buf, w->bufsize); - if (res == -EINTR) - continue; - if (res <= 0) { - if (res < 0) { - fuse_session_exit(mt->se); - mt->error = -1; - } - break; - } - - pthread_mutex_lock(&mt->lock); - if (mt->exit) { - pthread_mutex_unlock(&mt->lock); - return NULL; - } - - /* - * This disgusting hack is needed so that zillions of threads - * are not created on a burst of FORGET messages - */ - if (((struct fuse_in_header *) w->buf)->opcode == FUSE_FORGET) - isforget = 1; - - if (!isforget) - mt->numavail--; - if (mt->numavail == 0) - fuse_start_thread(mt); - pthread_mutex_unlock(&mt->lock); - - fuse_session_process(mt->se, w->buf, res, ch); - - pthread_mutex_lock(&mt->lock); - if (!isforget) - mt->numavail++; - if (mt->numavail > 10) { - if (mt->exit) { - pthread_mutex_unlock(&mt->lock); - return NULL; - } - list_del_worker(w); - mt->numavail--; - mt->numworker--; - pthread_mutex_unlock(&mt->lock); - - pthread_detach(w->thread_id); - free(w->buf); - free(w); - return NULL; - } - pthread_mutex_unlock(&mt->lock); - } - - sem_post(&mt->finish); - pause(); - - return NULL; -} - -static int fuse_start_thread(struct fuse_mt *mt) -{ - sigset_t oldset; - sigset_t newset; - int res; - struct fuse_worker *w = malloc(sizeof(struct fuse_worker)); - if (!w) { - fprintf(stderr, "fuse: failed to allocate worker structure\n"); - return -1; - } - memset(w, 0, sizeof(struct fuse_worker)); - w->bufsize = fuse_chan_bufsize(mt->prevch); - w->buf = malloc(w->bufsize); - w->mt = mt; - if (!w->buf) { - fprintf(stderr, "fuse: failed to allocate read buffer\n"); - free(w); - return -1; - } - - /* Disallow signal reception in worker threads */ - sigemptyset(&newset); - sigaddset(&newset, SIGTERM); - sigaddset(&newset, SIGINT); - sigaddset(&newset, SIGHUP); - sigaddset(&newset, SIGQUIT); - pthread_sigmask(SIG_BLOCK, &newset, &oldset); - res = pthread_create(&w->thread_id, NULL, fuse_do_work, w); - pthread_sigmask(SIG_SETMASK, &oldset, NULL); - if (res != 0) { - fprintf(stderr, "fuse: error creating thread: %s\n", strerror(res)); - free(w->buf); - free(w); - return -1; - } - list_add_worker(w, &mt->main); - mt->numavail ++; - mt->numworker ++; - - return 0; -} - -static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w) -{ - pthread_join(w->thread_id, NULL); - pthread_mutex_lock(&mt->lock); - list_del_worker(w); - pthread_mutex_unlock(&mt->lock); - free(w->buf); - free(w); -} - -int fuse_session_loop_mt(struct fuse_session *se) -{ - int err; - struct fuse_mt mt; - struct fuse_worker *w; - - memset(&mt, 0, sizeof(struct fuse_mt)); - mt.se = se; - mt.prevch = fuse_session_next_chan(se, NULL); - mt.error = 0; - mt.numworker = 0; - mt.numavail = 0; - mt.main.thread_id = pthread_self(); - mt.main.prev = mt.main.next = &mt.main; - sem_init(&mt.finish, 0, 0); - fuse_mutex_init(&mt.lock); - - pthread_mutex_lock(&mt.lock); - err = fuse_start_thread(&mt); - pthread_mutex_unlock(&mt.lock); - if (!err) { - /* sem_wait() is interruptible */ - while (!fuse_session_exited(se)) - sem_wait(&mt.finish); - - for (w = mt.main.next; w != &mt.main; w = w->next) - pthread_cancel(w->thread_id); - mt.exit = 1; - pthread_mutex_unlock(&mt.lock); - - while (mt.main.next != &mt.main) - fuse_join_worker(&mt, mt.main.next); - - err = mt.error; - } - - pthread_mutex_destroy(&mt.lock); - sem_destroy(&mt.finish); - fuse_session_reset(se); - return err; -} diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index 3df9d265..42387073 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -1244,13 +1244,8 @@ static void fuse_ll_destroy(void *data) free(f); } -/* - * always call fuse_lowlevel_new_common() internally, to work around a - * misfeature in the FreeBSD runtime linker, which links the old - * version of a symbol to internal references. - */ -struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, - const struct fuse_lowlevel_ops *op, +struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata) { struct fuse_ll *f; @@ -1297,11 +1292,3 @@ struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, return NULL; } - -struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, - const struct fuse_lowlevel_ops *op, - size_t op_size, void *userdata) -{ - return fuse_lowlevel_new_common(args, op, op_size, userdata); -} - diff --git a/libfuse-lite/fuse_mt.c b/libfuse-lite/fuse_mt.c deleted file mode 100644 index 1b850c62..00000000 --- a/libfuse-lite/fuse_mt.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi - - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. -*/ - -#include "config.h" -#include "fuse_i.h" -#include "fuse_lowlevel.h" - -#include -#include -#include -#include -#include - -int fuse_loop_mt(struct fuse *f) -{ - if (f == NULL) - return -1; - - return fuse_session_loop_mt(fuse_get_session(f)); -} diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c index 2827cf71..6c640aec 100644 --- a/libfuse-lite/helper.c +++ b/libfuse-lite/helper.c @@ -8,154 +8,9 @@ #include "config.h" #include "fuse_i.h" -#include "fuse_opt.h" #include "fuse_lowlevel.h" -#include -#include -#include -#include -#include -#include -#include -#include - -enum { - KEY_HELP, - KEY_HELP_NOHEADER, - KEY_VERSION, -}; - -struct helper_opts { - int singlethread; - int foreground; - int nodefault_subtype; - char *mountpoint; -}; - -#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 } - -static const struct fuse_opt fuse_helper_opts[] = { - FUSE_HELPER_OPT("-d", foreground), - FUSE_HELPER_OPT("debug", foreground), - FUSE_HELPER_OPT("-f", foreground), - FUSE_HELPER_OPT("-s", singlethread), - FUSE_HELPER_OPT("fsname=", nodefault_subtype), - - FUSE_OPT_KEY("-h", KEY_HELP), - FUSE_OPT_KEY("--help", KEY_HELP), - FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER), - FUSE_OPT_KEY("-V", KEY_VERSION), - FUSE_OPT_KEY("--version", KEY_VERSION), - FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), - FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), - FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), - FUSE_OPT_END -}; - -static void usage(const char *progname) -{ - fprintf(stderr, - "usage: %s mountpoint [options]\n\n", progname); - fprintf(stderr, - "general options:\n" - " -o opt,[opt...] mount options\n" - " -h --help print help\n" - " -V --version print version\n" - "\n"); -} - -static void helper_help(void) -{ - fprintf(stderr, - "FUSE options:\n" - " -d -o debug enable debug output (implies -f)\n" - " -f foreground operation\n" - " -s disable multi-threaded operation\n" - "\n" - ); -} - -static void helper_version(void) -{ - fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION); -} - -static int fuse_helper_opt_proc(void *data, const char *arg, int key, - struct fuse_args *outargs) -{ - struct helper_opts *hopts = data; - - switch (key) { - case KEY_HELP: - usage(outargs->argv[0]); - /* fall through */ - - case KEY_HELP_NOHEADER: - helper_help(); - return fuse_opt_add_arg(outargs, "-h"); - - case KEY_VERSION: - helper_version(); - return 1; - - case FUSE_OPT_KEY_NONOPT: - if (!hopts->mountpoint) { - char mountpoint[PATH_MAX]; - if (realpath(arg, mountpoint) == NULL) { - fprintf(stderr, "fuse: bad mount point `%s': %s\n", arg, strerror(errno)); - return -1; - } - return fuse_opt_add_opt(&hopts->mountpoint, mountpoint); - } else { - fprintf(stderr, "fuse: invalid argument `%s'\n", arg); - return -1; - } - - default: - return 1; - } -} - -int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, - int *multithreaded, int *foreground) -{ - int res; - struct helper_opts hopts; - - memset(&hopts, 0, sizeof(hopts)); - res = fuse_opt_parse(args, &hopts, fuse_helper_opts, fuse_helper_opt_proc); - if (res == -1) - return -1; - - if (mountpoint) - *mountpoint = hopts.mountpoint; - else - free(hopts.mountpoint); - - if (multithreaded) - *multithreaded = !hopts.singlethread; - if (foreground) - *foreground = hopts.foreground; - return 0; -} - -int fuse_daemonize(int foreground) -{ - int res; - - if (!foreground) { - res = daemon(0, 0); - if (res == -1) { - perror("fuse: failed to daemonize program\n"); - return -1; - } - } - return 0; -} - -static struct fuse_chan *fuse_mount_common(const char *mountpoint, - struct fuse_args *args) +struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args) { struct fuse_chan *ch; int fd; @@ -171,124 +26,13 @@ static struct fuse_chan *fuse_mount_common(const char *mountpoint, return ch; } -struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args) -{ - return fuse_mount_common(mountpoint, args); -} - -static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch) +void fuse_unmount(const char *mountpoint, struct fuse_chan *ch) { int fd = ch ? fuse_chan_fd(ch) : -1; fuse_kern_unmount(mountpoint, fd); fuse_chan_destroy(ch); } -void fuse_unmount(const char *mountpoint, struct fuse_chan *ch) -{ - fuse_unmount_common(mountpoint, ch); -} - -static struct fuse *fuse_setup_common(int argc, char *argv[], - const struct fuse_operations *op, - size_t op_size, - char **mountpoint, - int *multithreaded, - int *fd, - void *user_data) -{ - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - struct fuse_chan *ch; - struct fuse *fuse; - int foreground; - int res; - - res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground); - if (res == -1) - return NULL; - - ch = fuse_mount_common(*mountpoint, &args); - if (!ch) { - fuse_opt_free_args(&args); - goto err_free; - } - - fuse = fuse_new_common(ch, &args, op, op_size, user_data); - fuse_opt_free_args(&args); - if (fuse == NULL) - goto err_unmount; - - res = fuse_daemonize(foreground); - if (res == -1) - goto err_unmount; - - res = fuse_set_signal_handlers(fuse_get_session(fuse)); - if (res == -1) - goto err_unmount; - - if (fd) - *fd = fuse_chan_fd(ch); - - return fuse; - - err_unmount: - fuse_unmount_common(*mountpoint, ch); - if (fuse) - fuse_destroy(fuse); - err_free: - free(*mountpoint); - return NULL; -} - -static void fuse_teardown_common(struct fuse *fuse, char *mountpoint) -{ - struct fuse_session *se = fuse_get_session(fuse); - struct fuse_chan *ch = fuse_session_next_chan(se, NULL); - fuse_remove_signal_handlers(se); - fuse_unmount_common(mountpoint, ch); - fuse_destroy(fuse); - free(mountpoint); -} - -static int fuse_main_common(int argc, char *argv[], - const struct fuse_operations *op, size_t op_size, - void *user_data) -{ - struct fuse *fuse; - char *mountpoint; - int multithreaded; - int res; - - fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint, - &multithreaded, NULL, user_data); - if (fuse == NULL) - return 1; - - if (multithreaded) - res = fuse_loop_mt(fuse); - else - res = fuse_loop(fuse); - - fuse_teardown_common(fuse, mountpoint); - if (res == -1) - return 1; - - return 0; -} - -int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, - size_t op_size, void *user_data) -{ - return fuse_main_common(argc, argv, op, op_size, user_data); -} - -#undef fuse_main -int fuse_main(void); -int fuse_main(void) -{ - fprintf(stderr, "fuse_main(): This function does not exist\n"); - return -1; -} - int fuse_version(void) { return FUSE_VERSION; From b660e27c2482aaea45ea6732a7c9f08b6fa8671c Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 17 Jul 2008 15:01:50 +0000 Subject: [PATCH 202/328] add Solaris support (Mark Phalan, Laszlo Peter, Marc Glisse, Szabolcs Szakacsits) --- configure.ac | 11 +-- include/ntfs-3g/compat.h | 13 ++- libntfs-3g/compat.c | 187 +++++++++++++++++++++++++++++++++++++-- src/ntfs-3g.c | 12 ++- 4 files changed, 209 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index a62c4c17..c11a3016 100644 --- a/configure.ac +++ b/configure.ac @@ -161,7 +161,7 @@ linux*) [with_fuse="internal"] ) ;; -darwin*|netbsd*) +darwin*|netbsd*|solaris*) with_fuse="external" ;; freebsd*) @@ -234,7 +234,7 @@ AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ mntent.h stddef.h stdint.h stdlib.h stdio.h stdarg.h string.h \ strings.h errno.h time.h unistd.h utime.h wchar.h getopt.h features.h \ endian.h byteswap.h sys/byteorder.h sys/endian.h sys/param.h \ - sys/ioctl.h sys/mount.h sys/stat.h sys/types.h sys/vfs.h \ + sys/ioctl.h sys/mkdev.h sys/mount.h sys/stat.h sys/types.h sys/vfs.h \ sys/statvfs.h sys/sysmacros.h linux/major.h linux/fd.h linux/hdreg.h \ machine/endian.h windows.h syslog.h]) @@ -270,9 +270,10 @@ AC_FUNC_STRFTIME AC_FUNC_UTIME_NULL AC_FUNC_VPRINTF AC_CHECK_FUNCS([ \ - atexit basename dup2 fdatasync getopt_long hasmntopt mbsinit \ - memmove memset realpath regcomp setlocale setxattr strcasecmp strchr \ - strdup strerror strnlen strtol strtoul sysconf utime fork \ + atexit basename daemon dup2 fdatasync ffs getopt_long hasmntopt \ + mbsinit memmove memset realpath regcomp setlocale setxattr \ + strcasecmp strchr strdup strerror strnlen strsep strtol strtoul \ + sysconf utime fork \ ]) AC_SYS_LARGEFILE diff --git a/include/ntfs-3g/compat.h b/include/ntfs-3g/compat.h index 74d555cf..148f8b75 100644 --- a/include/ntfs-3g/compat.h +++ b/include/ntfs-3g/compat.h @@ -27,13 +27,20 @@ #include "config.h" #endif -#ifdef WINDOWS - #ifndef HAVE_FFS -#define HAVE_FFS extern int ffs(int i); #endif /* HAVE_FFS */ +#ifndef HAVE_DAEMON +extern int daemon(int nochdir, int noclose); +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +extern char *strsep(char **stringp, const char *delim); +#endif /* HAVE_STRSEP */ + +#ifdef WINDOWS + #define HAVE_STDIO_H /* mimic config.h */ #define HAVE_STDARG_H diff --git a/libntfs-3g/compat.c b/libntfs-3g/compat.c index 4d8c41a4..5d2437ae 100644 --- a/libntfs-3g/compat.c +++ b/libntfs-3g/compat.c @@ -20,16 +20,12 @@ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef WINDOWS - #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "compat.h" -/* TODO: Add check for FFS in the configure script... (AIA) */ - #ifndef HAVE_FFS /** * ffs - Find the first set bit in an int @@ -69,5 +65,186 @@ int ffs(int x) } #endif /* HAVE_FFS */ -#endif /* WINDOWS */ +#ifndef HAVE_DAEMON +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/daemon.c + */ +/* + * Copyright (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.2 2008-07-17 15:01:48 szaka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +int daemon(int nochdir, int noclose) { + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, 0); + (void)dup2(fd, 1); + (void)dup2(fd, 2); + if (fd > 2) + (void)close (fd); + } + return (0); +} +/* + * End: src/lib/libresolv2/common/bsd/daemon.c + *************************************************************/ +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/strsep.c + */ +/* + * Copyright (c) 1997, by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.2 2008-07-17 15:01:48 szaka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char *strsep(char **stringp, const char *delim) { + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +/* + * End: src/lib/libresolv2/common/bsd/strsep.c + *************************************************************/ +#endif /* HAVE_STRSEP */ diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 120cf907..39bbefd8 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -74,6 +74,14 @@ #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_MKDEV_H +#include +#endif + +#include "compat.h" #include "attrib.h" #include "inode.h" #include "volume.h" @@ -2287,15 +2295,17 @@ int main(int argc, char *argv[]) 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) { From a970d47c378470c19b9ed4a4f0b27fe9db24ad30 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 17 Jul 2008 15:04:23 +0000 Subject: [PATCH 203/328] version 1.2717 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index c11a3016..cd1a13ab 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.2712],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="34" +AC_INIT([ntfs-3g],[1.2717],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="35" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From d7ae435430bc2aee57e8c31809463054061bd0e2 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 18 Jul 2008 01:07:21 +0000 Subject: [PATCH 204/328] create/mknod cleanup (Yura Pakhuchiy) --- src/ntfs-3g.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 39bbefd8..257522f4 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -914,29 +914,7 @@ static int ntfs_fuse_create_stream(const char *path, return res; } -static int ntfs_fuse_create_file(const char *org_path, mode_t mode, - struct fuse_file_info *fi __attribute__((unused))) -{ - char *path = NULL; - ntfschar *stream_name; - int stream_name_len; - int res; - - stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); - if (stream_name_len < 0) - return stream_name_len; - if (!stream_name_len) - res = ntfs_fuse_create(path, mode & S_IFMT, 0, NULL); - else - res = ntfs_fuse_create_stream(path, stream_name, - stream_name_len); - free(path); - if (stream_name_len) - free(stream_name); - return res; -} - -static int ntfs_fuse_mknod(const char *org_path, mode_t mode, dev_t dev) +static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev) { char *path = NULL; ntfschar *stream_name; @@ -962,6 +940,17 @@ exit: return res; } +static int ntfs_fuse_mknod(const char *path, mode_t mode, dev_t dev) +{ + return ntfs_fuse_mknod_common(path, mode, dev); +} + +static int ntfs_fuse_create_file(const char *path, mode_t mode, + struct fuse_file_info *fi __attribute__((unused))) +{ + return ntfs_fuse_mknod_common(path, mode, 0); +} + static int ntfs_fuse_symlink(const char *to, const char *from) { if (ntfs_fuse_is_named_data_stream(from)) From a13e6e6f0bdbe3f13afeebcc596ff746b4b4d86d Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 18 Jul 2008 11:32:35 +0000 Subject: [PATCH 205/328] mkdir(), open() API documentation update (Nikolaus Rath) --- include/fuse-lite/fuse.h | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h index 2b58bcd3..c09cbf07 100644 --- a/include/fuse-lite/fuse.h +++ b/include/fuse-lite/fuse.h @@ -89,7 +89,12 @@ struct fuse_operations { */ int (*mknod) (const char *, mode_t, dev_t); - /** Create a directory */ + /** Create a directory + * + * Note that the mode argument may not have the type specification + * bits set, i.e. S_ISDIR(mode) can be false. To obtain the + * correct directory type bits use mode|S_IFDIR + * */ int (*mkdir) (const char *, mode_t); /** Remove a file */ @@ -124,12 +129,18 @@ struct fuse_operations { /** File open operation * - * No creation, or truncation flags (O_CREAT, O_EXCL, O_TRUNC) - * will be passed to open(). Unless the 'default_permissions' - * mount option is given, open should check if the operation - * is permitted for the given flags. Optionally open may also - * return an arbitrary filehandle in the fuse_file_info structure, - * which will be passed to all file operations. + * No creation (O_CREAT, O_EXCL) and by default also no + * truncation (O_TRUNC) flags will be passed to open(). If an + * application specifies O_TRUNC, fuse first calls truncate() + * and then open(). Only if 'atomic_o_trunc' has been + * specified and kernel version is 2.6.24 or later, O_TRUNC is + * passed on to open. + * + * Unless the 'default_permissions' mount option is given, + * open should check if the operation is permitted for the + * given flags. Optionally open may also return an arbitrary + * filehandle in the fuse_file_info structure, which will be + * passed to all file operations. * * Changed in version 2.2 */ From cdf4fd5469b4f95d1f77fa5668ece7e5e9a7791d Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 18 Jul 2008 12:37:37 +0000 Subject: [PATCH 206/328] don't flood log with "Operation not supported" compressed/encrypted write errors (Mark McIntyre, Jean-Pierre Andre, Szabolcs Szakacsits) --- src/ntfs-3g.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 257522f4..2c7949a7 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -735,17 +735,17 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, goto exit; } while (size) { - res = ntfs_attr_pwrite(na, offset, size, buf); - if (res < (s64)size) + s64 ret = ntfs_attr_pwrite(na, offset, size, buf); + if (0 <= ret && ret < (s64)size) ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " - "%lld <> %d)", (long long)offset, (long long)size, res); - if (res <= 0) { + "%lld <> %d)", (long long)offset, (long long)size, ret); + if (ret <= 0) { res = -errno; goto exit; } - size -= res; - offset += res; - total += res; + size -= ret; + offset += ret; + total += ret; } res = total; if (res > 0) From b4fd7fe3ce457ab1094fd3d56c61f1ddfe04a457 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 18 Jul 2008 13:04:38 +0000 Subject: [PATCH 207/328] ntfs_fuse_write(): fix format string warning --- src/ntfs-3g.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 2c7949a7..843ec0d3 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -737,8 +737,9 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, while (size) { s64 ret = ntfs_attr_pwrite(na, offset, size, buf); if (0 <= ret && ret < (s64)size) - ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " - "%lld <> %d)", (long long)offset, (long long)size, ret); + ntfs_log_perror("ntfs_attr_pwrite partial write to '%s'" + " (%lld: %lld <> %lld)", path, (long long)offset, + (long long)size, ret); if (ret <= 0) { res = -errno; goto exit; From 43e4670b803c78484cc74b7acbf3d6432d7d3be0 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 18 Jul 2008 14:08:30 +0000 Subject: [PATCH 208/328] remove redundant ctx->vol checks (Yura Pakhuchiy) --- src/ntfs-3g.c | 50 ++++++++++---------------------------------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 843ec0d3..22a04fc1 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -351,17 +351,15 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) int res = 0; ntfs_inode *ni; ntfs_attr *na; - ntfs_volume *vol; char *path = NULL; ntfschar *stream_name; int stream_name_len; - vol = ctx->vol; stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) return stream_name_len; memset(stbuf, 0, sizeof(struct stat)); - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) { res = -errno; goto exit; @@ -595,15 +593,13 @@ static int ntfs_fuse_readdir(const char *path, void *buf, struct fuse_file_info *fi __attribute__((unused))) { ntfs_fuse_fill_context_t fill_ctx; - ntfs_volume *vol; ntfs_inode *ni; s64 pos = 0; int err = 0; - vol = ctx->vol; fill_ctx.filler = filler; fill_ctx.buf = buf; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; if (ntfs_readdir(ni, &pos, &fill_ctx, @@ -618,7 +614,6 @@ static int ntfs_fuse_readdir(const char *path, void *buf, static int ntfs_fuse_open(const char *org_path, struct fuse_file_info *fi __attribute__((unused))) { - ntfs_volume *vol; ntfs_inode *ni; ntfs_attr *na; int res = 0; @@ -629,8 +624,7 @@ static int ntfs_fuse_open(const char *org_path, stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) return stream_name_len; - vol = ctx->vol; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (ni) { na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); if (na) { @@ -711,7 +705,6 @@ exit: static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi __attribute__((unused))) { - ntfs_volume *vol; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; char *path = NULL; @@ -723,8 +716,7 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, res = stream_name_len; goto out; } - vol = ctx->vol; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) { res = -errno; goto exit; @@ -765,7 +757,6 @@ out: static int ntfs_fuse_truncate(const char *org_path, off_t size) { - ntfs_volume *vol; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; int res; @@ -776,8 +767,7 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) return stream_name_len; - vol = ctx->vol; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) goto exit; @@ -1306,17 +1296,13 @@ static const int nf_ns_xattr_preffix_len = 5; static int ntfs_fuse_listxattr(const char *path, char *list, size_t size) { ntfs_attr_search_ctx *actx = NULL; - ntfs_volume *vol; ntfs_inode *ni; char *to = list; int ret = 0; if (ctx->streams != NF_STREAMS_INTERFACE_XATTR) return -EOPNOTSUPP; - vol = ctx->vol; - if (!vol) - return -ENODEV; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; actx = ntfs_attr_get_search_ctx(ni, NULL); @@ -1370,17 +1356,13 @@ static int ntfs_fuse_getxattr_windows(const char *path, const char *name, char *value, size_t size) { ntfs_attr_search_ctx *actx = NULL; - ntfs_volume *vol; ntfs_inode *ni; char *to = value; int ret = 0; if (strcmp(name, "ntfs.streams.list")) return -EOPNOTSUPP; - vol = ctx->vol; - if (!vol) - return -ENODEV; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; actx = ntfs_attr_get_search_ctx(ni, NULL); @@ -1436,7 +1418,6 @@ exit: static int ntfs_fuse_getxattr(const char *path, const char *name, char *value, size_t size) { - ntfs_volume *vol; ntfs_inode *ni; ntfs_attr *na = NULL; ntfschar *lename = NULL; @@ -1449,10 +1430,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, if (strncmp(name, nf_ns_xattr_preffix, nf_ns_xattr_preffix_len) || strlen(name) == (size_t)nf_ns_xattr_preffix_len) return -ENODATA; - vol = ctx->vol; - if (!vol) - return -ENODEV; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename); @@ -1486,7 +1464,6 @@ exit: static int ntfs_fuse_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { - ntfs_volume *vol; ntfs_inode *ni; ntfs_attr *na = NULL; ntfschar *lename = NULL; @@ -1497,10 +1474,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, if (strncmp(name, nf_ns_xattr_preffix, nf_ns_xattr_preffix_len) || strlen(name) == (size_t)nf_ns_xattr_preffix_len) return -EACCES; - vol = ctx->vol; - if (!vol) - return -ENODEV; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename); @@ -1544,7 +1518,6 @@ exit: static int ntfs_fuse_removexattr(const char *path, const char *name) { - ntfs_volume *vol; ntfs_inode *ni; ntfschar *lename = NULL; int res = 0, lename_len; @@ -1555,10 +1528,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) if (strncmp(name, nf_ns_xattr_preffix, nf_ns_xattr_preffix_len) || strlen(name) == (size_t)nf_ns_xattr_preffix_len) return -ENODATA; - vol = ctx->vol; - if (!vol) - return -ENODEV; - ni = ntfs_pathname_to_inode(vol, NULL, path); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; lename_len = ntfs_mbstoucs(name + nf_ns_xattr_preffix_len, &lename); From 87c9b0956f573415b8544e2b966ecf77fbce1dad Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 18 Jul 2008 22:13:46 +0000 Subject: [PATCH 209/328] sync with reindented upstream FUSE headers --- include/fuse-lite/fuse.h | 723 +++++------ include/fuse-lite/fuse_common.h | 112 +- include/fuse-lite/fuse_lowlevel.h | 1475 +++++++++++----------- include/fuse-lite/fuse_lowlevel_compat.h | 10 +- include/fuse-lite/fuse_opt.h | 56 +- 5 files changed, 1195 insertions(+), 1181 deletions(-) diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h index c09cbf07..13f33e99 100644 --- a/include/fuse-lite/fuse.h +++ b/include/fuse-lite/fuse.h @@ -1,9 +1,9 @@ /* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. */ #ifndef _FUSE_H_ @@ -28,7 +28,7 @@ extern "C" { #endif /* ----------------------------------------------------------- * - * Basic FUSE API * + * Basic FUSE API * * ----------------------------------------------------------- */ /** Handle for a FUSE filesystem */ @@ -46,7 +46,7 @@ struct fuse_cmd; * @return 1 if buffer is full, zero otherwise */ typedef int (*fuse_fill_dir_t) (void *buf, const char *name, - const struct stat *stbuf, off_t off); + const struct stat *stbuf, off_t off); /** * The file system operations: @@ -63,356 +63,357 @@ typedef int (*fuse_fill_dir_t) (void *buf, const char *name, * featured filesystem can still be implemented. */ struct fuse_operations { - /** Get file attributes. - * - * Similar to stat(). The 'st_dev' and 'st_blksize' fields are - * ignored. The 'st_ino' field is ignored except if the 'use_ino' - * mount option is given. - */ - int (*getattr) (const char *, struct stat *); + /** Get file attributes. + * + * Similar to stat(). The 'st_dev' and 'st_blksize' fields are + * ignored. The 'st_ino' field is ignored except if the 'use_ino' + * mount option is given. + */ + int (*getattr) (const char *, struct stat *); - /** Read the target of a symbolic link - * - * The buffer should be filled with a null terminated string. The - * buffer size argument includes the space for the terminating - * null character. If the linkname is too long to fit in the - * buffer, it should be truncated. The return value should be 0 - * for success. - */ - int (*readlink) (const char *, char *, size_t); + /** Read the target of a symbolic link + * + * The buffer should be filled with a null terminated string. The + * buffer size argument includes the space for the terminating + * null character. If the linkname is too long to fit in the + * buffer, it should be truncated. The return value should be 0 + * for success. + */ + int (*readlink) (const char *, char *, size_t); - /** Create a file node - * - * This is called for creation of all non-directory, non-symlink - * nodes. If the filesystem defines a create() method, then for - * regular files that will be called instead. - */ - int (*mknod) (const char *, mode_t, dev_t); + /** Create a file node + * + * This is called for creation of all non-directory, non-symlink + * nodes. If the filesystem defines a create() method, then for + * regular files that will be called instead. + */ + int (*mknod) (const char *, mode_t, dev_t); - /** Create a directory - * - * Note that the mode argument may not have the type specification - * bits set, i.e. S_ISDIR(mode) can be false. To obtain the - * correct directory type bits use mode|S_IFDIR - * */ - int (*mkdir) (const char *, mode_t); + /** Create a directory + * + * Note that the mode argument may not have the type specification + * bits set, i.e. S_ISDIR(mode) can be false. To obtain the + * correct directory type bits use mode|S_IFDIR + * */ + int (*mkdir) (const char *, mode_t); - /** Remove a file */ - int (*unlink) (const char *); + /** Remove a file */ + int (*unlink) (const char *); - /** Remove a directory */ - int (*rmdir) (const char *); + /** Remove a directory */ + int (*rmdir) (const char *); - /** Create a symbolic link */ - int (*symlink) (const char *, const char *); + /** Create a symbolic link */ + int (*symlink) (const char *, const char *); - /** Rename a file */ - int (*rename) (const char *, const char *); + /** Rename a file */ + int (*rename) (const char *, const char *); - /** Create a hard link to a file */ - int (*link) (const char *, const char *); + /** Create a hard link to a file */ + int (*link) (const char *, const char *); - /** Change the permission bits of a file */ - int (*chmod) (const char *, mode_t); + /** Change the permission bits of a file */ + int (*chmod) (const char *, mode_t); - /** Change the owner and group of a file */ - int (*chown) (const char *, uid_t, gid_t); + /** Change the owner and group of a file */ + int (*chown) (const char *, uid_t, gid_t); - /** Change the size of a file */ - int (*truncate) (const char *, off_t); + /** Change the size of a file */ + int (*truncate) (const char *, off_t); - /** Change the access and/or modification times of a file - * - * Deprecated, use utimens() instead. - */ - int (*utime) (const char *, struct utimbuf *); + /** Change the access and/or modification times of a file + * + * Deprecated, use utimens() instead. + */ + int (*utime) (const char *, struct utimbuf *); - /** File open operation - * - * No creation (O_CREAT, O_EXCL) and by default also no - * truncation (O_TRUNC) flags will be passed to open(). If an - * application specifies O_TRUNC, fuse first calls truncate() - * and then open(). Only if 'atomic_o_trunc' has been - * specified and kernel version is 2.6.24 or later, O_TRUNC is - * passed on to open. - * - * Unless the 'default_permissions' mount option is given, - * open should check if the operation is permitted for the - * given flags. Optionally open may also return an arbitrary - * filehandle in the fuse_file_info structure, which will be - * passed to all file operations. - * - * Changed in version 2.2 - */ - int (*open) (const char *, struct fuse_file_info *); + /** File open operation + * + * No creation (O_CREAT, O_EXCL) and by default also no + * truncation (O_TRUNC) flags will be passed to open(). If an + * application specifies O_TRUNC, fuse first calls truncate() + * and then open(). Only if 'atomic_o_trunc' has been + * specified and kernel version is 2.6.24 or later, O_TRUNC is + * passed on to open. + * + * Unless the 'default_permissions' mount option is given, + * open should check if the operation is permitted for the + * given flags. Optionally open may also return an arbitrary + * filehandle in the fuse_file_info structure, which will be + * passed to all file operations. + * + * Changed in version 2.2 + */ + int (*open) (const char *, struct fuse_file_info *); - /** Read data from an open file - * - * Read should return exactly the number of bytes requested except - * on EOF or error, otherwise the rest of the data will be - * substituted with zeroes. An exception to this is when the - * 'direct_io' mount option is specified, in which case the return - * value of the read system call will reflect the return value of - * this operation. - * - * Changed in version 2.2 - */ - int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *); + /** Read data from an open file + * + * Read should return exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the + * 'direct_io' mount option is specified, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * Changed in version 2.2 + */ + int (*read) (const char *, char *, size_t, off_t, + struct fuse_file_info *); - /** Write data to an open file - * - * Write should return exactly the number of bytes requested - * except on error. An exception to this is when the 'direct_io' - * mount option is specified (see read operation). - * - * Changed in version 2.2 - */ - int (*write) (const char *, const char *, size_t, off_t, - struct fuse_file_info *); + /** Write data to an open file + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the 'direct_io' + * mount option is specified (see read operation). + * + * Changed in version 2.2 + */ + int (*write) (const char *, const char *, size_t, off_t, + struct fuse_file_info *); - /** Get file system statistics - * - * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored - * - * Replaced 'struct statfs' parameter with 'struct statvfs' in - * version 2.5 - */ - int (*statfs) (const char *, struct statvfs *); + /** Get file system statistics + * + * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored + * + * Replaced 'struct statfs' parameter with 'struct statvfs' in + * version 2.5 + */ + int (*statfs) (const char *, struct statvfs *); - /** Possibly flush cached data - * - * BIG NOTE: This is not equivalent to fsync(). It's not a - * request to sync dirty data. - * - * Flush is called on each close() of a file descriptor. So if a - * filesystem wants to return write errors in close() and the file - * has cached dirty data, this is a good place to write back data - * and return any errors. Since many applications ignore close() - * errors this is not always useful. - * - * NOTE: The flush() method may be called more than once for each - * open(). This happens if more than one file descriptor refers - * to an opened file due to dup(), dup2() or fork() calls. It is - * not possible to determine if a flush is final, so each flush - * should be treated equally. Multiple write-flush sequences are - * relatively rare, so this shouldn't be a problem. - * - * Filesystems shouldn't assume that flush will always be called - * after some writes, or that if will be called at all. - * - * Changed in version 2.2 - */ - int (*flush) (const char *, struct fuse_file_info *); + /** Possibly flush cached data + * + * BIG NOTE: This is not equivalent to fsync(). It's not a + * request to sync dirty data. + * + * Flush is called on each close() of a file descriptor. So if a + * filesystem wants to return write errors in close() and the file + * has cached dirty data, this is a good place to write back data + * and return any errors. Since many applications ignore close() + * errors this is not always useful. + * + * NOTE: The flush() method may be called more than once for each + * open(). This happens if more than one file descriptor refers + * to an opened file due to dup(), dup2() or fork() calls. It is + * not possible to determine if a flush is final, so each flush + * should be treated equally. Multiple write-flush sequences are + * relatively rare, so this shouldn't be a problem. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * Changed in version 2.2 + */ + int (*flush) (const char *, struct fuse_file_info *); - /** Release an open file - * - * Release is called when there are no more references to an open - * file: all file descriptors are closed and all memory mappings - * are unmapped. - * - * For every open() call there will be exactly one release() call - * with the same flags and file descriptor. It is possible to - * have a file opened more than once, in which case only the last - * release will mean, that no more reads/writes will happen on the - * file. The return value of release is ignored. - * - * Changed in version 2.2 - */ - int (*release) (const char *, struct fuse_file_info *); + /** Release an open file + * + * Release is called when there are no more references to an open + * file: all file descriptors are closed and all memory mappings + * are unmapped. + * + * For every open() call there will be exactly one release() call + * with the same flags and file descriptor. It is possible to + * have a file opened more than once, in which case only the last + * release will mean, that no more reads/writes will happen on the + * file. The return value of release is ignored. + * + * Changed in version 2.2 + */ + int (*release) (const char *, struct fuse_file_info *); - /** Synchronize file contents - * - * If the datasync parameter is non-zero, then only the user data - * should be flushed, not the meta data. - * - * Changed in version 2.2 - */ - int (*fsync) (const char *, int, struct fuse_file_info *); + /** Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * Changed in version 2.2 + */ + int (*fsync) (const char *, int, struct fuse_file_info *); - /** Set extended attributes */ - int (*setxattr) (const char *, const char *, const char *, size_t, int); + /** Set extended attributes */ + int (*setxattr) (const char *, const char *, const char *, size_t, int); - /** Get extended attributes */ - int (*getxattr) (const char *, const char *, char *, size_t); + /** Get extended attributes */ + int (*getxattr) (const char *, const char *, char *, size_t); - /** List extended attributes */ - int (*listxattr) (const char *, char *, size_t); + /** List extended attributes */ + int (*listxattr) (const char *, char *, size_t); - /** Remove extended attributes */ - int (*removexattr) (const char *, const char *); + /** Remove extended attributes */ + int (*removexattr) (const char *, const char *); - /** Open directory - * - * This method should check if the open operation is permitted for - * this directory - * - * Introduced in version 2.3 - */ - int (*opendir) (const char *, struct fuse_file_info *); + /** Open directory + * + * This method should check if the open operation is permitted for + * this directory + * + * Introduced in version 2.3 + */ + int (*opendir) (const char *, struct fuse_file_info *); - /** Read directory - * - * The filesystem may choose between two modes of operation: - * - * 1) The readdir implementation ignores the offset parameter, and - * passes zero to the filler function's offset. The filler - * function will not return '1' (unless an error happens), so the - * whole directory is read in a single readdir operation. - * - * 2) The readdir implementation keeps track of the offsets of the - * directory entries. It uses the offset parameter and always - * passes non-zero offset to the filler function. When the buffer - * is full (or an error happens) the filler function will return - * '1'. - * - * Introduced in version 2.3 - */ - int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, - struct fuse_file_info *); + /** Read directory + * + * The filesystem may choose between two modes of operation: + * + * 1) The readdir implementation ignores the offset parameter, and + * passes zero to the filler function's offset. The filler + * function will not return '1' (unless an error happens), so the + * whole directory is read in a single readdir operation. + * + * 2) The readdir implementation keeps track of the offsets of the + * directory entries. It uses the offset parameter and always + * passes non-zero offset to the filler function. When the buffer + * is full (or an error happens) the filler function will return + * '1'. + * + * Introduced in version 2.3 + */ + int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, + struct fuse_file_info *); - /** Release directory - * - * Introduced in version 2.3 - */ - int (*releasedir) (const char *, struct fuse_file_info *); + /** Release directory + * + * Introduced in version 2.3 + */ + int (*releasedir) (const char *, struct fuse_file_info *); - /** Synchronize directory contents - * - * If the datasync parameter is non-zero, then only the user data - * should be flushed, not the meta data - * - * Introduced in version 2.3 - */ - int (*fsyncdir) (const char *, int, struct fuse_file_info *); + /** Synchronize directory contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data + * + * Introduced in version 2.3 + */ + int (*fsyncdir) (const char *, int, struct fuse_file_info *); - /** - * Initialize filesystem - * - * The return value will passed in the private_data field of - * fuse_context to all file operations and as a parameter to the - * destroy() method. - * - * Introduced in version 2.3 - * Changed in version 2.6 - */ - void *(*init) (struct fuse_conn_info *conn); + /** + * Initialize filesystem + * + * The return value will passed in the private_data field of + * fuse_context to all file operations and as a parameter to the + * destroy() method. + * + * Introduced in version 2.3 + * Changed in version 2.6 + */ + void *(*init) (struct fuse_conn_info *conn); - /** - * Clean up filesystem - * - * Called on filesystem exit. - * - * Introduced in version 2.3 - */ - void (*destroy) (void *); + /** + * Clean up filesystem + * + * Called on filesystem exit. + * + * Introduced in version 2.3 + */ + void (*destroy) (void *); - /** - * Check file access permissions - * - * This will be called for the access() system call. If the - * 'default_permissions' mount option is given, this method is not - * called. - * - * This method is not called under Linux kernel versions 2.4.x - * - * Introduced in version 2.5 - */ - int (*access) (const char *, int); + /** + * Check file access permissions + * + * This will be called for the access() system call. If the + * 'default_permissions' mount option is given, this method is not + * called. + * + * This method is not called under Linux kernel versions 2.4.x + * + * Introduced in version 2.5 + */ + int (*access) (const char *, int); - /** - * Create and open a file - * - * If the file does not exist, first create it with the specified - * mode, and then open it. - * - * If this method is not implemented or under Linux kernel - * versions earlier than 2.6.15, the mknod() and open() methods - * will be called instead. - * - * Introduced in version 2.5 - */ - int (*create) (const char *, mode_t, struct fuse_file_info *); + /** + * Create and open a file + * + * If the file does not exist, first create it with the specified + * mode, and then open it. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the mknod() and open() methods + * will be called instead. + * + * Introduced in version 2.5 + */ + int (*create) (const char *, mode_t, struct fuse_file_info *); - /** - * Change the size of an open file - * - * This method is called instead of the truncate() method if the - * truncation was invoked from an ftruncate() system call. - * - * If this method is not implemented or under Linux kernel - * versions earlier than 2.6.15, the truncate() method will be - * called instead. - * - * Introduced in version 2.5 - */ - int (*ftruncate) (const char *, off_t, struct fuse_file_info *); + /** + * Change the size of an open file + * + * This method is called instead of the truncate() method if the + * truncation was invoked from an ftruncate() system call. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the truncate() method will be + * called instead. + * + * Introduced in version 2.5 + */ + int (*ftruncate) (const char *, off_t, struct fuse_file_info *); - /** - * Get attributes from an open file - * - * This method is called instead of the getattr() method if the - * file information is available. - * - * Currently this is only called after the create() method if that - * is implemented (see above). Later it may be called for - * invocations of fstat() too. - * - * Introduced in version 2.5 - */ - int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *); + /** + * Get attributes from an open file + * + * This method is called instead of the getattr() method if the + * file information is available. + * + * Currently this is only called after the create() method if that + * is implemented (see above). Later it may be called for + * invocations of fstat() too. + * + * Introduced in version 2.5 + */ + int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *); - /** - * Perform POSIX file locking operation - * - * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. - * - * For the meaning of fields in 'struct flock' see the man page - * for fcntl(2). The l_whence field will always be set to - * SEEK_SET. - * - * For checking lock ownership, the 'fuse_file_info->owner' - * argument must be used. - * - * For F_GETLK operation, the library will first check currently - * held locks, and if a conflicting lock is found it will return - * information without calling this method. This ensures, that - * for local locks the l_pid field is correctly filled in. The - * results may not be accurate in case of race conditions and in - * the presence of hard links, but it's unlikly that an - * application would rely on accurate GETLK results in these - * cases. If a conflicting lock is not found, this method will be - * called, and the filesystem may fill out l_pid by a meaningful - * value, or it may leave this field zero. - * - * For F_SETLK and F_SETLKW the l_pid field will be set to the pid - * of the process performing the locking operation. - * - * Note: if this method is not implemented, the kernel will still - * allow file locking to work locally. Hence it is only - * interesting for network filesystems and similar. - * - * Introduced in version 2.6 - */ - int (*lock) (const char *, struct fuse_file_info *, int cmd, - struct flock *); + /** + * Perform POSIX file locking operation + * + * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. + * + * For the meaning of fields in 'struct flock' see the man page + * for fcntl(2). The l_whence field will always be set to + * SEEK_SET. + * + * For checking lock ownership, the 'fuse_file_info->owner' + * argument must be used. + * + * For F_GETLK operation, the library will first check currently + * held locks, and if a conflicting lock is found it will return + * information without calling this method. This ensures, that + * for local locks the l_pid field is correctly filled in. The + * results may not be accurate in case of race conditions and in + * the presence of hard links, but it's unlikly that an + * application would rely on accurate GETLK results in these + * cases. If a conflicting lock is not found, this method will be + * called, and the filesystem may fill out l_pid by a meaningful + * value, or it may leave this field zero. + * + * For F_SETLK and F_SETLKW the l_pid field will be set to the pid + * of the process performing the locking operation. + * + * Note: if this method is not implemented, the kernel will still + * allow file locking to work locally. Hence it is only + * interesting for network filesystems and similar. + * + * Introduced in version 2.6 + */ + int (*lock) (const char *, struct fuse_file_info *, int cmd, + struct flock *); - /** - * Change the access and modification times of a file with - * nanosecond resolution - * - * Introduced in version 2.6 - */ - int (*utimens) (const char *, const struct timespec tv[2]); + /** + * Change the access and modification times of a file with + * nanosecond resolution + * + * Introduced in version 2.6 + */ + int (*utimens) (const char *, const struct timespec tv[2]); - /** - * Map block index within file to block index within device - * - * Note: This makes sense only for block device backed filesystems - * mounted with the 'blkdev' option - * - * Introduced in version 2.6 - */ - int (*bmap) (const char *, size_t blocksize, uint64_t *idx); + /** + * Map block index within file to block index within device + * + * Note: This makes sense only for block device backed filesystems + * mounted with the 'blkdev' option + * + * Introduced in version 2.6 + */ + int (*bmap) (const char *, size_t blocksize, uint64_t *idx); }; /** Extra context that may be needed by some filesystems @@ -421,24 +422,24 @@ struct fuse_operations { * operation. */ struct fuse_context { - /** Pointer to the fuse object */ - struct fuse *fuse; + /** Pointer to the fuse object */ + struct fuse *fuse; - /** User ID of the calling process */ - uid_t uid; + /** User ID of the calling process */ + uid_t uid; - /** Group ID of the calling process */ - gid_t gid; + /** Group ID of the calling process */ + gid_t gid; - /** Thread ID of the calling process */ - pid_t pid; + /** Thread ID of the calling process */ + pid_t pid; - /** Private filesystem data */ - void *private_data; + /** Private filesystem data */ + void *private_data; }; /* ----------------------------------------------------------- * - * More detailed API * + * More detailed API * * ----------------------------------------------------------- */ /** @@ -452,15 +453,15 @@ struct fuse_context { * @return the created FUSE handle */ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, - const struct fuse_operations *op, size_t op_size, - void *user_data); + const struct fuse_operations *op, size_t op_size, + void *user_data); /** * Destroy the FUSE handle. * * The communication channel attached to the handle is also destroyed. * - * NOTE: This function does not unmount the filesystem. If this is + * NOTE: This function does not unmount the filesystem. If this is * needed, call fuse_unmount() before calling this function. * * @param f the FUSE handle @@ -525,63 +526,63 @@ struct fuse_fs; int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf); int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, - const char *newpath); + const char *newpath); int fuse_fs_unlink(struct fuse_fs *fs, const char *path); int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, - const char *path); + const char *path); int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); -int fuse_fs_release(struct fuse_fs *fs, const char *path, - struct fuse_file_info *fi); +int fuse_fs_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); int fuse_fs_open(struct fuse_fs *fs, const char *path, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, - off_t off, struct fuse_file_info *fi); + off_t off, struct fuse_file_info *fi); int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, - size_t size, off_t off, struct fuse_file_info *fi); + size_t size, off_t off, struct fuse_file_info *fi); int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_flush(struct fuse_fs *fs, const char *path, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); int fuse_fs_opendir(struct fuse_fs *fs, const char *path, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, - fuse_fill_dir_t filler, off_t off, - struct fuse_file_info *fi); + fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi); int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_lock(struct fuse_fs *fs, const char *path, - struct fuse_file_info *fi, int cmd, struct flock *lock); + struct fuse_file_info *fi, int cmd, struct flock *lock); int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode); int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid); int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size); int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, - struct fuse_file_info *fi); + struct fuse_file_info *fi); int fuse_fs_utimens(struct fuse_fs *fs, const char *path, - const struct timespec tv[2]); + const struct timespec tv[2]); int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, - size_t len); + size_t len); int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, - dev_t rdev); + dev_t rdev); int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, - const char *value, size_t size, int flags); + const char *value, size_t size, int flags); int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, - char *value, size_t size); + char *value, size_t size); int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, - size_t size); + size_t size); int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, - const char *name); + const char *name); int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, - uint64_t *idx); + uint64_t *idx); void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); void fuse_fs_destroy(struct fuse_fs *fs); @@ -597,7 +598,7 @@ void fuse_fs_destroy(struct fuse_fs *fs); * @return a new filesystem object */ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, - void *user_data); + void *user_data); /* ----------------------------------------------------------- * * Advanced API for event handling, don't worry about this... * diff --git a/include/fuse-lite/fuse_common.h b/include/fuse-lite/fuse_common.h index 20868cf8..6ddcf99e 100644 --- a/include/fuse-lite/fuse_common.h +++ b/include/fuse-lite/fuse_common.h @@ -1,15 +1,15 @@ /* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. */ /** @file */ #if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_) -#error "Never include directly; use or directly; use or instead." #endif #ifndef _FUSE_COMMON_H_ @@ -37,38 +37,38 @@ extern "C" { * Changed in version 2.5 */ struct fuse_file_info { - /** Open flags. Available in open() and release() */ - int flags; + /** Open flags. Available in open() and release() */ + int flags; - /** Old file handle, don't use */ - unsigned long fh_old; + /** Old file handle, don't use */ + unsigned long fh_old; - /** In case of a write operation indicates if this was caused by a - writepage */ - int writepage; + /** In case of a write operation indicates if this was caused by a + writepage */ + int writepage; - /** Can be filled in by open, to use direct I/O on this file. - Introduced in version 2.4 */ - unsigned int direct_io : 1; + /** Can be filled in by open, to use direct I/O on this file. + Introduced in version 2.4 */ + unsigned int direct_io : 1; - /** Can be filled in by open, to indicate, that cached file data - need not be invalidated. Introduced in version 2.4 */ - unsigned int keep_cache : 1; + /** Can be filled in by open, to indicate, that cached file data + need not be invalidated. Introduced in version 2.4 */ + unsigned int keep_cache : 1; - /** Indicates a flush operation. Set in flush operation, also - maybe set in highlevel lock operation and lowlevel release - operation. Introduced in version 2.6 */ - unsigned int flush : 1; + /** Indicates a flush operation. Set in flush operation, also + maybe set in highlevel lock operation and lowlevel release + operation. Introduced in version 2.6 */ + unsigned int flush : 1; - /** Padding. Do not use*/ - unsigned int padding : 29; + /** Padding. Do not use*/ + unsigned int padding : 29; - /** File handle. May be filled in by filesystem in open(). - Available in all other file operations */ - uint64_t fh; + /** File handle. May be filled in by filesystem in open(). + Available in all other file operations */ + uint64_t fh; - /** Lock owner id. Available in locking operations and flush */ - uint64_t lock_owner; + /** Lock owner id. Available in locking operations and flush */ + uint64_t lock_owner; }; /** @@ -79,36 +79,36 @@ struct fuse_file_info { * value must usually be smaller than the indicated value. */ struct fuse_conn_info { - /** - * Major version of the protocol (read-only) - */ - unsigned proto_major; + /** + * Major version of the protocol (read-only) + */ + unsigned proto_major; - /** - * Minor version of the protocol (read-only) - */ - unsigned proto_minor; + /** + * Minor version of the protocol (read-only) + */ + unsigned proto_minor; - /** - * Is asynchronous read supported (read-write) - */ - unsigned async_read; + /** + * Is asynchronous read supported (read-write) + */ + unsigned async_read; - /** - * Maximum size of the write buffer - */ - unsigned max_write; + /** + * Maximum size of the write buffer + */ + unsigned max_write; - /** - * Maximum readahead - */ - unsigned max_readahead; + /** + * Maximum readahead + */ + unsigned max_readahead; - /** - * For future use. - */ - unsigned reserved[27]; -}; + /** + * For future use. + */ + unsigned reserved[27]; + }; struct fuse_session; struct fuse_chan; @@ -141,13 +141,13 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch); int fuse_version(void); /* ----------------------------------------------------------- * - * Signal handling * + * Signal handling * * ----------------------------------------------------------- */ /** * Exit session on HUP, TERM and INT signals and ignore PIPE signal * - * Stores session in a global variable. May only be called once per + * Stores session in a global variable. May only be called once per * process until fuse_remove_signal_handlers() is called. * * @param se the session to exit diff --git a/include/fuse-lite/fuse_lowlevel.h b/include/fuse-lite/fuse_lowlevel.h index 662aca0d..64d8bf71 100644 --- a/include/fuse-lite/fuse_lowlevel.h +++ b/include/fuse-lite/fuse_lowlevel.h @@ -1,9 +1,9 @@ /* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. */ #ifndef _FUSE_LOWLEVEL_H_ @@ -28,7 +28,7 @@ extern "C" { #endif /* ----------------------------------------------------------- * - * Miscellaneous definitions * + * Miscellaneous definitions * * ----------------------------------------------------------- */ /** The node ID of the root inode */ @@ -57,43 +57,49 @@ struct fuse_chan; /** Directory entry parameters supplied to fuse_reply_entry() */ struct fuse_entry_param { - /** Unique inode number - * - * In lookup, zero means negative entry (from version 2.5) - * Returning ENOENT also means negative entry, but by setting zero - * ino the kernel may cache negative entries for entry_timeout - * seconds. - */ - fuse_ino_t ino; + /** Unique inode number + * + * In lookup, zero means negative entry (from version 2.5) + * Returning ENOENT also means negative entry, but by setting zero + * ino the kernel may cache negative entries for entry_timeout + * seconds. + */ + fuse_ino_t ino; - /** Generation number for this entry. The ino/generation pair - should be unique for the filesystem's lifetime. It must be - non-zero, otherwise FUSE will treat it as an error. */ - unsigned long generation; + /** Generation number for this entry. + * + * The ino/generation pair should be unique for the filesystem's + * lifetime. It must be non-zero, otherwise FUSE will treat it as an + * error. + */ + unsigned long generation; - /** Inode attributes. Even if attr_timeout == 0, attr must be - correct. For example, for open(), FUSE uses attr.st_size from - lookup() to determine how many bytes to request. If this - value is not correct, incorrect data will be returned. */ - struct stat attr; + /** Inode attributes. + * + * Even if attr_timeout == 0, attr must be correct. For example, + * for open(), FUSE uses attr.st_size from lookup() to determine + * how many bytes to request. If this value is not correct, + * incorrect data will be returned. + */ + struct stat attr; - /** Validity timeout (in seconds) for the attributes */ - double attr_timeout; + /** Validity timeout (in seconds) for the attributes */ + double attr_timeout; - /** Validity timeout (in seconds) for the name */ - double entry_timeout; + /** Validity timeout (in seconds) for the name */ + double entry_timeout; }; /** Additional context associated with requests */ struct fuse_ctx { - /** User ID of the calling process */ - uid_t uid; + /** User ID of the calling process */ + uid_t uid; - /** Group ID of the calling process */ - gid_t gid; + /** Group ID of the calling process */ + gid_t gid; - /** Thread ID of the calling process */ - pid_t pid; + /** Thread ID of the calling process */ + pid_t pid; }; /* 'to_set' flags in setattr */ @@ -105,7 +111,7 @@ struct fuse_ctx { #define FUSE_SET_ATTR_MTIME (1 << 5) /* ----------------------------------------------------------- * - * Request methods and replies * + * Request methods and replies * * ----------------------------------------------------------- */ /** @@ -130,662 +136,668 @@ struct fuse_ctx { * this file will not be called. */ struct fuse_lowlevel_ops { - /** - * Initialize filesystem - * - * Called before any other filesystem method - * - * There's no reply to this function - * - * @param userdata the user data passed to fuse_lowlevel_new() - */ - void (*init) (void *userdata, struct fuse_conn_info *conn); + /** + * Initialize filesystem + * + * Called before any other filesystem method + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_lowlevel_new() + */ + void (*init) (void *userdata, struct fuse_conn_info *conn); - /** - * Clean up filesystem - * - * Called on filesystem exit - * - * There's no reply to this function - * - * @param userdata the user data passed to fuse_lowlevel_new() - */ - void (*destroy) (void *userdata); + /** + * Clean up filesystem + * + * Called on filesystem exit + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_lowlevel_new() + */ + void (*destroy) (void *userdata); - /** - * Look up a directory entry by name and get its attributes. - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name the name to look up - */ - void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); + /** + * Look up a directory entry by name and get its attributes. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name the name to look up + */ + void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); - /** - * Forget about an inode - * - * The nlookup parameter indicates the number of lookups - * previously performed on this inode. - * - * If the filesystem implements inode lifetimes, it is recommended - * that inodes acquire a single reference on each lookup, and lose - * nlookup references on each forget. - * - * The filesystem may ignore forget calls, if the inodes don't - * need to have a limited lifetime. - * - * On unmount it is not guaranteed, that all referenced inodes - * will receive a forget message. - * - * Valid replies: - * fuse_reply_none - * - * @param req request handle - * @param ino the inode number - * @param nlookup the number of lookups to forget - */ - void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); + /** + * Forget about an inode + * + * The nlookup parameter indicates the number of lookups + * previously performed on this inode. + * + * If the filesystem implements inode lifetimes, it is recommended + * that inodes acquire a single reference on each lookup, and lose + * nlookup references on each forget. + * + * The filesystem may ignore forget calls, if the inodes don't + * need to have a limited lifetime. + * + * On unmount it is not guaranteed, that all referenced inodes + * will receive a forget message. + * + * Valid replies: + * fuse_reply_none + * + * @param req request handle + * @param ino the inode number + * @param nlookup the number of lookups to forget + */ + void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); - /** - * Get file attributes - * - * Valid replies: - * fuse_reply_attr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi for future use, currently always NULL - */ - void (*getattr) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + /** + * Get file attributes + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi for future use, currently always NULL + */ + void (*getattr) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); - /** - * Set file attributes - * - * In the 'attr' argument only members indicated by the 'to_set' - * bitmask contain valid values. Other members contain undefined - * values. - * - * If the setattr was invoked from the ftruncate() system call - * under Linux kernel versions 2.6.15 or later, the fi->fh will - * contain the value set by the open method or will be undefined - * if the open method didn't set any value. Otherwise (not - * ftruncate call, or kernel version earlier than 2.6.15) the fi - * parameter will be NULL. - * - * Valid replies: - * fuse_reply_attr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param attr the attributes - * @param to_set bit mask of attributes which should be set - * @param fi file information, or NULL - * - * Changed in version 2.5: - * file information filled in for ftruncate - */ - void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, - int to_set, struct fuse_file_info *fi); + /** + * Set file attributes + * + * In the 'attr' argument only members indicated by the 'to_set' + * bitmask contain valid values. Other members contain undefined + * values. + * + * If the setattr was invoked from the ftruncate() system call + * under Linux kernel versions 2.6.15 or later, the fi->fh will + * contain the value set by the open method or will be undefined + * if the open method didn't set any value. Otherwise (not + * ftruncate call, or kernel version earlier than 2.6.15) the fi + * parameter will be NULL. + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param attr the attributes + * @param to_set bit mask of attributes which should be set + * @param fi file information, or NULL + * + * Changed in version 2.5: + * file information filled in for ftruncate + */ + void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi); - /** - * Read symbolic link - * - * Valid replies: - * fuse_reply_readlink - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - */ - void (*readlink) (fuse_req_t req, fuse_ino_t ino); + /** + * Read symbolic link + * + * Valid replies: + * fuse_reply_readlink + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + */ + void (*readlink) (fuse_req_t req, fuse_ino_t ino); - /** - * Create file node - * - * Create a regular file, character device, block device, fifo or - * socket node. - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to create - * @param mode file type and mode with which to create the new file - * @param rdev the device number (only valid if created file is a device) - */ - void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, - mode_t mode, dev_t rdev); + /** + * Create file node + * + * Create a regular file, character device, block device, fifo or + * socket node. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param rdev the device number (only valid if created file is a device) + */ + void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev); - /** - * Create a directory - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to create - * @param mode with which to create the new file - */ - void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, - mode_t mode); + /** + * Create a directory + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode with which to create the new file + */ + void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode); - /** - * Remove a file - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to remove - */ - void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); + /** + * Remove a file + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); - /** - * Remove a directory - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to remove - */ - void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); + /** + * Remove a directory + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); - /** - * Create a symbolic link - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param link the contents of the symbolic link - * @param parent inode number of the parent directory - * @param name to create - */ - void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, - const char *name); + /** + * Create a symbolic link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param link the contents of the symbolic link + * @param parent inode number of the parent directory + * @param name to create + */ + void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, + const char *name); - /** Rename a file - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the old parent directory - * @param name old name - * @param newparent inode number of the new parent directory - * @param newname new name - */ - void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, - fuse_ino_t newparent, const char *newname); + /** Rename a file + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the old parent directory + * @param name old name + * @param newparent inode number of the new parent directory + * @param newname new name + */ + void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname); - /** - * Create a hard link - * - * Valid replies: - * fuse_reply_entry - * fuse_reply_err - * - * @param req request handle - * @param ino the old inode number - * @param newparent inode number of the new parent directory - * @param newname new name to create - */ - void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, - const char *newname); + /** + * Create a hard link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param ino the old inode number + * @param newparent inode number of the new parent directory + * @param newname new name to create + */ + void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname); - /** - * Open a file - * - * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and - * O_TRUNC) are available in fi->flags. - * - * Filesystem may store an arbitrary file handle (pointer, index, - * etc) in fi->fh, and use this in other all other file operations - * (read, write, flush, release, fsync). - * - * Filesystem may also implement stateless file I/O and not store - * anything in fi->fh. - * - * There are also some flags (direct_io, keep_cache) which the - * filesystem may set in fi, to change the way the file is opened. - * See fuse_file_info structure in for more details. - * - * Valid replies: - * fuse_reply_open - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ - void (*open) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + /** + * Open a file + * + * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and + * O_TRUNC) are available in fi->flags. + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other file operations + * (read, write, flush, release, fsync). + * + * Filesystem may also implement stateless file I/O and not store + * anything in fi->fh. + * + * There are also some flags (direct_io, keep_cache) which the + * filesystem may set in fi, to change the way the file is opened. + * See fuse_file_info structure in for more details. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*open) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); - /** - * Read data - * - * Read should send exactly the number of bytes requested except - * on EOF or error, otherwise the rest of the data will be - * substituted with zeroes. An exception to this is when the file - * has been opened in 'direct_io' mode, in which case the return - * value of the read system call will reflect the return value of - * this operation. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size number of bytes to read - * @param off offset to read from - * @param fi file information - */ - void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info *fi); + /** + * Read data + * + * Read should send exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the file + * has been opened in 'direct_io' mode, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size number of bytes to read + * @param off offset to read from + * @param fi file information + */ + void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); - /** - * Write data - * - * Write should return exactly the number of bytes requested - * except on error. An exception to this is when the file has - * been opened in 'direct_io' mode, in which case the return value - * of the write system call will reflect the return value of this - * operation. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * Valid replies: - * fuse_reply_write - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param buf data to write - * @param size number of bytes to write - * @param off offset to write to - * @param fi file information - */ - void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, - size_t size, off_t off, struct fuse_file_info *fi); + /** + * Write data + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the file has + * been opened in 'direct_io' mode, in which case the return value + * of the write system call will reflect the return value of this + * operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_write + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param buf data to write + * @param size number of bytes to write + * @param off offset to write to + * @param fi file information + */ + void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi); - /** - * Flush method - * - * This is called on each close() of the opened file. - * - * Since file descriptors can be duplicated (dup, dup2, fork), for - * one open call there may be many flush calls. - * - * Filesystems shouldn't assume that flush will always be called - * after some writes, or that if will be called at all. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * - * NOTE: the name of the method is misleading, since (unlike - * fsync) the filesystem is not forced to flush pending writes. - * One reason to flush data, is if the filesystem wants to return - * write errors. - * - * If the filesystem supports file locking operations (setlk, - * getlk) it should remove all locks belonging to 'fi->owner'. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ - void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + /** + * Flush method + * + * This is called on each close() of the opened file. + * + * Since file descriptors can be duplicated (dup, dup2, fork), for + * one open call there may be many flush calls. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * NOTE: the name of the method is misleading, since (unlike + * fsync) the filesystem is not forced to flush pending writes. + * One reason to flush data, is if the filesystem wants to return + * write errors. + * + * If the filesystem supports file locking operations (setlk, + * getlk) it should remove all locks belonging to 'fi->owner'. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*flush) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); - /** - * Release an open file - * - * Release is called when there are no more references to an open - * file: all file descriptors are closed and all memory mappings - * are unmapped. - * - * For every open call there will be exactly one release call. - * - * The filesystem may reply with an error, but error values are - * not returned to close() or munmap() which triggered the - * release. - * - * fi->fh will contain the value set by the open method, or will - * be undefined if the open method didn't set any value. - * fi->flags will contain the same flags as for open. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ - void (*release) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + /** + * Release an open file + * + * Release is called when there are no more references to an open + * file: all file descriptors are closed and all memory mappings + * are unmapped. + * + * For every open call there will be exactly one release call. + * + * The filesystem may reply with an error, but error values are + * not returned to close() or munmap() which triggered the + * release. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * fi->flags will contain the same flags as for open. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*release) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); - /** - * Synchronize file contents - * - * If the datasync parameter is non-zero, then only the user data - * should be flushed, not the meta data. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param datasync flag indicating if only data should be flushed - * @param fi file information - */ - void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info *fi); + /** + * Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); - /** - * Open a directory - * - * Filesystem may store an arbitrary file handle (pointer, index, - * etc) in fi->fh, and use this in other all other directory - * stream operations (readdir, releasedir, fsyncdir). - * - * Filesystem may also implement stateless directory I/O and not - * store anything in fi->fh, though that makes it impossible to - * implement standard conforming directory stream operations in - * case the contents of the directory can change between opendir - * and releasedir. - * - * Valid replies: - * fuse_reply_open - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ - void (*opendir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + /** + * Open a directory + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other directory + * stream operations (readdir, releasedir, fsyncdir). + * + * Filesystem may also implement stateless directory I/O and not + * store anything in fi->fh, though that makes it impossible to + * implement standard conforming directory stream operations in + * case the contents of the directory can change between opendir + * and releasedir. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*opendir) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); - /** - * Read directory - * - * Send a buffer filled using fuse_add_direntry(), with size not - * exceeding the requested size. Send an empty buffer on end of - * stream. - * - * fi->fh will contain the value set by the opendir method, or - * will be undefined if the opendir method didn't set any value. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size maximum number of bytes to send - * @param off offset to continue reading the directory stream - * @param fi file information - */ - void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, - struct fuse_file_info *fi); + /** + * Read directory + * + * Send a buffer filled using fuse_add_direntry(), with size not + * exceeding the requested size. Send an empty buffer on end of + * stream. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum number of bytes to send + * @param off offset to continue reading the directory stream + * @param fi file information + */ + void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); - /** - * Release an open directory - * - * For every opendir call there will be exactly one releasedir - * call. - * - * fi->fh will contain the value set by the opendir method, or - * will be undefined if the opendir method didn't set any value. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - */ - void (*releasedir) (fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi); + /** + * Release an open directory + * + * For every opendir call there will be exactly one releasedir + * call. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*releasedir) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); - /** - * Synchronize directory contents - * - * If the datasync parameter is non-zero, then only the directory - * contents should be flushed, not the meta data. - * - * fi->fh will contain the value set by the opendir method, or - * will be undefined if the opendir method didn't set any value. - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param datasync flag indicating if only data should be flushed - * @param fi file information - */ - void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info *fi); + /** + * Synchronize directory contents + * + * If the datasync parameter is non-zero, then only the directory + * contents should be flushed, not the meta data. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); - /** - * Get file system statistics - * - * Valid replies: - * fuse_reply_statfs - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number, zero means "undefined" - */ - void (*statfs) (fuse_req_t req, fuse_ino_t ino); + /** + * Get file system statistics + * + * Valid replies: + * fuse_reply_statfs + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number, zero means "undefined" + */ + void (*statfs) (fuse_req_t req, fuse_ino_t ino); - /** - * Set an extended attribute - * - * Valid replies: - * fuse_reply_err - */ - void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, - const char *value, size_t size, int flags); + /** + * Set an extended attribute + * + * Valid replies: + * fuse_reply_err + */ + void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags); - /** - * Get an extended attribute - * - * If size is zero, the size of the value should be sent with - * fuse_reply_xattr. - * - * If the size is non-zero, and the value fits in the buffer, the - * value should be sent with fuse_reply_buf. - * - * If the size is too small for the value, the ERANGE error should - * be sent. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_xattr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param name of the extended attribute - * @param size maximum size of the value to send - */ - void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, - size_t size); + /** + * Get an extended attribute + * + * If size is zero, the size of the value should be sent with + * fuse_reply_xattr. + * + * If the size is non-zero, and the value fits in the buffer, the + * value should be sent with fuse_reply_buf. + * + * If the size is too small for the value, the ERANGE error should + * be sent. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + * @param size maximum size of the value to send + */ + void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size); - /** - * List extended attribute names - * - * If size is zero, the total size of the attribute list should be - * sent with fuse_reply_xattr. - * - * If the size is non-zero, and the null character separated - * attribute list fits in the buffer, the list should be sent with - * fuse_reply_buf. - * - * If the size is too small for the list, the ERANGE error should - * be sent. - * - * Valid replies: - * fuse_reply_buf - * fuse_reply_xattr - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param size maximum size of the list to send - */ - void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); + /** + * List extended attribute names + * + * If size is zero, the total size of the attribute list should be + * sent with fuse_reply_xattr. + * + * If the size is non-zero, and the null character separated + * attribute list fits in the buffer, the list should be sent with + * fuse_reply_buf. + * + * If the size is too small for the list, the ERANGE error should + * be sent. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum size of the list to send + */ + void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); - /** - * Remove an extended attribute - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param name of the extended attribute - */ - void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); + /** + * Remove an extended attribute + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + */ + void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); - /** - * Check file access permissions - * - * This will be called for the access() system call. If the - * 'default_permissions' mount option is given, this method is not - * called. - * - * This method is not called under Linux kernel versions 2.4.x - * - * Introduced in version 2.5 - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param mask requested access mode - */ - void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); + /** + * Check file access permissions + * + * This will be called for the access() system call. If the + * 'default_permissions' mount option is given, this method is not + * called. + * + * This method is not called under Linux kernel versions 2.4.x + * + * Introduced in version 2.5 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param mask requested access mode + */ + void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); - /** - * Create and open a file - * - * If the file does not exist, first create it with the specified - * mode, and then open it. - * - * Open flags (with the exception of O_NOCTTY) are available in - * fi->flags. - * - * Filesystem may store an arbitrary file handle (pointer, index, - * etc) in fi->fh, and use this in other all other file operations - * (read, write, flush, release, fsync). - * - * There are also some flags (direct_io, keep_cache) which the - * filesystem may set in fi, to change the way the file is opened. - * See fuse_file_info structure in for more details. - * - * If this method is not implemented or under Linux kernel - * versions earlier than 2.6.15, the mknod() and open() methods - * will be called instead. - * - * Introduced in version 2.5 - * - * Valid replies: - * fuse_reply_create - * fuse_reply_err - * - * @param req request handle - * @param parent inode number of the parent directory - * @param name to create - * @param mode file type and mode with which to create the new file - * @param fi file information - */ - void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, - mode_t mode, struct fuse_file_info *fi); + /** + * Create and open a file + * + * If the file does not exist, first create it with the specified + * mode, and then open it. + * + * Open flags (with the exception of O_NOCTTY) are available in + * fi->flags. + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other file operations + * (read, write, flush, release, fsync). + * + * There are also some flags (direct_io, keep_cache) which the + * filesystem may set in fi, to change the way the file is opened. + * See fuse_file_info structure in for more details. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the mknod() and open() methods + * will be called instead. + * + * Introduced in version 2.5 + * + * Valid replies: + * fuse_reply_create + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param fi file information + */ + void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi); - /** - * Test for a POSIX file lock - * - * Introduced in version 2.6 - * - * Valid replies: - * fuse_reply_lock - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * @param lock the region/type to test - */ - void (*getlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, - struct flock *lock); + /** + * Test for a POSIX file lock + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_lock + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + */ + void (*getlk) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock); - /** - * Acquire, modify or release a POSIX file lock - * - * For POSIX threads (NPTL) there's a 1-1 relation between pid and - * owner, but otherwise this is not always the case. For checking - * lock ownership, 'fi->owner' must be used. The l_pid field in - * 'struct flock' should only be used to fill in this field in - * getlk(). - * - * Note: if the locking methods are not implemented, the kernel - * will still allow file locking to work locally. Hence these are - * only interesting for network filesystems and similar. - * - * Introduced in version 2.6 - * - * Valid replies: - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param fi file information - * @param lock the region/type to test - * @param sleep locking operation may sleep - */ - void (*setlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, - struct flock *lock, int sleep); + /** + * Acquire, modify or release a POSIX file lock + * + * For POSIX threads (NPTL) there's a 1-1 relation between pid and + * owner, but otherwise this is not always the case. For checking + * lock ownership, 'fi->owner' must be used. The l_pid field in + * 'struct flock' should only be used to fill in this field in + * getlk(). + * + * Note: if the locking methods are not implemented, the kernel + * will still allow file locking to work locally. Hence these are + * only interesting for network filesystems and similar. + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + * @param sleep locking operation may sleep + */ + void (*setlk) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, + struct flock *lock, int sleep); - /** - * Map block index within file to block index within device - * - * Note: This makes sense only for block device backed filesystems - * mounted with the 'blkdev' option - * - * Introduced in version 2.6 - * - * Valid replies: - * fuse_reply_bmap - * fuse_reply_err - * - * @param req request handle - * @param ino the inode number - * @param blocksize unit of block index - * @param idx block index within file - */ - void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, - uint64_t idx); + /** + * Map block index within file to block index within device + * + * Note: This makes sense only for block device backed filesystems + * mounted with the 'blkdev' option + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_bmap + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param blocksize unit of block index + * @param idx block index within file + */ + void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t idx); }; /** @@ -840,7 +852,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); * @return zero for success, -errno for failure to send reply */ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, - const struct fuse_file_info *fi); + const struct fuse_file_info *fi); /** * Reply with attributes @@ -850,11 +862,11 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, * * @param req request handle * @param the attributes - * @param attr_timeout validity timeout (in seconds) for the attributes + * @param attr_timeout validity timeout (in seconds) for the attributes * @return zero for success, -errno for failure to send reply */ int fuse_reply_attr(fuse_req_t req, const struct stat *attr, - double attr_timeout); + double attr_timeout); /** * Reply with the contents of a symbolic link @@ -964,13 +976,13 @@ int fuse_reply_lock(fuse_req_t req, struct flock *lock); * bmap * * @param req request handle - * @param idx block index within device + * @param idx block index within device * @return zero for success, -errno for failure to send reply */ int fuse_reply_bmap(fuse_req_t req, uint64_t idx); /* ----------------------------------------------------------- * - * Filling a buffer in readdir * + * Filling a buffer in readdir * * ----------------------------------------------------------- */ /** @@ -998,11 +1010,11 @@ int fuse_reply_bmap(fuse_req_t req, uint64_t idx); * @return the space needed for the entry */ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, - const char *name, const struct stat *stbuf, - off_t off); + const char *name, const struct stat *stbuf, + off_t off); /* ----------------------------------------------------------- * - * Utility functions * + * Utility functions * * ----------------------------------------------------------- */ /** @@ -1044,7 +1056,7 @@ typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); * @parm data user data passed to the callback function */ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, - void *data); + void *data); /** * Check if a request has already been interrupted @@ -1055,7 +1067,7 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, int fuse_req_interrupted(fuse_req_t req); /* ----------------------------------------------------------- * - * Filesystem setup * + * Filesystem setup * * ----------------------------------------------------------- */ /** @@ -1068,11 +1080,11 @@ int fuse_req_interrupted(fuse_req_t req); * @return the created session object, or NULL on failure */ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, - const struct fuse_lowlevel_ops *op, - size_t op_size, void *userdata); + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata); /* ----------------------------------------------------------- * - * Session interface * + * Session interface * * ----------------------------------------------------------- */ /** @@ -1081,39 +1093,39 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, * This is used in session creation */ struct fuse_session_ops { - /** - * Hook to process a request (mandatory) - * - * @param data user data passed to fuse_session_new() - * @param buf buffer containing the raw request - * @param len request length - * @param ch channel on which the request was received - */ - void (*process) (void *data, const char *buf, size_t len, - struct fuse_chan *ch); + /** + * Hook to process a request (mandatory) + * + * @param data user data passed to fuse_session_new() + * @param buf buffer containing the raw request + * @param len request length + * @param ch channel on which the request was received + */ + void (*process) (void *data, const char *buf, size_t len, + struct fuse_chan *ch); - /** - * Hook for session exit and reset (optional) - * - * @param data user data passed to fuse_session_new() - * @param val exited status (1 - exited, 0 - not exited) - */ - void (*exit) (void *data, int val); + /** + * Hook for session exit and reset (optional) + * + * @param data user data passed to fuse_session_new() + * @param val exited status (1 - exited, 0 - not exited) + */ + void (*exit) (void *data, int val); - /** - * Hook for querying the current exited status (optional) - * - * @param data user data passed to fuse_session_new() - * @return 1 if exited, 0 if not exited - */ - int (*exited) (void *data); + /** + * Hook for querying the current exited status (optional) + * + * @param data user data passed to fuse_session_new() + * @return 1 if exited, 0 if not exited + */ + int (*exited) (void *data); - /** - * Hook for cleaning up the channel on destroy (optional) - * - * @param data user data passed to fuse_session_new() - */ - void (*destroy) (void *data); + /** + * Hook for cleaning up the channel on destroy (optional) + * + * @param data user data passed to fuse_session_new() + */ + void (*destroy) (void *data); }; /** @@ -1159,7 +1171,7 @@ void fuse_session_remove_chan(struct fuse_chan *ch); * @return the next channel, or NULL if no more channels exist */ struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, - struct fuse_chan *ch); + struct fuse_chan *ch); /** * Process a raw request @@ -1170,7 +1182,7 @@ struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, * @param ch channel on which the request was received */ void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, - struct fuse_chan *ch); + struct fuse_chan *ch); /** * Destroy a session @@ -1218,7 +1230,7 @@ int fuse_session_loop(struct fuse_session *se); int fuse_session_loop_mt(struct fuse_session *se); /* ----------------------------------------------------------- * - * Channel interface * + * Channel interface * * ----------------------------------------------------------- */ /** @@ -1227,35 +1239,36 @@ int fuse_session_loop_mt(struct fuse_session *se); * This is used in channel creation */ struct fuse_chan_ops { - /** - * Hook for receiving a raw request - * - * @param ch pointer to the channel - * @param buf the buffer to store the request in - * @param size the size of the buffer - * @return the actual size of the raw request, or -1 on error - */ - int (*receive)(struct fuse_chan **chp, char *buf, size_t size); + /** + * Hook for receiving a raw request + * + * @param ch pointer to the channel + * @param buf the buffer to store the request in + * @param size the size of the buffer + * @return the actual size of the raw request, or -1 on error + */ + int (*receive)(struct fuse_chan **chp, char *buf, size_t size); - /** - * Hook for sending a raw reply - * - * A return value of -ENOENT means, that the request was - * interrupted, and the reply was discarded - * - * @param ch the channel - * @param iov vector of blocks - * @param count the number of blocks in vector - * @return zero on success, -errno on failure - */ - int (*send)(struct fuse_chan *ch, const struct iovec iov[], size_t count); + /** + * Hook for sending a raw reply + * + * A return value of -ENOENT means, that the request was + * interrupted, and the reply was discarded + * + * @param ch the channel + * @param iov vector of blocks + * @param count the number of blocks in vector + * @return zero on success, -errno on failure + */ + int (*send)(struct fuse_chan *ch, const struct iovec iov[], + size_t count); - /** - * Destroy the channel - * - * @param ch the channel - */ - void (*destroy)(struct fuse_chan *ch); + /** + * Destroy the channel + * + * @param ch the channel + */ + void (*destroy)(struct fuse_chan *ch); }; /** @@ -1268,7 +1281,7 @@ struct fuse_chan_ops { * @return the new channel object, or NULL on failure */ struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, - size_t bufsize, void *data); + size_t bufsize, void *data); /** * Query the file descriptor of the channel @@ -1326,7 +1339,7 @@ int fuse_chan_recv(struct fuse_chan **ch, char *buf, size_t size); * @return zero on success, -errno on failure */ int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], - size_t count); + size_t count); /** * Destroy a channel diff --git a/include/fuse-lite/fuse_lowlevel_compat.h b/include/fuse-lite/fuse_lowlevel_compat.h index 1bdebd8a..753d46bc 100644 --- a/include/fuse-lite/fuse_lowlevel_compat.h +++ b/include/fuse-lite/fuse_lowlevel_compat.h @@ -1,9 +1,9 @@ /* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. */ /* these definitions provide source compatibility to prior versions. @@ -12,5 +12,5 @@ size_t fuse_dirent_size(size_t namelen); char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, - off_t off); + off_t off); diff --git a/include/fuse-lite/fuse_opt.h b/include/fuse-lite/fuse_opt.h index 6123d0b4..7ae08af6 100644 --- a/include/fuse-lite/fuse_opt.h +++ b/include/fuse-lite/fuse_opt.h @@ -1,9 +1,9 @@ /* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2007 Miklos Szeredi + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB. + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. */ #ifndef _FUSE_OPT_H_ @@ -41,7 +41,7 @@ extern "C" { * * - 'offsetof(struct foo, member)' actions i) and iii) * - * - -1 action ii) + * - -1 action ii) * * The 'offsetof()' macro is defined in the header. * @@ -52,7 +52,7 @@ extern "C" { * * The types of templates are: * - * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only + * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only * themselves. Invalid values are "--" and anything beginning * with "-o" * @@ -74,30 +74,30 @@ extern "C" { * with scanf(). */ struct fuse_opt { - /** Matching template and optional parameter formatting */ - const char *templ; + /** Matching template and optional parameter formatting */ + const char *templ; - /** - * Offset of variable within 'data' parameter of fuse_opt_parse() - * or -1 - */ - unsigned long offset; + /** + * Offset of variable within 'data' parameter of fuse_opt_parse() + * or -1 + */ + unsigned long offset; - /** - * Value to set the variable to, or to be passed as 'key' to the - * processing function. Ignored if template has a format - */ - int value; + /** + * Value to set the variable to, or to be passed as 'key' to the + * processing function. Ignored if template has a format + */ + int value; }; /** - * Key option. In case of a match, the processing function will be + * Key option. In case of a match, the processing function will be * called with the specified key. */ #define FUSE_OPT_KEY(templ, key) { templ, -1U, key } /** - * Last option. An array of 'struct fuse_opt' must end with a NULL + * Last option. An array of 'struct fuse_opt' must end with a NULL * template value */ #define FUSE_OPT_END { .templ = NULL } @@ -106,14 +106,14 @@ struct fuse_opt { * Argument list */ struct fuse_args { - /** Argument count */ - int argc; + /** Argument count */ + int argc; - /** Argument vector. NULL terminated */ - char **argv; + /** Argument vector. NULL terminated */ + char **argv; - /** Is 'argv' allocated? */ - int allocated; + /** Is 'argv' allocated? */ + int allocated; }; /** @@ -177,7 +177,7 @@ struct fuse_args { * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept */ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, - struct fuse_args *outargs); + struct fuse_args *outargs); /** * Option parsing function @@ -200,7 +200,7 @@ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, * @return -1 on error, 0 on success */ int fuse_opt_parse(struct fuse_args *args, void *data, - const struct fuse_opt opts[], fuse_opt_proc_t proc); + const struct fuse_opt opts[], fuse_opt_proc_t proc); /** * Add an option to a comma separated option list From ce1d83ef0642de2efcfcab1d9bab5be581dd40b5 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 31 Jul 2008 23:06:55 +0000 Subject: [PATCH 210/328] remove not needed auto_cache hereby the librt dependency when using fuse-lite --- configure.ac | 6 --- libfuse-lite/fuse.c | 97 --------------------------------------------- 2 files changed, 103 deletions(-) diff --git a/configure.ac b/configure.ac index cd1a13ab..6dd36acb 100644 --- a/configure.ac +++ b/configure.ac @@ -192,12 +192,6 @@ if test "${with_fuse}" = "internal"; then [LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lpthread"], [AC_MSG_ERROR([Cannot find pthread library])] ) - AC_CHECK_LIB( - [rt], - [clock_gettime], - [LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lrt"], - [AC_MSG_ERROR([Cannot find rt library])] - ) AC_DEFINE( [_REENTRANT], [1], diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 5de1ffec..00c3497f 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -51,7 +51,6 @@ struct fuse_config { int set_gid; int direct_io; int kernel_cache; - int auto_cache; int intr; int intr_signal; int help; @@ -98,10 +97,6 @@ struct node { uint64_t nlookup; int open_count; int is_hidden; - struct timespec stat_updated; - struct timespec mtime; - off_t size; - int cache_valid; struct lock *locks; }; @@ -934,40 +929,6 @@ static int hide_node(struct fuse *f, const char *oldpath, return err; } -static int mtime_eq(const struct stat *stbuf, const struct timespec *ts) -{ - return stbuf->st_mtime == ts->tv_sec && ST_MTIM_NSEC(stbuf) == ts->tv_nsec; -} - -#ifndef CLOCK_MONOTONIC -#define CLOCK_MONOTONIC CLOCK_REALTIME -#endif - -static void curr_time(struct timespec *now) -{ - static clockid_t clockid = CLOCK_MONOTONIC; - int res = clock_gettime(clockid, now); - if (res == -1 && errno == EINVAL) { - clockid = CLOCK_REALTIME; - res = clock_gettime(clockid, now); - } - if (res == -1) { - perror("fuse: clock_gettime"); - abort(); - } -} - -static void update_stat(struct node *node, const struct stat *stbuf) -{ - if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) || - stbuf->st_size != node->size)) - node->cache_valid = 0; - node->mtime.tv_sec = stbuf->st_mtime; - node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf); - node->size = stbuf->st_size; - curr_time(&node->stat_updated); -} - static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, const char *path, struct fuse_entry_param *e, struct fuse_file_info *fi) @@ -990,11 +951,6 @@ static int lookup_path(struct fuse *f, fuse_ino_t nodeid, e->generation = node->generation; e->entry_timeout = f->conf.entry_timeout; e->attr_timeout = f->conf.attr_timeout; - if (f->conf.auto_cache) { - pthread_mutex_lock(&f->lock); - update_stat(node, &e->attr); - pthread_mutex_unlock(&f->lock); - } set_stat(f, e->ino, &e->attr); if (f->conf.debug) fprintf(stderr, " NODEID: %lu\n", (unsigned long) e->ino); @@ -1183,11 +1139,6 @@ static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, } pthread_rwlock_unlock(&f->tree_lock); if (!err) { - if (f->conf.auto_cache) { - pthread_mutex_lock(&f->lock); - update_stat(get_node(f, ino), &buf); - pthread_mutex_unlock(&f->lock); - } set_stat(f, ino, &buf); fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else @@ -1249,11 +1200,6 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, } pthread_rwlock_unlock(&f->tree_lock); if (!err) { - if (f->conf.auto_cache) { - pthread_mutex_lock(&f->lock); - update_stat(get_node(f, ino), &buf); - pthread_mutex_unlock(&f->lock); - } set_stat(f, ino, &buf); fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else @@ -1598,43 +1544,6 @@ static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, pthread_rwlock_unlock(&f->tree_lock); } -static double diff_timespec(const struct timespec *t1, - const struct timespec *t2) -{ - return (t1->tv_sec - t2->tv_sec) + - ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0; -} - -static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, - struct fuse_file_info *fi) -{ - struct node *node; - - pthread_mutex_lock(&f->lock); - node = get_node(f, ino); - if (node->cache_valid) { - struct timespec now; - - curr_time(&now); - if (diff_timespec(&now, &node->stat_updated) > f->conf.ac_attr_timeout) { - struct stat stbuf; - int err; - pthread_mutex_unlock(&f->lock); - err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi); - pthread_mutex_lock(&f->lock); - if (!err) - update_stat(node, &stbuf); - else - node->cache_valid = 0; - } - } - if (node->cache_valid) - fi->keep_cache = 1; - - node->cache_valid = 1; - pthread_mutex_unlock(&f->lock); -} - static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { @@ -1654,9 +1563,6 @@ static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, fi->direct_io = 1; if (f->conf.kernel_cache) fi->keep_cache = 1; - - if (f->conf.auto_cache) - open_auto_cache(f, ino, path, fi); } fuse_finish_interrupt(f, req, &d); } @@ -2568,8 +2474,6 @@ static const struct fuse_opt fuse_lib_opts[] = { FUSE_LIB_OPT("readdir_ino", readdir_ino, 1), FUSE_LIB_OPT("direct_io", direct_io, 1), FUSE_LIB_OPT("kernel_cache", kernel_cache, 1), - FUSE_LIB_OPT("auto_cache", auto_cache, 1), - FUSE_LIB_OPT("noauto_cache", auto_cache, 0), FUSE_LIB_OPT("umask=", set_mode, 1), FUSE_LIB_OPT("umask=%o", umask, 0), FUSE_LIB_OPT("uid=", set_uid, 1), @@ -2594,7 +2498,6 @@ static void fuse_lib_help(void) " -o readdir_ino try to fill in d_ino in readdir\n" " -o direct_io use direct I/O\n" " -o kernel_cache cache files in kernel\n" -" -o [no]auto_cache enable caching based on modification times\n" " -o umask=M set file permissions (octal)\n" " -o uid=N set file owner\n" " -o gid=N set file group\n" From 1212a635bb9d617224614534de6ab9ee398a469b Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 11 Aug 2008 13:02:09 +0000 Subject: [PATCH 211/328] portability fix: use -Wall only with gcc (e.g. Sun Studio fails) --- configure.ac | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 6dd36acb..fad167cc 100644 --- a/configure.ac +++ b/configure.ac @@ -271,8 +271,10 @@ AC_CHECK_FUNCS([ \ ]) AC_SYS_LARGEFILE -# We add -Wall to enable some compiler warnings. -CFLAGS="${CFLAGS} -Wall" +if test "$GCC" = "yes" ; then + # We add -Wall to enable some compiler warnings. + CFLAGS="${CFLAGS} -Wall" +fi if test "${enable_pedantic}" = "yes"; then enable_warnings="yes" From fdd8b1307329d1d8a08367b3bef3b7fd0bcbde35 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 11 Aug 2008 13:04:51 +0000 Subject: [PATCH 212/328] preferred compiler is gcc (currently the only tested and certified one) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fad167cc..66c404b1 100644 --- a/configure.ac +++ b/configure.ac @@ -117,7 +117,7 @@ AC_ARG_ENABLE( AC_GNU_SOURCE # Programs -AC_PROG_CC +AC_PROG_CC(gcc cc) AC_PROG_LN_S AM_PROG_CC_C_O From 9119ac8de26b8d57aba6078c6cbff2c743bbf017 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 11 Aug 2008 15:48:52 +0000 Subject: [PATCH 213/328] version 1.2811 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 66c404b1..25535d40 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.2717],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="35" +AC_INIT([ntfs-3g],[1.2811],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="36" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 330c50c5304440823b65f950290e6f553f00b5d1 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 11 Aug 2008 16:08:31 +0000 Subject: [PATCH 214/328] ntfs-3g can be built only with gcc on Solaris --- configure.ac | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 25535d40..a9f60812 100644 --- a/configure.ac +++ b/configure.ac @@ -168,11 +168,19 @@ freebsd*) AC_MSG_ERROR([Please see FreeBSD support at http://www.freshports.org/sysutils/fusefs-ntfs]) ;; *) - AC_MSG_ERROR([ntfs-3g can be built only under Linux, FreeBSD, Mac OS X, and NetBSD.]) + AC_MSG_ERROR([ntfs-3g can be built for Linux, FreeBSD, Mac OS X, NetBSD, and Solaris only.]) ;; esac AC_MSG_RESULT([${with_fuse}]) +case "${target_os}" in +solaris*) + if test "x$GCC" != "xyes" ; then + AC_MSG_ERROR([ntfs-3g can be built only with gcc on Solaris. Install it by 'pkg install gcc-dev' and retry.)]) + fi + ;; +esac + if test "${enable_ldscript}" = "yes"; then AC_MSG_CHECKING([Output format]) OUTPUT_FORMAT="$(${CC} ${CFLAGS} ${LDFLAGS} -Wl,--verbose 2>&1 | ${SED} -n 's/^OUTPUT_FORMAT("\([[^"]]*\)",.*/\1/p')" From a11f31256084a730c00fd3eebe9eb3e72a892c76 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 11 Aug 2008 16:10:29 +0000 Subject: [PATCH 215/328] version 1.2812 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a9f60812..f674a588 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.2811],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[1.2812],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="36" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) From 31a4ce20e6cdae7a9250990b7f35e752df9f0763 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 21 Aug 2008 20:27:16 +0000 Subject: [PATCH 216/328] FUSE API documentation update (Daniel Benamy) --- include/fuse-lite/fuse.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h index 13f33e99..05c1d7af 100644 --- a/include/fuse-lite/fuse.h +++ b/include/fuse-lite/fuse.h @@ -61,6 +61,11 @@ typedef int (*fuse_fill_dir_t) (void *buf, const char *name, * releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock, * init and destroy are special purpose methods, without which a full * featured filesystem can still be implemented. + * + * Almost all operations take a path which can be of any length. + * + * Changed in fuse 2.8.0 (regardless of API version) + * Previously, paths were limited to a length of PATH_MAX. */ struct fuse_operations { /** Get file attributes. From 2bb181d328a79f28466a29a042dbca87820a12d2 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 17 Sep 2008 21:35:51 +0000 Subject: [PATCH 217/328] fix driver hang when reading corrupted dirs (Peter J. Creath, Paul Mattal, Szabolcs Szakacsits) --- libntfs-3g/dir.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index db5c1edd..560c9ddd 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -168,6 +168,9 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) + goto put_err_out; /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. @@ -317,6 +320,11 @@ descend_into_child_node: */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) { + errno = EIO; + goto close_err_out; + } /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. @@ -808,6 +816,10 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, /* The last entry cannot contain a name. */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + /* Skip index root entry if continuing previous readdir. */ if (ir_pos > (u8*)ie - (u8*)ir) continue; @@ -960,6 +972,10 @@ find_next_index_buffer: /* The last entry cannot contain a name. */ if (ie->ie_flags & INDEX_ENTRY_END) break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + /* Skip index entry if continuing previous readdir. */ if (ia_pos - ia_start > (u8*)ie - (u8*)ia) continue; From 01db330964397d5daadf5a6125d770664769c631 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 18 Sep 2008 12:41:01 +0000 Subject: [PATCH 218/328] release 1.2918 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index f674a588..cce8f1d9 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.2812],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="36" +AC_INIT([ntfs-3g],[1.2918],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="38" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From f089c4b2082631bf612c61ec1e6690c1f257d2dd Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 18 Sep 2008 21:03:35 +0000 Subject: [PATCH 219/328] support unlimited file and directory creation --- libntfs-3g/inode.c | 5 + libntfs-3g/mft.c | 355 +++++++++++++++++++++++++++++++++++++++----- libntfs-3g/volume.c | 4 +- 3 files changed, 327 insertions(+), 37 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index e1c2e33f..6ab5b448 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -1054,6 +1054,7 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) * find next, because we don't need such. */ while (ctx->ntfs_ino->mft_no != ni->mft_no) { +retry: if (ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { err = errno; @@ -1065,6 +1066,10 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) } } + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + ctx->attr->type == AT_DATA) + goto retry; + record_size = le32_to_cpu(ctx->attr->length); /* Move away attribute. */ diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index a57165a1..79375259 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -444,10 +444,19 @@ static inline unsigned int ntfs_ffz(unsigned int word) return ffs(~word) - 1; } +static int ntfs_is_mft(ntfs_inode *ni) +{ + if (ni && ni->mft_no == FILE_MFT) + return 1; + return 0; +} + #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif +#define RESERVED_MFT_RECORDS 64 + /** * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap * @vol: volume on which to search for a free mft record @@ -491,10 +500,10 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) data_pos = vol->mft_data_pos; else data_pos = base_ni->mft_no + 1; - if (data_pos < 24) - data_pos = 24; + if (data_pos < RESERVED_MFT_RECORDS) + data_pos = RESERVED_MFT_RECORDS; if (data_pos >= pass_end) { - data_pos = 24; + data_pos = RESERVED_MFT_RECORDS; pass = 2; /* This happens on a freshly formatted volume. */ if (data_pos >= pass_end) { @@ -502,6 +511,10 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) goto leave; } } + if (ntfs_is_mft(base_ni)) { + data_pos = 0; + pass = 2; + } pass_start = data_pos; buf = ntfs_malloc(PAGE_SIZE); if (!buf) @@ -541,6 +554,14 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) byte ? *byte : -1, b); for (; bit < size && data_pos + bit < pass_end; bit &= ~7ull, bit += 8) { + /* + * If we're extending $MFT and running out of the first + * mft record (base record) then give up searching since + * no guarantee that the found record will be accessible. + */ + if (ntfs_is_mft(base_ni) && bit > 400) + goto out; + byte = buf + (bit >> 3); if (*byte == 0xff) continue; @@ -574,7 +595,7 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) * part of the zone which we omitted earlier. */ pass_end = pass_start; - data_pos = pass_start = 24; + data_pos = pass_start = RESERVED_MFT_RECORDS; ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end " "0x%llx.\n", pass, (long long)pass_start, (long long)pass_end); @@ -583,6 +604,7 @@ static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) } } /* No free mft records in currently initialized mft bitmap. */ +out: free(buf); errno = ENOSPC; leave: @@ -590,6 +612,32 @@ leave: return ret; } +int ntfs_mft_attr_extend(ntfs_volume *vol, ntfs_attr *na) +{ + int ret = STATUS_ERROR; + ntfs_log_enter("Entering\n"); + + if (!NInoAttrList(na->ni)) { + if (ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("%s: Can not add attrlist #3", __FUNCTION__); + goto out; + } + /* We can't sync the $MFT inode since its runlist is bogus. */ + ret = STATUS_KEEP_SEARCHING; + goto out; + } + + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + goto out; + } + + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; +} + /** * ntfs_mft_bitmap_extend_allocation_i - see ntfs_mft_bitmap_extend_allocation */ @@ -603,8 +651,10 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) MFT_RECORD *m = NULL; /* silence compiler warning */ ATTR_RECORD *a = NULL; /* silence compiler warning */ int err, mp_size; + int ret = STATUS_ERROR; u32 old_alen = 0; /* silence compiler warning */ BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; mftbmp_na = vol->mftbmp_na; /* @@ -618,7 +668,7 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) "cluster of mft bitmap attribute.\n"); if (rl) errno = EIO; - return -1; + return STATUS_ERROR; } lcn = rl->lcn + rl->length; @@ -626,7 +676,7 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) if (!rl2) { ntfs_log_error("Failed to allocate a cluster for " "the mft bitmap.\n"); - return -1; + return STATUS_ERROR; } rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); if (!rl) { @@ -638,7 +688,7 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) "cluster.%s\n", es); free(rl2); errno = err; - return -1; + return STATUS_ERROR; } mftbmp_na->rl = rl; ntfs_log_debug("Adding one run to mft bitmap.\n"); @@ -681,12 +731,14 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) old_alen = le32_to_cpu(a->length); if (ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(a->mapping_pairs_offset))) { - // TODO: Deal with this by moving this extent to a new mft - // record or by starting a new extent in a new mft record. - ntfs_log_error("Not enough space in this mft record to " - "accommodate extended mft bitmap attribute " - "extent. Cannot handle this yet.\n"); - errno = EOPNOTSUPP; + ntfs_log_info("extending $MFT bitmap\n"); + ret = ntfs_mft_attr_extend(vol, vol->mftbmp_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } goto undo_alloc; } mp_rebuilt = TRUE; @@ -720,12 +772,14 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) } a = ctx->attr; } +ok: mftbmp_na->allocated_size += vol->cluster_size; a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size); /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); - return 0; + return STATUS_OK; + restore_undo_alloc: err = errno; ntfs_attr_reinit_search_ctx(ctx); @@ -740,7 +794,7 @@ restore_undo_alloc: * base attribute extent which chkdsk should be able to fix. */ errno = err; - return -1; + return STATUS_ERROR; } m = ctx->mrec; a = ctx->attr; @@ -771,10 +825,14 @@ undo_alloc: "record.%s\n", es); ntfs_inode_mark_dirty(ctx->ntfs_ino); } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mftbmp_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; - return -1; + return ret; } /** @@ -915,9 +973,10 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) MFT_RECORD *m = NULL; /* silence compiler warning */ ATTR_RECORD *a = NULL; /* silence compiler warning */ int err, mp_size; - int ret = -1; + int ret = STATUS_ERROR; u32 old_alen = 0; /* silence compiler warning */ BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; ntfs_log_enter("Extending mft data allocation.\n"); @@ -1021,13 +1080,13 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) old_alen = le32_to_cpu(a->length); if (ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(a->mapping_pairs_offset))) { - // TODO: Deal with this by moving this extent to a new mft - // record or by starting a new extent in a new mft record. - // Note: Use the special reserved mft records and ensure that - // this extent is not required to find the mft record in - // question. - errno = EOPNOTSUPP; - ntfs_log_perror("Not enough space to extended mft data"); + ret = ntfs_mft_attr_extend(vol, vol->mft_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } goto undo_alloc; } mp_rebuilt = TRUE; @@ -1065,12 +1124,13 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) } a = ctx->attr; } +ok: mft_na->allocated_size += nr << vol->cluster_size_bits; a->allocated_size = cpu_to_sle64(mft_na->allocated_size); /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); - ret = 0; + ret = STATUS_OK; out: ntfs_log_leave("\n"); return ret; @@ -1089,6 +1149,7 @@ restore_undo_alloc: * base attribute extent which chkdsk should be able to fix. */ errno = err; + ret = STATUS_ERROR; goto out; } m = ctx->mrec; @@ -1115,6 +1176,10 @@ undo_alloc: "record.%s\n", es); ntfs_inode_mark_dirty(ctx->ntfs_ino); } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mft_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; @@ -1149,7 +1214,7 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) (long long)mft_na->data_size, (long long)mft_na->initialized_size); while (size > mft_na->allocated_size) { - if (ntfs_mft_data_extend_allocation(vol)) + if (ntfs_mft_data_extend_allocation(vol) == STATUS_ERROR) goto out; ntfs_log_debug("Status of mft data after allocation extension: " "allocated_size 0x%llx, data_size 0x%llx, " @@ -1195,6 +1260,7 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) } ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + ctx->attr->allocated_size = cpu_to_sle64(mft_na->allocated_size); /* Ensure the changes make it to disk. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); @@ -1210,8 +1276,6 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) if (mft_na->data_size > mft_na->allocated_size || mft_na->initialized_size > mft_na->data_size) NTFS_BUG("mft_na sanity checks failed"); - // BUG_ON(mft_na->initialized_size > mft_na->data_size); - // BUG_ON(mft_na->data_size > mft_na->allocated_size); /* Sync MFT to minimize data loss if there won't be clean unmount. */ if (ntfs_inode_sync(mft_na->ni)) @@ -1228,6 +1292,217 @@ undo_data_init: goto out; } +static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na, *mftbmp_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + if (size > mft_na->allocated_size || size > mft_na->initialized_size) { + errno = EIO; + ntfs_log_perror("%s: unexpected $MFT sizes, see below", __FUNCTION__); + ntfs_log_error("$MFT: size=%lld allocated_size=%lld " + "data_size=%lld initialized_size=%lld\n", + (long long)size, + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + goto out; + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + + /* CHECKME: ctx->attr->allocation_size is already ok? */ + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + ntfs_inode *base_ni; + int err; + u16 seq_no, usn; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + base_ni = mft_na->ni; + + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) + goto found_free_rec; + + if (errno != ENOSPC) + goto out; + + errno = ENOSPC; + /* strerror() is intentionally used below, we want to log this error. */ + ntfs_log_error("No free mft record for $MFT: %s\n", strerror(errno)); + goto err_out; + +found_free_rec: + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap #2\n"); + goto err_out; + } + + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_rec_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + ntfs_log_perror("Error reading mft %lld #2", (long long)bit); + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto undo_mftbmp_alloc; + } + + seq_no = m->sequence_number; + usn = *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (le16_to_cpu(seq_no)) + m->sequence_number = seq_no; + seq_no = le16_to_cpu(usn); + if (seq_no && seq_no != 0xffff) + *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->extent_nis) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = time(NULL); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_error("allocated %sinode %lld\n", + base_ni ? "extent " : "", (long long)bit); +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; +err_exit: + ni = NULL; + goto out; +} + /** * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume * @vol: volume on which to allocate the mft record @@ -1331,6 +1606,11 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) goto out; } + if (ntfs_is_mft(base_ni)) { + ni = ntfs_mft_rec_alloc(vol); + goto out; + } + mft_na = vol->mft_na; mftbmp_na = vol->mftbmp_na; retry: @@ -1352,10 +1632,10 @@ retry: */ ll = mft_na->initialized_size >> vol->mft_record_size_bits; if (mftbmp_na->initialized_size << 3 > ll && - mftbmp_na->initialized_size > 3) { + mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8) { bit = ll; - if (bit < 24) - bit = 24; + if (bit < RESERVED_MFT_RECORDS) + bit = RESERVED_MFT_RECORDS; ntfs_log_debug("found free record (#2) at %lld\n", (long long)bit); goto found_free_rec; @@ -1371,10 +1651,17 @@ retry: (long long)mftbmp_na->data_size, (long long)mftbmp_na->initialized_size); if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { - /* Need to extend bitmap by one more cluster. */ - ntfs_log_debug("mftbmp: initialized_size + 8 > allocated_size.\n"); - if (ntfs_mft_bitmap_extend_allocation(vol)) + + int ret = ntfs_mft_bitmap_extend_allocation(vol); + + if (ret == STATUS_ERROR) goto err_out; + if (ret == STATUS_KEEP_SEARCHING) { + ret = ntfs_mft_bitmap_extend_allocation(vol); + if (ret != STATUS_OK) + goto err_out; + } + ntfs_log_debug("Status of mftbmp after allocation extension: " "allocated_size 0x%llx, data_size 0x%llx, " "initialized_size 0x%llx.\n", diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 07a4dfc0..4849db9f 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -479,9 +479,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) "%s\n", strerror(errno)); /* We now initialize the cluster allocator. */ - - mft_zone_size = min(vol->nr_clusters >> 3, /* 12.5% */ - 200 * 1000 * 1024 >> vol->cluster_size_bits); + mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */ /* Setup the mft zone. */ vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; From 3e4699435ecec06334b7357553036977f71a542c Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 18 Sep 2008 21:05:32 +0000 Subject: [PATCH 220/328] release 1.2926-RC --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index cce8f1d9..1e4e2566 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.2918],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="38" +AC_INIT([ntfs-3g],[1.2926-RC],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="39" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 4d0b4c195c0e7bfd9ce86b4a11d6cf72aa999faa Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 11 Oct 2008 12:39:59 +0000 Subject: [PATCH 221/328] release 1.5012 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 1e4e2566..2d9c6148 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.2926-RC],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="39" +AC_INIT([ntfs-3g],[1.5012],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="40" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 6ef5d450da7b54958743a5c4f8cd2a26ccf5e814 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 15 Oct 2008 08:47:33 +0000 Subject: [PATCH 222/328] mft.c: fix compiler warnings --- libntfs-3g/mft.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 79375259..2e4f3065 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -612,7 +612,7 @@ leave: return ret; } -int ntfs_mft_attr_extend(ntfs_volume *vol, ntfs_attr *na) +static int ntfs_mft_attr_extend(ntfs_attr *na) { int ret = STATUS_ERROR; ntfs_log_enter("Entering\n"); @@ -1354,7 +1354,7 @@ undo_data_init: goto out; } -ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) +static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) { s64 ll, bit; ntfs_attr *mft_na, *mftbmp_na; @@ -1498,7 +1498,6 @@ undo_mftbmp_alloc: err_out: if (!errno) errno = EIO; -err_exit: ni = NULL; goto out; } From 46f8418edc49d018fa6a9e34191826b183012ed5 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 15 Oct 2008 09:18:53 +0000 Subject: [PATCH 223/328] fix ntfs_mft_attr_extend usage --- libntfs-3g/mft.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 2e4f3065..d638cc51 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -732,7 +732,7 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) if (ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(a->mapping_pairs_offset))) { ntfs_log_info("extending $MFT bitmap\n"); - ret = ntfs_mft_attr_extend(vol, vol->mftbmp_na); + ret = ntfs_mft_attr_extend(vol->mftbmp_na); if (ret == STATUS_OK) goto ok; if (ret == STATUS_ERROR) { @@ -1080,7 +1080,7 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) old_alen = le32_to_cpu(a->length); if (ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(a->mapping_pairs_offset))) { - ret = ntfs_mft_attr_extend(vol, vol->mft_na); + ret = ntfs_mft_attr_extend(vol->mft_na); if (ret == STATUS_OK) goto ok; if (ret == STATUS_ERROR) { From 75564ddbd1e7332860975ec0de12c6ee4d76cdee Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 15 Oct 2008 09:21:47 +0000 Subject: [PATCH 224/328] fix redundant redeclaration of fuse_lowlevel_new --- libfuse-lite/fuse_i.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libfuse-lite/fuse_i.h b/libfuse-lite/fuse_i.h index 9101fb58..38c45c72 100644 --- a/libfuse-lite/fuse_i.h +++ b/libfuse-lite/fuse_i.h @@ -21,9 +21,5 @@ struct fuse_cmd { struct fuse_chan *fuse_kern_chan_new(int fd); -struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, - const struct fuse_lowlevel_ops *op, - size_t op_size, void *userdata); - void fuse_kern_unmount(const char *mountpoint, int fd); int fuse_kern_mount(const char *mountpoint, struct fuse_args *args); From 1f8c7efb38763017bc61218320da474aa5b950a2 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 16 Oct 2008 23:38:13 +0000 Subject: [PATCH 225/328] remove unused version files --- include/ntfs-3g/Makefile.am | 1 - include/ntfs-3g/version.h | 29 --------------------------- libntfs-3g/Makefile.am | 1 - libntfs-3g/version.c | 40 ------------------------------------- src/ntfs-3g.c | 1 - 5 files changed, 72 deletions(-) delete mode 100644 include/ntfs-3g/version.h delete mode 100644 libntfs-3g/version.c diff --git a/include/ntfs-3g/Makefile.am b/include/ntfs-3g/Makefile.am index a6550186..3f085486 100644 --- a/include/ntfs-3g/Makefile.am +++ b/include/ntfs-3g/Makefile.am @@ -29,7 +29,6 @@ headers = \ support.h \ types.h \ unistr.h \ - version.h \ volume.h if INSTALL_LIBRARY diff --git a/include/ntfs-3g/version.h b/include/ntfs-3g/version.h deleted file mode 100644 index 5f94d11a..00000000 --- a/include/ntfs-3g/version.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * version.h - Info about the NTFS library. Originated from the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - * Copyright (c) 2005 Richard Russon - * - * This program/include file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program/include file is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the NTFS-3G - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _NTFS_VERSION_H_ -#define _NTFS_VERSION_H_ - -extern const char *ntfs_libntfs_version(void); - -#endif /* _NTFS_VERSION_H_ */ - diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index 825742dc..325d2f68 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -39,7 +39,6 @@ libntfs_3g_la_SOURCES = \ runlist.c \ security.c \ unistr.c \ - version.c \ volume.c if NTFS_DEVICE_DEFAULT_IO_OPS diff --git a/libntfs-3g/version.c b/libntfs-3g/version.c deleted file mode 100644 index 76b742f8..00000000 --- a/libntfs-3g/version.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * version.c - Info about the NTFS library. Originated from the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - * Copyright (c) 2005 Richard Russon - * - * This program/include file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program/include file is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the NTFS-3G - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "version.h" - -/* FIXME: merge libntfs into the user space NTFS driver */ -static const char *libntfs_version_string = "22:0:0"; - -/** - * ntfs_libntfs_version - query version number of the ntfs library libntfs - * - * Returns pointer to a text string representing the version of libntfs. - */ -const char *ntfs_libntfs_version(void) -{ - return libntfs_version_string; -} diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 22a04fc1..af34fe6e 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -90,7 +90,6 @@ #include "layout.h" #include "index.h" #include "utils.h" -#include "version.h" #include "ntfstime.h" #include "misc.h" From 524c10f74b3f2e67fb129f9dbe9a437fe01313bf Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 22 Oct 2008 13:50:59 +0000 Subject: [PATCH 226/328] version 1.5020 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 2d9c6148..c181b4b9 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.5012],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="40" +AC_INIT([ntfs-3g],[1.5020],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="41" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From da3774f53fe8f6be1f8458c02e5f238d3cffcf52 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 13 Nov 2008 01:48:07 +0000 Subject: [PATCH 227/328] remove optional log coloring --- include/ntfs-3g/logging.h | 1 - libntfs-3g/logging.c | 48 --------------------------------------- 2 files changed, 49 deletions(-) diff --git a/include/ntfs-3g/logging.h b/include/ntfs-3g/logging.h index 042ae762..401f5c97 100644 --- a/include/ntfs-3g/logging.h +++ b/include/ntfs-3g/logging.h @@ -86,7 +86,6 @@ int ntfs_log_redirect(const char *function, const char *file, int line, #define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ #define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ #define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ -#define NTFS_LOG_FLAG_COLOUR (1 << 5) /* Colour highlight some messages */ /* Macros to simplify logging. One for each level defined above. * Note, ntfs_log_debug/trace have effect only if DEBUG is defined. diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index 3330fc0d..385bcaa6 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -50,14 +50,6 @@ #define PATH_SEP '/' #endif -/* Colour prefixes and a suffix */ -static const char *col_green = "\e[32m"; -static const char *col_cyan = "\e[36m"; -static const char *col_yellow = "\e[01;33m"; -static const char *col_red = "\e[01;31m"; -static const char *col_redinv = "\e[01;07;31m"; -static const char *col_end = "\e[0m"; - #ifdef DEBUG static int tab; #endif @@ -426,8 +418,6 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, int ret = 0; int olderr = errno; FILE *stream; - const char *col_prefix = NULL; - const char *col_suffix = NULL; if (!data) /* Interpret data as a FILE stream. */ return 0; /* If it's NULL, we can't do anything. */ @@ -439,41 +429,10 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, tab--; return 0; } -#endif - if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) { - /* Pick a colour determined by the log level */ - switch (level) { - case NTFS_LOG_LEVEL_DEBUG: - col_prefix = col_green; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_TRACE: - col_prefix = col_cyan; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_WARNING: - col_prefix = col_yellow; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_ERROR: - case NTFS_LOG_LEVEL_PERROR: - col_prefix = col_red; - col_suffix = col_end; - break; - case NTFS_LOG_LEVEL_CRITICAL: - col_prefix = col_redinv; - col_suffix = col_end; - break; - } - } -#ifdef DEBUG for (i = 0; i < tab; i++) ret += fprintf(stream, " "); #endif - if (col_prefix) - ret += fprintf(stream, col_prefix); - if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && (strchr(file, PATH_SEP))) /* Abbreviate the filename */ file = strrchr(file, PATH_SEP) + 1; @@ -496,9 +455,6 @@ int ntfs_log_handler_fprintf(const char *function, const char *file, if (level & NTFS_LOG_LEVEL_PERROR) ret += fprintf(stream, ": %s\n", strerror(olderr)); - if (col_suffix) - ret += fprintf(stream, col_suffix); - #ifdef DEBUG if (level == NTFS_LOG_LEVEL_ENTER) tab++; @@ -649,10 +605,6 @@ BOOL ntfs_log_parse_option(const char *option) } else if (strcmp(option, "--log-trace") == 0) { ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); return TRUE; - } else if ((strcmp(option, "--log-colour") == 0) || - (strcmp(option, "--log-color") == 0)) { - ntfs_log_set_flags(NTFS_LOG_FLAG_COLOUR); - return TRUE; } ntfs_log_debug("Unknown logging option '%s'\n", option); From e8b6fe03f6902944e2196d794598b0e6948d6930 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 13 Nov 2008 01:53:21 +0000 Subject: [PATCH 228/328] fix all -Wformat -Wformat-security compiler warnings (Vincent Untz) --- src/ntfs-3g.c | 4 ++-- src/utils.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index af34fe6e..d5d1fcf0 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2013,7 +2013,7 @@ static void mknod_dev_fuse(const char *dev) if (mknod(dev, S_IFCHR | 0666, makedev(10, 229))) { ntfs_log_perror("Failed to create '%s'", dev); if (errno == EPERM) - ntfs_log_error(dev_fuse_msg); + ntfs_log_error("%s", dev_fuse_msg); } umask(mask); } @@ -2290,7 +2290,7 @@ int main(int argc, char *argv[]) #if defined(linux) || defined(__uClinux__) if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) - ntfs_log_info(fuse26_kmod_msg); + ntfs_log_info("%s", fuse26_kmod_msg); #endif setup_logging(parsed_options); diff --git a/src/utils.c b/src/utils.c index f59683d0..5f3f3db0 100644 --- a/src/utils.c +++ b/src/utils.c @@ -133,7 +133,7 @@ void utils_mount_error(const char *volume, const char *mntpoint, int err) ntfs_log_error(hibernated_volume_msg, volume, mntpoint); break; case NTFS_VOLUME_UNCLEAN_UNMOUNT: - ntfs_log_error(unclean_journal_msg); + ntfs_log_error("%s", unclean_journal_msg); ntfs_log_error(forced_mount_msg, volume, mntpoint, volume, mntpoint); break; From 8b7954e4abd4dcc6ffaf350e10d18053c66a1920 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 13 Nov 2008 02:21:58 +0000 Subject: [PATCH 229/328] ntfs_fuse_write: fix compiler warning on logging format string --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index d5d1fcf0..12e88ec5 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -730,7 +730,7 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, if (0 <= ret && ret < (s64)size) ntfs_log_perror("ntfs_attr_pwrite partial write to '%s'" " (%lld: %lld <> %lld)", path, (long long)offset, - (long long)size, ret); + (long long)size, (long long)ret); if (ret <= 0) { res = -errno; goto exit; From 32fe0868dd292d16636b36894fb95ed6b957717c Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 13 Nov 2008 02:27:41 +0000 Subject: [PATCH 230/328] add "-Wformat -Wformat-security" to --enable-warnings compiler warnings --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c181b4b9..2d8bed9d 100644 --- a/configure.ac +++ b/configure.ac @@ -290,7 +290,7 @@ if test "${enable_pedantic}" = "yes"; then fi if test "${enable_warnings}" = "yes"; then - CFLAGS="${CFLAGS} -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings" + CFLAGS="${CFLAGS} -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings -Wformat -Wformat-security" fi if test "${enable_debug}" = "yes"; then From ce825e1a423d8eaa623db24c38be633d65716c45 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 13 Nov 2008 02:31:50 +0000 Subject: [PATCH 231/328] uninline not inlineable functions, hinted by compiler warnings --- libfuse-lite/fuse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 00c3497f..b721bd68 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -515,14 +515,14 @@ static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d) fuse_req_interrupt_func(req, fuse_interrupt, d); } -static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req, +static void fuse_finish_interrupt(struct fuse *f, fuse_req_t req, struct fuse_intr_data *d) { if (f->conf.intr) fuse_do_finish_interrupt(f, req, d); } -static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, +static void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, struct fuse_intr_data *d) { if (f->conf.intr) From a1ca403e4176531f76ac431d24079b5ebca251fa Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 30 Nov 2008 17:16:37 +0000 Subject: [PATCH 232/328] fix read potentially returning corrupt data when the hardware fails --- libntfs-3g/attrib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 87aa2e18..803d48cb 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -914,8 +914,9 @@ retry: total += br; count -= br; b = (u8*)b + br; - continue; } + if (br == to_read) + continue; /* If the syscall was interrupted, try again. */ if (br == (s64)-1 && errno == EINTR) goto retry; From 86b9c8217652b12ae172ab601e1e2b79907e593e Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 30 Nov 2008 21:39:41 +0000 Subject: [PATCH 233/328] ntfs_external_attr_find: log all errors --- libntfs-3g/attrib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 803d48cb..77ed8a48 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2107,7 +2107,7 @@ is_enumeration: if (MSEQNO_LE(al_entry->mft_reference) != le16_to_cpu( ni->mrec->sequence_number)) { - ntfs_log_debug("Found stale mft reference in " + ntfs_log_error("Found stale mft reference in " "attribute list!\n"); break; } @@ -2193,8 +2193,8 @@ do_next_attr: ctx->mrec = ctx->base_mrec; ctx->attr = ctx->base_attr; } - ntfs_log_debug("Inode is corrupt.\n"); errno = EIO; + ntfs_log_perror("Inode is corrupt (%lld)", (unsigned long long)ni->mft_no); return -1; not_found: /* From 70b7c4f8bfc5caec99f74cbd89ba7977604485a0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 30 Nov 2008 21:54:18 +0000 Subject: [PATCH 234/328] change invocation examples to work on OS X --- src/ntfs-3g.c | 4 ++-- src/utils.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 12e88ec5..679a8a53 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -164,13 +164,13 @@ static const char *usage_msg = "Copyright (C) 2006-2008 Szabolcs Szakacsits\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "\n" -"Usage: %s [-o option[,...]]\n" +"Usage: %s [-o option[,...]] \n" "\n" "Options: ro (read-only mount), force, remove_hiberfile, locale=,\n" " uid=, gid=, umask=, fmask=, dmask=, streams_interface=.\n" " Please see the details in the manual.\n" "\n" -"Example: ntfs-3g /dev/sda1 /mnt/win -o force\n" +"Examples: ntfs-3g -o force /dev/sda1 /mnt/windows\n" "\n" "%s"; diff --git a/src/utils.c b/src/utils.c index 5f3f3db0..8914eb25 100644 --- a/src/utils.c +++ b/src/utils.c @@ -66,7 +66,7 @@ static const char *hibernated_volume_msg = "mount the volume read-write with the 'remove_hiberfile' mount option.\n" "For example type on the command line:\n" "\n" -" mount -t ntfs-3g %s %s -o remove_hiberfile\n" +" mount -t ntfs-3g -o remove_hiberfile %s %s\n" "\n"; static const char *unclean_journal_msg = @@ -97,7 +97,7 @@ static const char *access_denied_msg = static const char *forced_mount_msg = "\n" -" mount -t ntfs-3g %s %s -o force\n" +" mount -t ntfs-3g -o force %s %s\n" "\n" " Or add the option to the relevant row in the /etc/fstab file:\n" "\n" From 7da19cacd78650d6696e7e67215645cc9ebdb6c0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 30 Nov 2008 21:55:40 +0000 Subject: [PATCH 235/328] release 1.5130 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 2d8bed9d..99299c1d 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.5020],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="41" +AC_INIT([ntfs-3g],[1.5130],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="43" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 31026fb7b06c3405c3a62e2555e8afef5debd23a Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 6 Dec 2008 22:11:28 +0000 Subject: [PATCH 236/328] librarize utils_ functions --- include/ntfs-3g/volume.h | 5 ++ libntfs-3g/volume.c | 107 +++++++++++++++++++++++++++ src/Makefile.am | 4 +- src/ntfs-3g.c | 5 +- src/ntfs-3g.probe.c | 1 - src/utils.c | 151 --------------------------------------- src/utils.h | 37 ---------- 7 files changed, 116 insertions(+), 194 deletions(-) delete mode 100644 src/utils.c delete mode 100644 src/utils.h diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index f8cab845..4f6f004b 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -219,6 +219,8 @@ struct _ntfs_volume { s64 free_mft_records; /* Same for free mft records (see above) */ }; +extern const char *ntfs_home; + extern ntfs_volume *ntfs_volume_alloc(void); extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, @@ -237,6 +239,9 @@ extern int ntfs_logfile_reset(ntfs_volume *vol); extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); extern int ntfs_volume_error(int err); +extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); + +extern int ntfs_set_locale(void); #endif /* defined _NTFS_VOLUME_H */ diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 4849db9f..e4ba6b56 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -49,6 +49,9 @@ #ifdef HAVE_LIMITS_H #include #endif +#ifdef HAVE_LOCALE_H +#include +#endif #include "volume.h" #include "attrib.h" @@ -67,6 +70,66 @@ #define PATH_MAX 4096 #endif +const char *ntfs_home = +"Ntfs-3g news, support and information: http://ntfs-3g.org\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't seem to have a valid NTFS.\n" +"Maybe the wrong device is used? Or the whole disk instead of a\n" +"partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is either inconsistent, or there is a hardware fault, or it's a\n" +"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" +"then reboot into Windows twice. The usage of the /f parameter is very\n" +"important! If the device is a SoftRAID/FakeRAID then first activate\n" +"it and mount a different device under the /dev/mapper/ directory, (e.g.\n" +"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" +"for more details.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Please resume and shutdown Windows\n" +"properly, or mount the volume read-only with the 'ro' mount option, or\n" +"mount the volume read-write with the 'remove_hiberfile' mount option.\n" +"For example type on the command line:\n" +"\n" +" mount -t ntfs-3g -o remove_hiberfile %s %s\n" +"\n"; + +static const char *unclean_journal_msg = +"Mount is denied because NTFS is marked to be in use. Choose one action:\n" +"\n" +"Choice 1: If you have Windows then disconnect the external devices by\n" +" clicking on the 'Safely Remove Hardware' icon in the Windows\n" +" taskbar then shutdown Windows cleanly.\n" +"\n" +"Choice 2: If you don't have Windows then you can use the 'force' option for\n" +" your own responsibility. For example type on the command line:\n"; + +static const char *opened_volume_msg = +"Mount is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *fakeraid_msg = +"Either the device is missing or it's powered down, or you have\n" +"SoftRAID hardware and must use an activated, different device under\n" +"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" +"Please see the 'dmraid' documentation for help.\n"; + +static const char *access_denied_msg = +"Please check '%s' and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://ntfs-3g.org/support.html#unprivileged\n"; + +static const char *forced_mount_msg = +"\n" +" mount -t ntfs-3g -o force %s %s\n" +"\n" +" Or add the option to the relevant row in the /etc/fstab file:\n" +"\n" +" %s %s ntfs-3g force 0 0\n"; + /** * ntfs_volume_alloc - Create an NTFS volume object and initialise it * @@ -1473,3 +1536,47 @@ int ntfs_volume_error(int err) return ret; } + +void ntfs_mount_error(const char *volume, const char *mntpoint, int err) +{ + switch (err) { + case NTFS_VOLUME_NOT_NTFS: + ntfs_log_error(invalid_ntfs_msg, volume); + break; + case NTFS_VOLUME_CORRUPT: + ntfs_log_error("%s", corrupt_volume_msg); + break; + case NTFS_VOLUME_HIBERNATED: + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); + break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: + ntfs_log_error("%s", unclean_journal_msg); + ntfs_log_error(forced_mount_msg, volume, mntpoint, + volume, mntpoint); + break; + case NTFS_VOLUME_LOCKED: + ntfs_log_error("%s", opened_volume_msg); + break; + case NTFS_VOLUME_RAID: + ntfs_log_error("%s", fakeraid_msg); + break; + case NTFS_VOLUME_NO_PRIVILEGE: + ntfs_log_error(access_denied_msg, volume); + break; + } +} + +int ntfs_set_locale(void) +{ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Couldn't set local environment, using default " + "'%s'.\n", locale); + return 1; + } + return 0; +} + diff --git a/src/Makefile.am b/src/Makefile.am index e04776c8..22d70254 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,14 +23,14 @@ ntfs_3g_CFLAGS = \ -DFUSE_USE_VERSION=26 \ $(FUSE_CFLAGS) \ -I$(top_srcdir)/include/ntfs-3g -ntfs_3g_SOURCES = ntfs-3g.c utils.c utils.h +ntfs_3g_SOURCES = ntfs-3g.c ntfs_3g_probe_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la if REALLYSTATIC ntfs_3g_probe_LDFLAGS = $(AM_LDFLAGS) -all-static endif ntfs_3g_probe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g -ntfs_3g_probe_SOURCES = ntfs-3g.probe.c utils.c utils.h +ntfs_3g_probe_SOURCES = ntfs-3g.probe.c if RUN_LDCONFIG install-exec-hook: diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 679a8a53..83fe3f94 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -89,7 +89,6 @@ #include "unistr.h" #include "layout.h" #include "index.h" -#include "utils.h" #include "ntfstime.h" #include "misc.h" @@ -2219,7 +2218,7 @@ int main(int argc, char *argv[]) if (drop_privs()) return NTFS_VOLUME_NO_PRIVILEGE; - utils_set_locale(); + ntfs_set_locale(); ntfs_log_set_handler(ntfs_log_handler_stderr); if (parse_options(argc, argv)) { @@ -2301,7 +2300,7 @@ int main(int argc, char *argv[]) fuse_unmount(opts.mnt_point, ctx->fc); fuse_destroy(fh); err_out: - utils_mount_error(opts.device, opts.mnt_point, err); + ntfs_mount_error(opts.device, opts.mnt_point, err); err2: ntfs_close(); free(ctx); diff --git a/src/ntfs-3g.probe.c b/src/ntfs-3g.probe.c index 8981c064..be265dd7 100644 --- a/src/ntfs-3g.probe.c +++ b/src/ntfs-3g.probe.c @@ -33,7 +33,6 @@ #include #include "volume.h" -#include "utils.h" #include "misc.h" #ifndef PATH_MAX diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 8914eb25..00000000 --- a/src/utils.c +++ /dev/null @@ -1,151 +0,0 @@ -/** - * utils.c - Originated from the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2003-2006 Anton Altaparmakov - * Copyright (c) 2003 Lode Leroy - * Copyright (c) 2005-2008 Szabolcs Szakacsits - * - * A set of shared functions for ntfs utilities - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_LOCALE_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif - -#include "utils.h" - -const char *ntfs_home = - "Ntfs-3g news, support and information: http://ntfs-3g.org\n"; -const char *ntfs_gpl = "This program is free software, released under the GNU " - "General Public License\nand you are welcome to redistribute it under " - "certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for " - "details read the GNU General Public License to be\nfound in the file " - "\"COPYING\" distributed with this program, or online at:\n" - "http://www.gnu.org/copyleft/gpl.html\n"; - -static const char *invalid_ntfs_msg = -"The device '%s' doesn't have a valid NTFS.\n" -"Maybe you selected the wrong device? Or the whole disk instead of a\n" -"partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n"; - -static const char *corrupt_volume_msg = -"NTFS is either inconsistent, or you have hardware faults, or you have a\n" -"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" -"then reboot into Windows TWICE. The usage of the /f parameter is very\n" -"important! If you have SoftRAID/FakeRAID then first you must activate\n" -"it and mount a different device under the /dev/mapper/ directory, (e.g.\n" -"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" -"for the details.\n"; - -static const char *hibernated_volume_msg = -"The NTFS partition is hibernated. Please resume and shutdown Windows\n" -"properly, or mount the volume read-only with the 'ro' mount option, or\n" -"mount the volume read-write with the 'remove_hiberfile' mount option.\n" -"For example type on the command line:\n" -"\n" -" mount -t ntfs-3g -o remove_hiberfile %s %s\n" -"\n"; - -static const char *unclean_journal_msg = -"Mount is denied because NTFS is marked to be in use. Choose one action:\n" -"\n" -"Choice 1: If you have Windows then disconnect the external devices by\n" -" clicking on the 'Safely Remove Hardware' icon in the Windows\n" -" taskbar then shutdown Windows cleanly.\n" -"\n" -"Choice 2: If you don't have Windows then you can use the 'force' option for\n" -" your own responsibility. For example type on the command line:\n"; - -static const char *opened_volume_msg = -"Mount is denied because the NTFS volume is already exclusively opened.\n" -"The volume may be already mounted, or another software may use it which\n" -"could be identified for example by the help of the 'fuser' command.\n"; - -static const char *fakeraid_msg = -"Either the device is missing or it's powered down, or you have\n" -"SoftRAID hardware and must use an activated, different device under\n" -"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" -"Please see the 'dmraid' documentation for help.\n"; - -static const char *access_denied_msg = -"Please check '%s' and the ntfs-3g binary permissions,\n" -"and the mounting user ID. More explanation is provided at\n" -"http://ntfs-3g.org/support.html#unprivileged\n"; - -static const char *forced_mount_msg = -"\n" -" mount -t ntfs-3g -o force %s %s\n" -"\n" -" Or add the option to the relevant row in the /etc/fstab file:\n" -"\n" -" %s %s ntfs-3g force 0 0\n"; - -/** - * utils_set_locale - */ -int utils_set_locale(void) -{ - const char *locale; - - locale = setlocale(LC_ALL, ""); - if (!locale) { - locale = setlocale(LC_ALL, NULL); - ntfs_log_error("Couldn't set local environment, using default " - "'%s'.\n", locale); - return 1; - } - return 0; -} - -void utils_mount_error(const char *volume, const char *mntpoint, int err) -{ - switch (err) { - case NTFS_VOLUME_NOT_NTFS: - ntfs_log_error(invalid_ntfs_msg, volume); - break; - case NTFS_VOLUME_CORRUPT: - ntfs_log_error("%s", corrupt_volume_msg); - break; - case NTFS_VOLUME_HIBERNATED: - ntfs_log_error(hibernated_volume_msg, volume, mntpoint); - break; - case NTFS_VOLUME_UNCLEAN_UNMOUNT: - ntfs_log_error("%s", unclean_journal_msg); - ntfs_log_error(forced_mount_msg, volume, mntpoint, - volume, mntpoint); - break; - case NTFS_VOLUME_LOCKED: - ntfs_log_error("%s", opened_volume_msg); - break; - case NTFS_VOLUME_RAID: - ntfs_log_error("%s", fakeraid_msg); - break; - case NTFS_VOLUME_NO_PRIVILEGE: - ntfs_log_error(access_denied_msg, volume); - break; - } -} - diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 2d47ca4b..00000000 --- a/src/utils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * utils.h - Originated from the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2004 Anton Altaparmakov - * Copyright (c) 2005-2007 Szabolcs Szakacsits - * - * A set of shared functions for ntfs utilities - * - * 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 - */ - -#ifndef _NTFS_UTILS_H_ -#define _NTFS_UTILS_H_ - -#include "volume.h" - -extern const char *ntfs_home; -extern const char *ntfs_gpl; - -int utils_set_locale(void); -void utils_mount_error(const char *vol, const char *mntpoint, int err); - -#endif /* _NTFS_UTILS_H_ */ From 9dc9eb60e91c8336ee64e9a35218aaac9d60bc12 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 12 Dec 2008 22:14:41 +0000 Subject: [PATCH 237/328] ntfs_fuse_statfs: fix f_bsize comment about optimal transfer block size --- src/ntfs-3g.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 83fe3f94..dc8f5a64 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -260,7 +260,10 @@ static int ntfs_fuse_statfs(const char *path __attribute__((unused)), if (!vol) return -ENODEV; - /* File system block size, used for optimal transfer block size. */ + /* + * 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. */ From f7a15b70418ad1d8dcda40c4921fd52c539fe3e3 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 15 Dec 2008 03:10:49 +0000 Subject: [PATCH 238/328] uninline reply_err() --- libfuse-lite/fuse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index b721bd68..11be5b1b 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -1024,7 +1024,7 @@ static struct fuse *req_fuse_prepare(fuse_req_t req) return c->ctx.fuse; } -static inline void reply_err(fuse_req_t req, int err) +static void reply_err(fuse_req_t req, int err) { /* fuse_reply_err() uses non-negated errno values */ fuse_reply_err(req, -err); From 0017cc31dfe0ba07620d048b53801a21a0bc6e16 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 21 Dec 2008 23:28:08 +0000 Subject: [PATCH 239/328] add built-in UTF8 support, use as default (Bernhard Kaindl, Jean-Pierre Andre) --- libntfs-3g/unistr.c | 311 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index fac9ca56..1898a451 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -3,6 +3,8 @@ * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2008 Jean-Pierre Andre + * Copyright (c) 2008 Bernhard Kaindl * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -39,6 +41,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LOCALE_H +#include +#endif #include "attrib.h" #include "types.h" @@ -47,6 +52,8 @@ #include "logging.h" #include "misc.h" +#define NOREVBOM 0 /* JPA rejecting U+FFFE and U+FFFF, open to debate */ + /* * IMPORTANT * ========= @@ -55,6 +62,8 @@ * encoding inside the strings!!! */ +static int use_utf8 = 1; /* use UTF-8 encoding for file names */ + /* * This is used by the name collation functions to quickly determine what * characters are (in)valid. @@ -373,6 +382,282 @@ int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, err_val, ic, upcase, upcase_len); } +/* + NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough + for now]) for path names, but the Unicode code points need to be + converted before a path can be accessed under NTFS. For 7 bit ASCII/ANSI, + glibc does this even without a locale in a hard-coded fashion as that + appears to be is easy because the low 7-bit ASCII range appears to be + available in all charsets but it does not convert anything if + there was some error with the locale setup or none set up like + when mount is called during early boot where he (by policy) do + not use locales (and may be not available if /usr is not yet mounted), + so this patch fixes the resulting issues for systems which use + UTF-8 and for others, specifying the locale in fstab brings them + the encoding which they want. + + If no locale is defined or there was a problem with setting one + up and whenever nl_langinfo(CODESET) returns a sting starting with + "ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix + the bug where NTFS-3G does not show any path names which include + international characters!!! (and also fails on creating them) as result. + + Author: Bernhard Kaindl + Jean-Pierre Andre made it compliant with RFC3629/RFC2781. +*/ + +/* + * Return the amount of 8-bit elements in UTF-8 needed (without + * the terminating null) to store a given UTF-16LE string. + */ +static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) +{ + int i; + int count = 0; + BOOL surrog; + + surrog = FALSE; + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + if (surrog) { + if ((c >= 0xdc00) && (c < 0xe000)) { + surrog = FALSE; + count += 4; + } else goto fail; + } else + if (c < 0x80) + count++; + else if (c < 0x800) + count += 2; + else if (c < 0xd800) + count += 3; + else if (c < 0xdc00) + surrog = TRUE; +#if NOREVBOM + else if ((c >= 0xe000) && (c < 0xfffe)) +#else + else if (c >= 0xe000) +#endif + count += 3; + else goto fail; + if (count > outs_len) + goto fail; + } + if (surrog) goto fail; + return count; +fail: + return -1; +} + +/* + * ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string + * @ins: input utf16 string buffer + * @ins_len: length of input string in utf16 characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + */ +static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, + char **outs, int outs_len) +{ + char *t; + int i, size; + ntfschar halfpair; + + halfpair = 0; + if (!*outs) + outs_len = PATH_MAX; + + size = utf16_to_utf8_size(ins, ins_len, outs_len); + + if (size < 0) { + errno = ENAMETOOLONG; + goto fail; + } + if (!*outs) + *outs = ntfs_malloc((outs_len = size + 1)); + + t = *outs; + + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + /* size not double-checked */ + if (halfpair) { + if ((c >= 0xdc00) && (c < 0xe000)) { + *t++ = 0xf0 + (((halfpair + 64) >> 8) & 7); + *t++ = 0x80 + (((halfpair + 64) >> 2) & 63); + *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); + *t++ = 0x80 + (c & 63); + halfpair = 0; + } else goto fail; + } else if (c < 0x80) { + *t++ = c; + } else { + if (c < 0x800) { + *t++ = (0xc0 | ((c >> 6) & 0x3f)); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xd800) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xdc00) + halfpair = c; + else if (c >= 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else goto fail; + } + } + *t = '\0'; + return t - *outs; +fail: + return -1; +} + +/* + * Return the amount of 16-bit elements in UTF-16LE needed + * (without the terminating null) to store given UTF-8 string. + * + * Return -1 if it does not fit into PATH_MAX. + * + * Note: This does not check whether the input sequence is a valid utf8 string, + * and should be used only in context where such check is made! + */ +static int utf8_to_utf16_size(const char *s) +{ + unsigned int byte; + size_t count = 0; + + while ((byte = *((const unsigned char *)s++))) { + if (++count >= PATH_MAX || byte >= 0xF5) + goto fail; + if (!*s) break; + if (byte >= 0xC0) s++; + if (!*s) break; + if (byte >= 0xE0) s++; + if (!*s) break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } + } + return count; +fail: + return -1; +} +/* + * This converts one UTF-8 sequence to cpu-endian Unicode value + * within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF + * Returns the number of used utf8 bytes or -1 if sequence is invalid. + */ +static int utf8_to_unicode(u32 *wc, const char *s) +{ + unsigned int byte = *((const unsigned char *)s); + + /* single byte */ + if (byte == 0) { + *wc = (u32) 0; + return 0; + } else if (byte < 0x80) { + *wc = (u32) byte; + return 1; + /* double byte */ + } else if (byte < 0xc2) { + goto fail; + } else if (byte < 0xE0) { + if (strlen(s) < 2) + goto fail; + if ((s[1] & 0xC0) == 0x80) { + *wc = ((u32)(byte & 0x1F) << 6) + | ((u32)(s[1] & 0x3F)); + return 2; + } else + goto fail; + /* three-byte */ + } else if (byte < 0xF0) { + if (strlen(s) < 3) + goto fail; + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x0F) << 12) + | ((u32)(s[1] & 0x3F) << 6) + | ((u32)(s[2] & 0x3F)); + /* Check valid ranges */ +#if NOREVBOM + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + return 3; +#else + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + return 3; +#endif + } + goto fail; + /* four-byte */ + } else if (byte < 0xF5) { + if (strlen(s) < 4) + goto fail; + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) + && ((s[3] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x07) << 18) + | ((u32)(s[1] & 0x3F) << 12) + | ((u32)(s[2] & 0x3F) << 6) + | ((u32)(s[3] & 0x3F)); + /* Check valid ranges */ + if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) + return 4; + } + goto fail; + } +fail: + return -1; +} + +/** + * ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output utf16 string + * @outs_len: length of output buffer in utf16 characters + */ +static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) +{ + const char *t = ins; + u32 wc; + ntfschar *outpos; + int shorts = utf8_to_utf16_size(ins); + + if (shorts < 0) { + errno = EILSEQ; + goto fail; + } + if (!*outs) + *outs = ntfs_malloc((shorts+1) * sizeof(ntfschar)); + + outpos = *outs; + + while(1) { + int m = utf8_to_unicode(&wc, t); + if (m < 0) { + errno = EILSEQ; + goto fail; + } + if (wc < 0x10000) + *outpos++ = cpu_to_le16(wc); + else { + wc -= 0x10000; + *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); + *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); + } + if (m == 0) + break; + t += m; + } + return --outpos - *outs; +fail: + return -1; +} + /** * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string * @ins: input Unicode string buffer @@ -419,6 +704,8 @@ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, errno = ENAMETOOLONG; return -1; } + if (use_utf8) + return ntfs_utf16_to_utf8(ins, ins_len, outs, outs_len); if (!mbs) { mbs_len = (ins_len + 1) * MB_CUR_MAX; mbs = ntfs_malloc(mbs_len); @@ -525,6 +812,9 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs) return -1; } + if (use_utf8) + return ntfs_utf8_to_utf16(ins, outs); + /* Determine the size of the multi-byte string in bytes. */ ins_size = strlen(ins); /* Determine the length of the multi-byte string. */ @@ -734,3 +1024,24 @@ void ntfs_ucsfree(ntfschar *ucs) free(ucs); } +/* + * Define the character encoding to be used. + * Use UTF-8 unless specified otherwise. + */ + +int ntfs_set_char_encoding(const char *locale) +{ + use_utf8 = 0; + if (!locale || strstr(locale,"utf8") || strstr(locale,"UTF8") + || strstr(locale,"utf-8") || strstr(locale,"UTF-8")) + use_utf8 = 1; + else + if (setlocale(LC_ALL, locale)) + use_utf8 = 0; + else { + ntfs_log_error("Invalid locale, encoding to UTF-8\n"); + use_utf8 = 1; + } + return 0; /* always successful */ +} + From 704519c75e6604f193f21f271da486b294f481ea Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 22 Dec 2008 00:11:28 +0000 Subject: [PATCH 240/328] set errno values for the new built-in UTF8 conversion functions --- libntfs-3g/unistr.c | 110 +++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 38 deletions(-) diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index 1898a451..3cd45dac 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -407,12 +407,14 @@ int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, */ /* - * Return the amount of 8-bit elements in UTF-8 needed (without - * the terminating null) to store a given UTF-16LE string. + * Return the amount of 8-bit elements in UTF-8 needed (without the terminating + * null) to store a given UTF-16LE string. + * + * Return -1 with errno set if string has invalid byte sequence or too long. */ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) { - int i; + int i, ret = -1; int count = 0; BOOL surrog; @@ -423,7 +425,8 @@ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_l if ((c >= 0xdc00) && (c < 0xe000)) { surrog = FALSE; count += 4; - } else goto fail; + } else + goto fail; } else if (c < 0x80) count++; @@ -439,14 +442,22 @@ static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_l else if (c >= 0xe000) #endif count += 3; - else goto fail; - if (count > outs_len) - goto fail; + else + goto fail; + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } } - if (surrog) goto fail; - return count; + if (surrog) + goto fail; + + ret = count; +out: + return ret; fail: - return -1; + errno = EILSEQ; + goto out; } /* @@ -455,12 +466,14 @@ fail: * @ins_len: length of input string in utf16 characters * @outs: on return contains the (allocated) output multibyte string * @outs_len: length of output buffer in bytes + * + * Return -1 with errno set if string has invalid byte sequence or too long. */ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, char **outs, int outs_len) { char *t; - int i, size; + int i, size, ret = -1; ntfschar halfpair; halfpair = 0; @@ -469,10 +482,9 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, size = utf16_to_utf8_size(ins, ins_len, outs_len); - if (size < 0) { - errno = ENAMETOOLONG; - goto fail; - } + if (size < 0) + goto out; + if (!*outs) *outs = ntfs_malloc((outs_len = size + 1)); @@ -488,7 +500,8 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); *t++ = 0x80 + (c & 63); halfpair = 0; - } else goto fail; + } else + goto fail; } else if (c < 0x80) { *t++ = c; } else { @@ -505,51 +518,70 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, *t++ = 0xe0 | (c >> 12); *t++ = 0x80 | ((c >> 6) & 0x3f); *t++ = 0x80 | (c & 0x3f); - } else goto fail; + } else + goto fail; } } *t = '\0'; - return t - *outs; + ret = t - *outs; +out: + return ret; fail: - return -1; + errno = EILSEQ; + goto out; } /* * Return the amount of 16-bit elements in UTF-16LE needed * (without the terminating null) to store given UTF-8 string. * - * Return -1 if it does not fit into PATH_MAX. + * Return -1 with errno set if it's longer than PATH_MAX or string is invalid. * * Note: This does not check whether the input sequence is a valid utf8 string, * and should be used only in context where such check is made! */ static int utf8_to_utf16_size(const char *s) { + int ret = -1; unsigned int byte; size_t count = 0; while ((byte = *((const unsigned char *)s++))) { - if (++count >= PATH_MAX || byte >= 0xF5) + if (++count >= PATH_MAX) goto fail; - if (!*s) break; - if (byte >= 0xC0) s++; - if (!*s) break; - if (byte >= 0xE0) s++; - if (!*s) break; + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; if (byte >= 0xF0) { s++; if (++count >= PATH_MAX) goto fail; } } - return count; + ret = count; +out: + return ret; fail: - return -1; + errno = ENAMETOOLONG; + goto out; } /* * This converts one UTF-8 sequence to cpu-endian Unicode value * within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF - * Returns the number of used utf8 bytes or -1 if sequence is invalid. + * + * Return the number of used utf8 bytes or -1 with errno set + * if sequence is invalid. */ static int utf8_to_unicode(u32 *wc, const char *s) { @@ -611,6 +643,7 @@ static int utf8_to_unicode(u32 *wc, const char *s) goto fail; } fail: + errno = EILSEQ; return -1; } @@ -619,18 +652,20 @@ fail: * @ins: input multibyte string buffer * @outs: on return contains the (allocated) output utf16 string * @outs_len: length of output buffer in utf16 characters + * + * Return -1 with errno set. */ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) { const char *t = ins; u32 wc; ntfschar *outpos; - int shorts = utf8_to_utf16_size(ins); + int shorts, ret = -1; - if (shorts < 0) { - errno = EILSEQ; + shorts = utf8_to_utf16_size(ins); + if (shorts < 0) goto fail; - } + if (!*outs) *outs = ntfs_malloc((shorts+1) * sizeof(ntfschar)); @@ -638,10 +673,8 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) while(1) { int m = utf8_to_unicode(&wc, t); - if (m < 0) { - errno = EILSEQ; + if (m < 0) goto fail; - } if (wc < 0x10000) *outpos++ = cpu_to_le16(wc); else { @@ -653,9 +686,10 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) break; t += m; } - return --outpos - *outs; + + ret = --outpos - *outs; fail: - return -1; + return ret; } /** From 2a004f09112897543ce667acd162393680ba2b4d Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 22 Dec 2008 00:16:19 +0000 Subject: [PATCH 241/328] ntfs_utf16_to_utf8, ntfs_utf8_to_utf16: check ntfs_malloc() return value --- libntfs-3g/unistr.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index 3cd45dac..2877722e 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -485,8 +485,12 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, if (size < 0) goto out; - if (!*outs) - *outs = ntfs_malloc((outs_len = size + 1)); + if (!*outs) { + outs_len = size + 1; + *outs = ntfs_malloc(outs_len); + if (!*outs) + goto out; + } t = *outs; @@ -666,8 +670,11 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) if (shorts < 0) goto fail; - if (!*outs) - *outs = ntfs_malloc((shorts+1) * sizeof(ntfschar)); + if (!*outs) { + *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); + if (!*outs) + goto fail; + } outpos = *outs; From 6718cf6de06b9c070a1059f07eebbc0c9924b2ef Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 22 Dec 2008 00:27:12 +0000 Subject: [PATCH 242/328] readdir returns error if a filename can't be utf8 converted instead of skipping it --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index dc8f5a64..fc0776ab 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -562,7 +562,7 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, if (ntfs_ucstombs(name, name_len, &filename, 0) < 0) { ntfs_log_perror("Skipping unrepresentable filename (inode %llu)", (unsigned long long)MREF(mref)); - return 0; + return -1; } if (ntfs_fuse_is_named_data_stream(filename)) { From 0fcb9b7f42538b1fbca96acd01c9107a38c65ec1 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 22 Dec 2008 00:34:15 +0000 Subject: [PATCH 243/328] ntfs_pathname_to_inode: log filename utf8 conversion error --- libntfs-3g/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 560c9ddd..f16254ca 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -475,7 +475,8 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, len = ntfs_mbstoucs(p, &unicode); if (len < 0) { - ntfs_log_debug("Couldn't convert name to Unicode: %s.\n", p); + ntfs_log_perror("Couldn't convert filename to Unicode: " + "'%s'.\n", p); err = errno; goto close; } else if (len > NTFS_MAX_NAME_LEN) { From 882a17a6ce473ea1dcea4af52583f810cee4fda2 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 22 Dec 2008 00:48:29 +0000 Subject: [PATCH 244/328] locale= mount option is not used anymore for filename characterset conversion --- src/ntfs-3g.8.in | 8 ++------ src/ntfs-3g.c | 12 +++--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 8ccfe455..b6a49fe0 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 2006-2008 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G 8 "April 2008" "ntfs-3g @VERSION@" +.TH NTFS-3G 8 "December 2008" "ntfs-3g @VERSION@" .SH NAME ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS @@ -99,11 +99,7 @@ Mount filesystem read\-only. Useful if Windows is hibernated or the NTFS journal file is unclean. .TP .BI locale= value -This option can be useful if your language specific locale environment -variables are not set correctly or at all in your operating system. -In such cases, the national characters can be made visible by using this -option. Please see more information about this topic at -http://ntfs-3g.org/support.html#locale +Prints informative and diagnostic messages in the set locale. .TP .B force Force the mounting even if the NTFS logfile is unclean. The logfile diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index fc0776ab..e968314c 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -151,11 +151,6 @@ static char def_opts[] = "silent,allow_other,nonempty,"; static ntfs_fuse_context_t *ctx; static u32 ntfs_sequence; -static const char *locale_msg = -"WARNING: Couldn't set locale to '%s' thus some file names may not\n" -" be correct or visible. Please see the potential solution at\n" -" http://ntfs-3g.org/support.html#locale\n"; - static const char *usage_msg = "\n" "%s %s %s %d - Third Generation NTFS Driver\n" @@ -165,8 +160,8 @@ static const char *usage_msg = "\n" "Usage: %s [-o option[,...]] \n" "\n" -"Options: ro (read-only mount), force, remove_hiberfile, locale=,\n" -" uid=, gid=, umask=, fmask=, dmask=, streams_interface=.\n" +"Options: ro (read-only mount), force, remove_hiberfile, uid=,\n" +" gid=, umask=, fmask=, dmask=, streams_interface=.\n" " Please see the details in the manual.\n" "\n" "Examples: ntfs-3g -o force /dev/sda1 /mnt/windows\n" @@ -1817,8 +1812,7 @@ static char *parse_mount_options(const char *orig_opts) } else if (!strcmp(opt, "locale")) { if (missing_option_value(val, "locale")) goto err_exit; - if (!setlocale(LC_ALL, val)) - ntfs_log_error(locale_msg, val); + setlocale(LC_ALL, val); } else if (!strcmp(opt, "streams_interface")) { if (missing_option_value(val, "streams_interface")) goto err_exit; From f620cd20ebfe47831a89c1b955de2f8d65b55dae Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 22 Dec 2008 01:28:30 +0000 Subject: [PATCH 245/328] ntfs_inode_lookup_by_name, ntfs_mft_get_parent_ref: log all errors --- libntfs-3g/dir.c | 65 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index f16254ca..a91e1aca 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -134,7 +134,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Index root attribute missing in directory inode " - "0x%llx", (unsigned long long)dir_ni->mft_no); + "%lld", (unsigned long long)dir_ni->mft_no); goto put_err_out; } /* Get to the index root value. */ @@ -143,7 +143,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, index_block_size = le32_to_cpu(ir->index_block_size); if (index_block_size < NTFS_BLOCK_SIZE || index_block_size & (index_block_size - 1)) { - ntfs_log_debug("Index block size %u is invalid.\n", + ntfs_log_error("Index block size %u is invalid.\n", (unsigned)index_block_size); goto put_err_out; } @@ -160,8 +160,11 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || (u8*)ie + le16_to_cpu(ie->key_length) > - index_end) + index_end) { + ntfs_log_error("Index entry out of bounds in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); goto put_err_out; + } /* * The last entry cannot contain a name. It can however contain * a pointer to a child node in the B+tree so we just break out. @@ -169,8 +172,11 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, if (ie->ie_flags & INDEX_ENTRY_END) break; - if (!le16_to_cpu(ie->length)) + if (!le16_to_cpu(ie->length)) { + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); goto put_err_out; + } /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. @@ -229,8 +235,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, /* Open the index allocation attribute. */ ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); if (!ia_na) { - ntfs_log_perror("Failed to open index allocation attribute. Directory " - "inode 0x%llx is corrupt or driver bug", + ntfs_log_perror("Failed to open index allocation (inode %lld)", (unsigned long long)dir_ni->mft_no); goto put_err_out; } @@ -268,7 +273,7 @@ descend_into_child_node: } if (sle64_to_cpu(ia->index_block_vcn) != vcn) { - ntfs_log_debug("Actual VCN (0x%llx) of index buffer is different " + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " "from expected VCN (0x%llx).\n", (long long)sle64_to_cpu(ia->index_block_vcn), (long long)vcn); @@ -276,7 +281,7 @@ descend_into_child_node: goto close_err_out; } if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " "has a size (%u) differing from the directory " "specified size (%u).\n", (long long)vcn, (unsigned long long)dir_ni->mft_no, @@ -287,7 +292,7 @@ descend_into_child_node: } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); if (index_end > (u8*)ia + index_block_size) { - ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory inode " + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " "0x%llx exceeds maximum size.\n", (long long)vcn, (unsigned long long)dir_ni->mft_no); errno = EIO; @@ -308,9 +313,9 @@ descend_into_child_node: sizeof(INDEX_ENTRY_HEADER) > index_end || (u8*)ie + le16_to_cpu(ie->key_length) > index_end) { - ntfs_log_debug("Index entry out of bounds in directory " - "inode 0x%llx.\n", - (unsigned long long)dir_ni->mft_no); + ntfs_log_error("Index entry out of bounds in directory " + "inode %lld.\n", + (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } @@ -323,6 +328,8 @@ descend_into_child_node: if (!le16_to_cpu(ie->length)) { errno = EIO; + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); goto close_err_out; } /* @@ -369,8 +376,8 @@ descend_into_child_node: */ if (ie->ie_flags & INDEX_ENTRY_NODE) { if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) { - ntfs_log_debug("Index entry with child node found in a leaf " - "node in directory inode 0x%llx.\n", + ntfs_log_error("Index entry with child node found in a leaf " + "node in directory inode %lld.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; @@ -379,8 +386,8 @@ descend_into_child_node: vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); if (vcn >= 0) goto descend_into_child_node; - ntfs_log_debug("Negative child node vcn in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + ntfs_log_error("Negative child node vcn in directory inode " + "0x%llx.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } @@ -456,7 +463,7 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, ascii = strdup(pathname); if (!ascii) { - ntfs_log_debug("Out of memory.\n"); + ntfs_log_error("Out of memory.\n"); err = ENOMEM; goto close; } @@ -634,21 +641,21 @@ static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) if (!ctx) return ERR_MREF(-1); if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_log_debug("No file name found in inode 0x%llx. Corrupt " - "inode.\n", (unsigned long long)ni->mft_no); + ntfs_log_error("No file name found in inode %lld\n", + (unsigned long long)ni->mft_no); goto err_out; } if (ctx->attr->non_resident) { - ntfs_log_debug("File name attribute must be resident. Corrupt inode " - "0x%llx.\n", (unsigned long long)ni->mft_no); + ntfs_log_error("File name attribute must be resident (inode " + "%lld)\n", (unsigned long long)ni->mft_no); goto io_err_out; } fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { - ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n", - (unsigned long long)ni->mft_no); + ntfs_log_error("Corrupt file name attribute in inode %lld.\n", + (unsigned long long)ni->mft_no); goto io_err_out; } mref = le64_to_cpu(fn->parent_directory); @@ -708,7 +715,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, vol = dir_ni->vol; - ntfs_log_trace("Entering for inode 0x%llx, *pos 0x%llx.\n", + ntfs_log_trace("Entering for inode %lld, *pos 0x%llx.\n", (unsigned long long)dir_ni->mft_no, (long long)*pos); /* Open the index allocation attribute. */ @@ -716,7 +723,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, if (!ia_na) { if (errno != ENOENT) { ntfs_log_perror("Failed to open index allocation attribute. " - "Directory inode 0x%llx is corrupt or bug", + "Directory inode %lld is corrupt or bug", (unsigned long long)dir_ni->mft_no); return -1; } @@ -766,7 +773,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Index root attribute missing in directory inode " - "0x%llx", (unsigned long long)dir_ni->mft_no); + "%lld", (unsigned long long)dir_ni->mft_no); goto dir_err_out; } /* Get to the index root value. */ @@ -933,7 +940,7 @@ find_next_index_buffer: goto dir_err_out; } if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld " "has a size (%u) differing from the directory " "specified size (%u).\n", (long long)ia_start >> index_vcn_size_bits, @@ -945,7 +952,7 @@ find_next_index_buffer: index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); if (index_end > (u8*)ia + index_block_size) { ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " - "0x%llx exceeds maximum size.\n", + "%lld exceeds maximum size.\n", (long long)ia_start >> index_vcn_size_bits, (unsigned long long)dir_ni->mft_no); goto dir_err_out; @@ -967,7 +974,7 @@ find_next_index_buffer: (u8*)ie + le16_to_cpu(ie->key_length) > index_end) { ntfs_log_error("Index entry out of bounds in directory inode " - "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + "%lld.\n", (unsigned long long)dir_ni->mft_no); goto dir_err_out; } /* The last entry cannot contain a name. */ From 0971a60948fafb28e902ceb7da07fd95d9431b1a Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 22 Dec 2008 01:33:49 +0000 Subject: [PATCH 246/328] release 1.5222-RC --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 99299c1d..f83288ef 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.5130],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="43" +AC_INIT([ntfs-3g],[1.5222-RC],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="47" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From f70df990c572b95a6625c540734e0b20af0c2810 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 8 Jan 2009 00:13:42 +0000 Subject: [PATCH 247/328] OS X: support getting/setting file creation timestmap (Erik Larsson) --- src/ntfs-3g.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index e968314c..cb68da99 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -3,7 +3,8 @@ * * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel - * Copyright (c) 2006-2008 Szabolcs Szakacsits + * Copyright (c) 2006-2009 Szabolcs Szakacsits + * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. * @@ -342,6 +343,67 @@ static void set_fuse_error(int *err) *err = -errno; } +#if defined(__APPLE__) || defined(__DARWIN__) +static void *ntfs_macfuse_init(struct fuse_conn_info *conn) +{ + FUSE_ENABLE_XTIMES(conn); + /* FUSE_ENABLE_SETVOLNAME(conn); Later, when implemented */ + return NULL; +} + +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; +} +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) { int res = 0; @@ -1598,6 +1660,12 @@ static struct fuse_operations ntfs_3g_ops = { .removexattr = ntfs_fuse_removexattr, .listxattr = ntfs_fuse_listxattr, #endif /* HAVE_SETXATTR */ +#if defined(__APPLE__) || defined(__DARWIN__) + .init = ntfs_macfuse_init, + /* MacFUSE extensions. */ + .getxtimes = ntfs_macfuse_getxtimes, + .setcrtime = ntfs_macfuse_setcrtime, +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ }; static int ntfs_fuse_init(void) From 4869dc96d5d1a2a33bc561870920d4b06c1ab0ed Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 8 Jan 2009 02:52:12 +0000 Subject: [PATCH 248/328] OS X: remove FUSE_ENABLE_SETVOLNAME comment --- src/ntfs-3g.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index cb68da99..515f3704 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -347,7 +347,6 @@ static void set_fuse_error(int *err) static void *ntfs_macfuse_init(struct fuse_conn_info *conn) { FUSE_ENABLE_XTIMES(conn); - /* FUSE_ENABLE_SETVOLNAME(conn); Later, when implemented */ return NULL; } From e4bf04aeeaa8e6fb4f503011a1267c28d68ae5e4 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 15 Jan 2009 16:56:10 +0000 Subject: [PATCH 249/328] ntfs_attr_open: fix named attribute memory leak in error cases (Jean-Pierre Andre) --- libntfs-3g/attrib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 77ed8a48..a11299c8 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -376,6 +376,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, { ntfs_attr_search_ctx *ctx; ntfs_attr *na = NULL; + ntfschar *newname = NULL; ATTR_RECORD *a; BOOL cs; @@ -393,6 +394,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, name = ntfs_ucsndup(name, name_len); if (!name) goto err_out; + newname = name; } ctx = ntfs_attr_get_search_ctx(ni, NULL); @@ -410,6 +412,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, a->name_offset)), a->name_length); if (!name) goto put_err_out; + newname = name; name_len = a->name_length; } else { name = AT_UNNAMED; @@ -471,6 +474,7 @@ out: put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: + free(newname); free(na); na = NULL; goto out; From 987e6f7bf94ae77a14e91736bff06508ad32c196 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 19 Jan 2009 02:35:53 +0000 Subject: [PATCH 250/328] fix extended attribute corruption if new size was smaller (Jean-Pierre Andre) --- src/ntfs-3g.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 515f3704..fd649f32 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1558,6 +1558,11 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = -errno; goto exit; } + } else { + if (ntfs_attr_truncate(na, (s64)size)) { + res = -errno; + goto exit; + } } res = ntfs_attr_pwrite(na, 0, size, value); if (res != (s64) size) From faf970786407963962aea9eb7be04c8706c2dffc Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 21 Jan 2009 23:51:02 +0000 Subject: [PATCH 251/328] move conditional PATH_MAX define to compat.h --- include/ntfs-3g/compat.h | 8 ++++++++ libntfs-3g/volume.c | 7 ++----- src/ntfs-3g.c | 5 +---- src/ntfs-3g.probe.c | 7 ++----- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/include/ntfs-3g/compat.h b/include/ntfs-3g/compat.h index 148f8b75..d6620593 100644 --- a/include/ntfs-3g/compat.h +++ b/include/ntfs-3g/compat.h @@ -3,6 +3,7 @@ * * Copyright (c) 2002 Richard Russon * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2008-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -26,6 +27,13 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif #ifndef HAVE_FFS extern int ffs(int i); diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e4ba6b56..cb152b4d 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -2,7 +2,7 @@ * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2002-2009 Szabolcs Szakacsits * Copyright (c) 2004-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -53,6 +53,7 @@ #include #endif +#include "compat.h" #include "volume.h" #include "attrib.h" #include "mft.h" @@ -66,10 +67,6 @@ #include "logging.h" #include "misc.h" -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - const char *ntfs_home = "Ntfs-3g news, support and information: http://ntfs-3g.org\n"; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index fd649f32..a7b2e579 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4,6 +4,7 @@ * 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. @@ -93,10 +94,6 @@ #include "ntfstime.h" #include "misc.h" -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - typedef enum { FSTYPE_NONE, FSTYPE_UNKNOWN, diff --git a/src/ntfs-3g.probe.c b/src/ntfs-3g.probe.c index be265dd7..592abd76 100644 --- a/src/ntfs-3g.probe.c +++ b/src/ntfs-3g.probe.c @@ -1,7 +1,7 @@ /** * ntfs-3g.probe - Probe NTFS volume mountability * - * Copyright (c) 2007 Szabolcs Szakacsits + * Copyright (c) 2007-2009 Szabolcs Szakacsits * * 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 @@ -32,13 +32,10 @@ #endif #include +#include "compat.h" #include "volume.h" #include "misc.h" -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - typedef enum { PROBE_UNSET, PROBE_READONLY, From 8277f343c0cd6b22c750adabffccc329d156b857 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 21 Jan 2009 23:53:08 +0000 Subject: [PATCH 252/328] fix PATH_MAX undefined compilation error on Solaris --- libntfs-3g/unistr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index 2877722e..e3dac625 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -2,8 +2,8 @@ * unistr.c - Unicode string handling. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2002-2008 Szabolcs Szakacsits - * Copyright (c) 2008 Jean-Pierre Andre + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2008-2009 Jean-Pierre Andre * Copyright (c) 2008 Bernhard Kaindl * * This program/include file is free software; you can redistribute it and/or @@ -45,6 +45,7 @@ #include #endif +#include "compat.h" #include "attrib.h" #include "types.h" #include "unistr.h" From 8b4e66c23b98cc1464780fb6d87c11cb4237c465 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 22 Jan 2009 01:01:04 +0000 Subject: [PATCH 253/328] change: support the "user." extended attributes namespace by default on Linux --- src/ntfs-3g.8.in | 2 +- src/ntfs-3g.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index b6a49fe0..c398a0c1 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -167,7 +167,7 @@ to, one of \fBnone\fR, \fBwindows\fR or \fBxattr\fR. If the option is set to to \fBwindows\fR, then the user can access them just like in Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named data streams are mapped to xattrs and user can manipulate them using \fB{get,set}fattr\fR -utilities. The default is \fBnone\fR. +utilities. The default is \fBxattr\fR. .TP .B debug Makes ntfs-3g to not detach from terminal and print a lot of debug output from diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a7b2e579..f5586508 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1678,7 +1678,11 @@ static int ntfs_fuse_init(void) *ctx = (ntfs_fuse_context_t) { .uid = getuid(), .gid = getgid(), - .streams = NF_STREAMS_INTERFACE_NONE, +#if defined(linux) + .streams = NF_STREAMS_INTERFACE_XATTR, +#else + .streams = NF_STREAMS_INTERFACE_NONE, +#endif }; return 0; } From 318714058c963560c6ff30cdffcea6bc3445f667 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 22 Jan 2009 01:38:18 +0000 Subject: [PATCH 254/328] release 2009.1 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index f83288ef..ffa330c7 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[1.5222-RC],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="47" +AC_INIT([ntfs-3g],[2009.1],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="48" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 3aaaf459ee4409ed05837147fb338a4f6085c12b Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 22 Jan 2009 12:13:49 +0000 Subject: [PATCH 255/328] revert 'change: support the "user." extended attributes namespace ...' change --- src/ntfs-3g.8.in | 2 +- src/ntfs-3g.c | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index c398a0c1..b6a49fe0 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -167,7 +167,7 @@ to, one of \fBnone\fR, \fBwindows\fR or \fBxattr\fR. If the option is set to to \fBwindows\fR, then the user can access them just like in Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named data streams are mapped to xattrs and user can manipulate them using \fB{get,set}fattr\fR -utilities. The default is \fBxattr\fR. +utilities. The default is \fBnone\fR. .TP .B debug Makes ntfs-3g to not detach from terminal and print a lot of debug output from diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index f5586508..a7b2e579 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1678,11 +1678,7 @@ static int ntfs_fuse_init(void) *ctx = (ntfs_fuse_context_t) { .uid = getuid(), .gid = getgid(), -#if defined(linux) - .streams = NF_STREAMS_INTERFACE_XATTR, -#else - .streams = NF_STREAMS_INTERFACE_NONE, -#endif + .streams = NF_STREAMS_INTERFACE_NONE, }; return 0; } From 33432948aab9ea26fb27ec23fb48a3f18a2ba0be Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 22 Jan 2009 12:14:43 +0000 Subject: [PATCH 256/328] release 2009.1.1 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ffa330c7..827dd076 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2009.1],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[2009.1.1],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="48" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) From b6d78e9c7e376340ff592c69d9f76000f8173ef2 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 29 Jan 2009 00:05:17 +0000 Subject: [PATCH 257/328] ntfs_fuse_setxattr: return EOPNOTSUPP if an extended attribute namespace isn't supported --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a7b2e579..812932fa 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1527,7 +1527,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, return -EOPNOTSUPP; if (strncmp(name, nf_ns_xattr_preffix, nf_ns_xattr_preffix_len) || strlen(name) == (size_t)nf_ns_xattr_preffix_len) - return -EACCES; + return -EOPNOTSUPP; ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; From 21cca783ba7df88a95e70707d15a1b8080abc55b Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 29 Jan 2009 16:04:16 +0000 Subject: [PATCH 258/328] Credit major contributors --- src/ntfs-3g.8.in | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index b6a49fe0..41c0fa33 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -1,9 +1,9 @@ .\" Copyright (c) 2005-2006 Yura Pakhuchiy. .\" Copyright (c) 2005 Richard Russon. -.\" Copyright (c) 2006-2008 Szabolcs Szakacsits. +.\" Copyright (c) 2006-2009 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G 8 "December 2008" "ntfs-3g @VERSION@" +.TH NTFS-3G 8 "February 2009" "ntfs-3g @VERSION@" .SH NAME ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS @@ -238,7 +238,9 @@ Linux-NTFS team developer Szabolcs Szakacsits (szaka@ntfs-3g.org). Several people made heroic efforts, often over five or more years which resulted the ntfs-3g driver. Most importantly they are Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, -Yuval Fledel, and the author of the groundbreaking FUSE filesystem development +Yuval Fledel, Jean-Pierre Andre, Alon Bar-Lev, Dominique L Bouix, +Csaba Henk, Bernhard Kaindl, Erik Larsson, Alejandro Pulver, and +the author of the groundbreaking FUSE filesystem development framework, Miklos Szeredi. .SH SEE ALSO .BR ntfs-3g.probe (8), From cd4666ab7890d143979043153dc9b0141833aa1a Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 31 Jan 2009 15:48:48 +0000 Subject: [PATCH 259/328] implement MacFUSE setbkuptime not to break some apps (Erik Larsson) --- src/ntfs-3g.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 812932fa..d191f098 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -343,11 +343,11 @@ static void set_fuse_error(int *err) #if defined(__APPLE__) || defined(__DARWIN__) static void *ntfs_macfuse_init(struct fuse_conn_info *conn) { - FUSE_ENABLE_XTIMES(conn); - return NULL; + FUSE_ENABLE_XTIMES(conn); + return NULL; } -static int ntfs_macfuse_getxtimes(const char *org_path, +static int ntfs_macfuse_getxtimes(const char *org_path, struct timespec *bkuptime, struct timespec *crtime) { int res = 0; @@ -392,12 +392,33 @@ int ntfs_macfuse_setcrtime(const char *path, const struct timespec *tv) 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; + + /* + * Doing nothing while pretending to do something. NTFS has no backup + * time. If this function is not implemented then some apps break. + */ + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} #endif /* defined(__APPLE__) || defined(__DARWIN__) */ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) @@ -1662,10 +1683,11 @@ static struct fuse_operations ntfs_3g_ops = { .listxattr = ntfs_fuse_listxattr, #endif /* HAVE_SETXATTR */ #if defined(__APPLE__) || defined(__DARWIN__) - .init = ntfs_macfuse_init, + .init = ntfs_macfuse_init, /* MacFUSE extensions. */ - .getxtimes = ntfs_macfuse_getxtimes, - .setcrtime = ntfs_macfuse_setcrtime, + .getxtimes = ntfs_macfuse_getxtimes, + .setcrtime = ntfs_macfuse_setcrtime, + .setbkuptime = ntfs_macfuse_setbkuptime #endif /* defined(__APPLE__) || defined(__DARWIN__) */ }; From 91c57a8f08efd46c0e32c7ac1286477d8e9f7262 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 1 Feb 2009 08:16:50 +0000 Subject: [PATCH 260/328] document generic mount options accepted --- src/ntfs-3g.8.in | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 41c0fa33..0bd9e994 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -73,7 +73,12 @@ Named data streams act like normals files, so you can read from them, write to them and even delete them (using rm). You can list all the named data streams a file has by getting the "ntfs.streams.list" extended attribute. .SH OPTIONS -Below is a summary of the options that \fBntfs-3g\fR accepts. +Most of the generic mount options described in +. BR mount (8) +are +supported (ro, rw, suid, nosuid, dev, nodev, exec, noexec). +Below is a summary of the options that \fBntfs-3g\fR additionally +accepts. .TP \fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP Set the owner and the group of files and directories. The values are numerical. From 838af60ff1b66f1b2510531124f396288e76549a Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 1 Feb 2009 12:45:44 +0000 Subject: [PATCH 261/328] manual: merge authors and thanks sections --- src/ntfs-3g.8.in | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 0bd9e994..cfba1f93 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -233,13 +233,7 @@ the software then please send an email describing it in detail. You can contact the development team on the ntfs\-3g\-devel@lists.sf.net address. -.SH AUTHORS -.B ntfs-3g -was based on and a major improvement to ntfsmount and libntfs which were -written by Yura Pakhuchiy and the Linux-NTFS team. The improvements were -made, the ntfs-3g project was initiated and currently led by long time -Linux-NTFS team developer Szabolcs Szakacsits (szaka@ntfs-3g.org). -.SH THANKS +.SH ACKNOWLEDGEMENT Several people made heroic efforts, often over five or more years which resulted the ntfs-3g driver. Most importantly they are Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, From 84ef1c5f9d9cea4d64cf70d396a770eadefa6c49 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 1 Feb 2009 14:59:56 +0000 Subject: [PATCH 262/328] manual update --- src/ntfs-3g.8.in | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index cfba1f93..fcc7f10a 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -8,12 +8,12 @@ ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS .B ntfs-3g -.I volume mount_point \fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point .br .B mount \-t ntfs-3g -.I volume mount_point \fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point .SH DESCRIPTION \fBntfs-3g\fR is an NTFS driver, which can create, remove, rename, move files, directories, hard links, and @@ -43,6 +43,10 @@ options. Windows users have full access to the files created by .B ntfs-3g. .PP +Full ownership and permission support, including Windows user mapping +and POSIX file system compliance, is provided by the Advanced NTFS-3G +driver. +.PP If .B ntfs-3g is set setuid-root then non-root users will @@ -103,9 +107,6 @@ means full access to everybody. Mount filesystem read\-only. Useful if Windows is hibernated or the NTFS journal file is unclean. .TP -.BI locale= value -Prints informative and diagnostic messages in the set locale. -.TP .B force Force the mounting even if the NTFS logfile is unclean. The logfile will be unconditionally cleared. Use this option with caution and for @@ -160,6 +161,9 @@ limited anyway to 32 pages (which is 128kbyte on i386). Do nothing on chmod and chown operations, but do not return error. This option is on by default. .TP +.BI locale= value +Prints informative and diagnostic messages in the set locale. +.TP .B no_def_opts By default ntfs-3g acts as "silent" was passed to it, this option cancel this behaviour. @@ -181,7 +185,7 @@ libntfs-3g and FUSE. .B no_detach Same as above but with less debug output. .SH EXAMPLES -Mount /dev/sda1 to /mnt/windows: +Mount /dev/sda1 to /mnt/windows (make sure /mnt/windows exists): .RS .sp .B ntfs-3g /dev/sda1 /mnt/windows @@ -197,7 +201,7 @@ Read\-only mount /dev/sda5 to /home/user/mnt and make user with uid 1000 to be the owner of all files: .RS .sp -.B ntfs-3g /dev/sda5 /home/user/mnt \-o ro,uid=1000 +.B ntfs-3g \-o ro,uid=1000 /dev/sda5 /home/user/mnt .sp .RE /etc/fstab entry for the above: @@ -227,12 +231,8 @@ Please see http://ntfs-3g.org/support.html .sp .RE -for common questions and known issues. -If you would find a new one in the latest release of -the software then please send an email describing it -in detail. You can contact the -development team on the ntfs\-3g\-devel@lists.sf.net -address. +for common questions and known issues. You can contact the development +team on the ntfs\-3g\-devel@lists.sf.net address. .SH ACKNOWLEDGEMENT Several people made heroic efforts, often over five or more years which resulted the ntfs-3g driver. Most importantly they are @@ -245,4 +245,5 @@ framework, Miklos Szeredi. .BR ntfs-3g.probe (8), .BR ntfsprogs (8), .BR attr (5), -.BR getfattr (1) +.BR getfattr (1), +.BR setfattr (1) From 5157bd2fd361d51cb5bd25353a32c6e32da0f62d Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 13:00:04 +0000 Subject: [PATCH 263/328] add recover/norecover options, obsolete force option, recover from unclean journal by default --- include/ntfs-3g/volume.h | 6 +++--- libntfs-3g/logfile.c | 6 +++--- libntfs-3g/volume.c | 25 +++++-------------------- src/ntfs-3g.8.in | 23 ++++++++++++++--------- src/ntfs-3g.c | 38 ++++++++++++++++++++++---------------- 5 files changed, 47 insertions(+), 51 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 4f6f004b..8ebef49f 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -4,7 +4,7 @@ * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2005-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -52,8 +52,8 @@ #define MS_EXCLUSIVE 0x08000000 -#ifndef MS_FORCE -#define MS_FORCE 0x10000000 +#ifndef MS_RECOVER +#define MS_RECOVER 0x10000000 #endif #define MS_IGNORE_HIBERFILE 0x20000000 diff --git a/libntfs-3g/logfile.c b/libntfs-3g/logfile.c index 3fb04f3d..277ad142 100644 --- a/libntfs-3g/logfile.c +++ b/libntfs-3g/logfile.c @@ -3,7 +3,7 @@ * * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -676,8 +676,8 @@ BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) */ if (ra->client_in_use_list != LOGFILE_NO_CLIENT && !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { - ntfs_log_error("$LogFile indicates unclean shutdown (%d, %d)\n", - le16_to_cpu(ra->client_in_use_list), + ntfs_log_error("The disk contains an unclean file system (%d, " + "%d).\n", le16_to_cpu(ra->client_in_use_list), le16_to_cpu(ra->flags)); return FALSE; } diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index cb152b4d..3ae3dc9a 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -94,14 +94,8 @@ static const char *hibernated_volume_msg = "\n"; static const char *unclean_journal_msg = -"Mount is denied because NTFS is marked to be in use. Choose one action:\n" -"\n" -"Choice 1: If you have Windows then disconnect the external devices by\n" -" clicking on the 'Safely Remove Hardware' icon in the Windows\n" -" taskbar then shutdown Windows cleanly.\n" -"\n" -"Choice 2: If you don't have Windows then you can use the 'force' option for\n" -" your own responsibility. For example type on the command line:\n"; +"Write access is denied because the disk wasn't safely powered\n" +"off and the 'norecover' mount option was specified.\n"; static const char *opened_volume_msg = "Mount is denied because the NTFS volume is already exclusively opened.\n" @@ -119,14 +113,6 @@ static const char *access_denied_msg = "and the mounting user ID. More explanation is provided at\n" "http://ntfs-3g.org/support.html#unprivileged\n"; -static const char *forced_mount_msg = -"\n" -" mount -t ntfs-3g -o force %s %s\n" -"\n" -" Or add the option to the relevant row in the /etc/fstab file:\n" -"\n" -" %s %s ntfs-3g force 0 0\n"; - /** * ntfs_volume_alloc - Create an NTFS volume object and initialise it * @@ -1115,9 +1101,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) ntfs_volume_check_hiberfile(vol, 1) < 0) goto error_exit; if (ntfs_volume_check_logfile(vol) < 0) { - if (!(flags & MS_FORCE)) + if (!(flags & MS_RECOVER)) goto error_exit; - ntfs_log_info("WARNING: Forced mount, reset $LogFile.\n"); + ntfs_log_info("The file system wasn't safely " + "closed on Windows. Fixing.\n"); if (ntfs_logfile_reset(vol)) goto error_exit; } @@ -1548,8 +1535,6 @@ void ntfs_mount_error(const char *volume, const char *mntpoint, int err) break; case NTFS_VOLUME_UNCLEAN_UNMOUNT: ntfs_log_error("%s", unclean_journal_msg); - ntfs_log_error(forced_mount_msg, volume, mntpoint, - volume, mntpoint); break; case NTFS_VOLUME_LOCKED: ntfs_log_error("%s", opened_volume_msg); diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index fcc7f10a..3cf5af1f 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -104,13 +104,7 @@ present. The value is given in octal. The default value is 0 which means full access to everybody. .TP .B ro -Mount filesystem read\-only. Useful if Windows is hibernated or the -NTFS journal file is unclean. -.TP -.B force -Force the mounting even if the NTFS logfile is unclean. The logfile -will be unconditionally cleared. Use this option with caution and for -your own responsibility. +Mount filesystem read\-only. Useful if Windows is hibernated. .TP .B remove_hiberfile Unlike in case of read-only mount, the read-write mount is denied if @@ -120,6 +114,11 @@ hibernation file. Please note, this means that the saved Windows session will be completely lost. Use this option for your own responsibility. .TP +.B recover, norecover +Recover and repair a corrupted or inconsistent +NTFS volume if it's possible. The default behaviour is +.B recover. +.TP .B atime, noatime, relatime The .B atime @@ -178,9 +177,15 @@ file:stream). If it's set to \fBxattr\fR, then the named data streams are mapped to xattrs and user can manipulate them using \fB{get,set}fattr\fR utilities. The default is \fBnone\fR. .TP +.B force +This mount option is not used anymore. It was superseded by the +.B recover +and +.B norecover +options. +.TP .B debug -Makes ntfs-3g to not detach from terminal and print a lot of debug output from -libntfs-3g and FUSE. +Makes ntfs-3g to not detach from terminal and print a lot of driver debug output. .TP .B no_detach Same as above but with less debug output. diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index d191f098..81924f11 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -129,7 +129,7 @@ typedef struct { BOOL ro; BOOL show_sys_files; BOOL silent; - BOOL force; + BOOL recover; BOOL hiberfile; BOOL debug; BOOL no_detach; @@ -153,16 +153,18 @@ static const char *usage_msg = "\n" "%s %s %s %d - Third Generation NTFS Driver\n" "\n" -"Copyright (C) 2006-2008 Szabolcs Szakacsits\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), force, remove_hiberfile, uid=,\n" -" gid=, umask=, fmask=, dmask=, streams_interface=.\n" -" Please see the details in the manual.\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" -"Examples: ntfs-3g -o force /dev/sda1 /mnt/windows\n" +"Example: ntfs-3g /dev/sda1 /mnt/windows\n" "\n" "%s"; @@ -1698,9 +1700,12 @@ static int ntfs_fuse_init(void) return -1; *ctx = (ntfs_fuse_context_t) { - .uid = getuid(), - .gid = getgid(), + .uid = getuid(), + .gid = getgid(), .streams = NF_STREAMS_INTERFACE_NONE, + .atime = ATIME_RELATIVE, + .silent = TRUE, + .recover = TRUE }; return 0; } @@ -1713,8 +1718,8 @@ static int ntfs_open(const char *device) flags |= MS_EXCLUSIVE; if (ctx->ro) flags |= MS_RDONLY; - if (ctx->force) - flags |= MS_FORCE; + if (ctx->recover) + flags |= MS_RECOVER; if (ctx->hiberfile) flags |= MS_IGNORE_HIBERFILE; @@ -1814,9 +1819,6 @@ static char *parse_mount_options(const char *orig_opts) return NULL; } - ctx->silent = TRUE; - ctx->atime = ATIME_RELATIVE; - s = options; while (s && *s && (val = strsep(&s, ","))) { opt = strsep(&val, "="); @@ -1892,10 +1894,14 @@ static char *parse_mount_options(const char *orig_opts) if (bogus_option_value(val, "silent")) goto err_exit; ctx->silent = TRUE; - } else if (!strcmp(opt, "force")) { - if (bogus_option_value(val, "force")) + } else if (!strcmp(opt, "recover")) { + if (bogus_option_value(val, "recover")) goto err_exit; - ctx->force = TRUE; + 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; From 24e47ff99958d543642ba8aab84fe283a3078c6b Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 13:50:18 +0000 Subject: [PATCH 264/328] calloc -> ntfs_calloc --- libntfs-3g/attrib.c | 2 +- libntfs-3g/inode.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index a11299c8..2b4f87bd 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -387,7 +387,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, errno = EINVAL; goto out; } - na = calloc(sizeof(ntfs_attr), 1); + na = ntfs_calloc(sizeof(ntfs_attr)); if (!na) goto out; if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 6ab5b448..4dd74531 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -87,7 +87,7 @@ static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) { ntfs_inode *ni; - ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode)); + ni = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode)); if (ni) ni->vol = vol; return ni; From 18213bb6a9fa9318a995380e89eb1e2d640d223c Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 13:55:13 +0000 Subject: [PATCH 265/328] ntfs_extent_inode_open: remove redundant logging --- libntfs-3g/inode.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 4dd74531..ae2fe339 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -424,11 +424,8 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) ni = __ntfs_inode_allocate(base_ni->vol); if (!ni) goto out; - if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, - NULL)) { - ntfs_log_perror("ntfs_file_record_read failed #2"); + if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) goto err_out; - } ni->mft_no = mft_no; ni->nr_extents = -1; ni->base_ni = base_ni; From 501217f1df046179251696c3ff488b291d3d64cf Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 19:32:15 +0000 Subject: [PATCH 266/328] ntfs_mst_post_read_fixup, ntfs_mst_pre_write_fixup: log all errors --- libntfs-3g/mst.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/mst.c b/libntfs-3g/mst.c index 4cd1f154..b6beb39d 100644 --- a/libntfs-3g/mst.c +++ b/libntfs-3g/mst.c @@ -2,7 +2,7 @@ * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov - * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2006-2009 Szabolcs Szakacsits * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -63,6 +63,7 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) (u32)(usa_ofs + (usa_count * 2)) > size || (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } /* Position of usn in update sequence array. */ @@ -91,6 +92,7 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) */ b->magic = magic_BAAD; errno = EIO; + ntfs_log_perror("Incomplete multi-sector transfer"); return -1; } data_pos += NTFS_BLOCK_SIZE/sizeof(u16); @@ -142,6 +144,7 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) if (!b || ntfs_is_baad_record(b->magic) || ntfs_is_hole_record(b->magic)) { errno = EINVAL; + ntfs_log_perror("%s: bad argument", __FUNCTION__); return -1; } /* Setup the variables. */ @@ -153,6 +156,7 @@ int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) (u32)(usa_ofs + (usa_count * 2)) > size || (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } /* Position of usn in update sequence array. */ From eb27d2059f913f8c2215e4ac01cadb0c486ac2f2 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 20:04:27 +0000 Subject: [PATCH 267/328] ntfs_attr_find: log all errors --- libntfs-3g/attrib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 2b4f87bd..c3178600 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1698,6 +1698,7 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, } else { if (name && name != AT_UNNAMED) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } vol = NULL; @@ -1809,8 +1810,9 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, } } } - ntfs_log_debug("ntfs_attr_find(): File is corrupt. Run chkdsk.\n"); errno = EIO; + ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, + ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1); return -1; } From 6a3da7101b6b962f9da6ce4e3c69521fd1302813 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 20:06:29 +0000 Subject: [PATCH 268/328] ntfs_attr_lookup: log all errors --- libntfs-3g/attrib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index c3178600..4579bd4e 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2334,6 +2334,7 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || !vol->upcase || !vol->upcase_len))) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); goto out; } From a400e4c7f0f22701a42bdf65070b30bdeee680d9 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 20:16:48 +0000 Subject: [PATCH 269/328] ntfs_external_attr_find: log all errors --- libntfs-3g/attrib.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 4579bd4e..d0d06e70 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1980,6 +1980,7 @@ find_attr_list_attr: /* Check for bogus calls. */ if (name || name_len || val || val_len || lowest_vcn) { errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); return -1; } @@ -2010,9 +2011,9 @@ find_attr_list_attr: if (errno != ENOENT) return rc; - /* Not found?!? Absurd! Must be a bug... )-: */ - ntfs_log_error("Extant attribute list wasn't found\n"); - errno = EINVAL; + /* Not found?!? Absurd! */ + errno = EIO; + ntfs_log_error("Attribute list wasn't found"); return -1; } } From ff7940b7e7a9badc31925114daacb862caaffbcc Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 20:29:27 +0000 Subject: [PATCH 270/328] ntfs_external_attr_find: remove redundant error logging --- libntfs-3g/attrib.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index d0d06e70..eab45a2f 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2128,10 +2128,8 @@ is_enumeration: /* We want an extent record. */ ni = ntfs_extent_inode_open(base_ni, al_entry->mft_reference); - if (!ni) { - ntfs_log_perror("Failed to map extent inode"); + if (!ni) break; - } ctx->ntfs_ino = ni; ctx->mrec = ni->mrec; } From 27dfa1a10539cb42aa47097acd321b6adffce76f Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 10 Feb 2009 21:56:48 +0000 Subject: [PATCH 271/328] ntfs_external_attr_find: fix crash when extent inode can't be opened (Erik Larsson, Szabolcs Szakacsits) --- libntfs-3g/attrib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index eab45a2f..39a6a3cc 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2199,7 +2199,7 @@ do_next_attr: ctx->attr = ctx->base_attr; } errno = EIO; - ntfs_log_perror("Inode is corrupt (%lld)", (unsigned long long)ni->mft_no); + ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no); return -1; not_found: /* From 901c3a7227bb4c9333eae1fc5582034aea7fa899 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 11 Feb 2009 00:36:27 +0000 Subject: [PATCH 272/328] ntfs_mft_records_read: log all errors --- libntfs-3g/mft.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index d638cc51..32a5e11c 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -105,11 +105,9 @@ int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, if (br != count) { if (br != -1) errno = EIO; - if (br >= 0) - ntfs_log_debug("Error: partition is smaller than it should " - "be!\n"); - else - ntfs_log_perror("Error reading $Mft record(s)"); + ntfs_log_perror("Failed to read of MFT, mft=%llu count=%lld " + "br=%lld", (long long)m, (long long)count, + (long long)br); return -1; } return 0; From 29381f068ef33dcdc44b4ca36970a0b47c1bd560 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 11 Feb 2009 00:46:01 +0000 Subject: [PATCH 273/328] remove redundant ntfs_mft_record_read error loggings --- libntfs-3g/mft.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 32a5e11c..b73e9b21 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -288,10 +288,9 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, if (!m) return -1; } - if (ntfs_mft_record_read(vol, mref, m)) { - ntfs_log_perror("ntfs_mft_record_read failed"); + if (ntfs_mft_record_read(vol, mref, m)) goto err_out; - } + if (ntfs_mft_record_check(vol, mref, m)) goto err_out; @@ -1402,7 +1401,6 @@ found_free_rec: goto undo_mftbmp_alloc; if (ntfs_mft_record_read(vol, bit, m)) { - ntfs_log_perror("Error reading mft %lld #2", (long long)bit); free(m); goto undo_mftbmp_alloc; } @@ -1705,7 +1703,6 @@ found_free_rec: goto undo_mftbmp_alloc; if (ntfs_mft_record_read(vol, bit, m)) { - ntfs_log_perror("Error reading mft %lld", (long long)bit); free(m); goto undo_mftbmp_alloc; } From a682c3662532a80f37081abb7132b36c6c90c929 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 11 Feb 2009 21:38:49 +0000 Subject: [PATCH 274/328] change: support the "user." extended attribute namespace by default on Linux --- src/ntfs-3g.8.in | 2 +- src/ntfs-3g.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 3cf5af1f..532510e3 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -175,7 +175,7 @@ to, one of \fBnone\fR, \fBwindows\fR or \fBxattr\fR. If the option is set to to \fBwindows\fR, then the user can access them just like in Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named data streams are mapped to xattrs and user can manipulate them using \fB{get,set}fattr\fR -utilities. The default is \fBnone\fR. +utilities. The default is \fBxattr\fR on Linux, \fBnone\fR on other OSes. .TP .B force This mount option is not used anymore. It was superseded by the diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 81924f11..f1b493af 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1702,7 +1702,11 @@ static int ntfs_fuse_init(void) *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 From 1600d6917a99e7c701d4d56cfecd7456cb07883e Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 11 Feb 2009 21:41:49 +0000 Subject: [PATCH 275/328] release 2009.2.1 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 827dd076..8ea3430f 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2009.1.1],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="48" +AC_INIT([ntfs-3g],[2009.2.1],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="49" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From c459836f2f6957aa00a0ec0b671bdc67eec4cccf Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 16 Feb 2009 22:32:22 +0000 Subject: [PATCH 276/328] ntfs_attr_pwrite: don't convert EIO to EOPNOTSUPP --- libntfs-3g/attrib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 39a6a3cc..2d943b8e 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1426,7 +1426,6 @@ rl_err_out: * TODO: Need to try to change initialized_size. If it * succeeds goto done, otherwise goto err_out. (AIA) */ - errno = EOPNOTSUPP; goto err_out; } goto done; From 5a7cc7f969c7d8d8af18c54e2084a1731ecfdc2c Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 21 Feb 2009 19:49:15 +0000 Subject: [PATCH 277/328] remove unused fuse_reply_iov --- include/fuse-lite/fuse_lowlevel.h | 13 ------------- libfuse-lite/fuse_lowlevel.c | 18 ------------------ 2 files changed, 31 deletions(-) diff --git a/include/fuse-lite/fuse_lowlevel.h b/include/fuse-lite/fuse_lowlevel.h index 64d8bf71..9dd9ccd4 100644 --- a/include/fuse-lite/fuse_lowlevel.h +++ b/include/fuse-lite/fuse_lowlevel.h @@ -920,19 +920,6 @@ int fuse_reply_write(fuse_req_t req, size_t count); */ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); -/** - * Reply with data vector - * - * Possible requests: - * read, readdir, getxattr, listxattr - * - * @param req request handle - * @param iov the vector containing the data - * @param count the size of vector - * @return zero for success, -errno for failure to send reply - */ -int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count); - /** * Reply with filesystem statistics * diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index 42387073..7295832b 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -189,24 +189,6 @@ static int send_reply(fuse_req_t req, int error, const void *arg, return send_reply_iov(req, error, iov, count); } -int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) -{ - int res; - struct iovec *padded_iov; - - padded_iov = malloc((count + 1) * sizeof(struct iovec)); - if (padded_iov == NULL) - return fuse_reply_err(req, -ENOMEM); - - memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); - count++; - - res = send_reply_iov(req, 0, padded_iov, count); - free(padded_iov); - - return res; -} - size_t fuse_dirent_size(size_t namelen) { return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen); From e333c71e6344682727e416b976bc83456c2b60a1 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 25 Feb 2009 00:04:50 +0000 Subject: [PATCH 278/328] speed up mount time --- libntfs-3g/attrib.c | 55 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 2d943b8e..777258a4 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -5096,28 +5096,61 @@ int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, return ret; } +/* Below macros are 32-bit ready. */ +#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \ + (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) +#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) + +static u8 *ntfs_init_lut256(void) +{ + int i; + u8 *lut; + + lut = ntfs_malloc(256); + if (lut) + for(i = 0; i < 256; i++) + *(lut + i) = 8 - BITCOUNT(i); + return lut; +} + s64 ntfs_attr_get_free_bits(ntfs_attr *na) { - u8 *buf; + u8 *buf, *lut; + s64 br = 0; + s64 total = 0; s64 nr_free = 0; - s64 br, total = 0; - buf = ntfs_malloc(na->ni->vol->cluster_size); - if (!buf) + lut = ntfs_init_lut256(); + if (!lut) return -1; - while (1) { - int i, j; + + buf = ntfs_malloc(65536); + if (!buf) + goto out; - br = ntfs_attr_pread(na, total, na->ni->vol->cluster_size, buf); + while (1) { + u32 *p; + br = ntfs_attr_pread(na, total, 65536, buf); if (br <= 0) break; total += br; - for (i = 0; i < br; i++) - for (j = 0; j < 8; j++) - if (!((buf[i] >> j) & 1)) - nr_free++; + p = (u32 *)buf + br / 4 - 1; + for (; (u8 *)p >= buf; p--) { + nr_free += lut[ *p & 255] + + lut[(*p >> 8) & 255] + + lut[(*p >> 16) & 255] + + lut[(*p >> 24) ]; + } + switch (br % 4) { + case 3: nr_free += lut[*(buf + br - 3)]; + case 2: nr_free += lut[*(buf + br - 2)]; + case 1: nr_free += lut[*(buf + br - 1)]; + } } free(buf); +out: + free(lut); if (!total || br < 0) return -1; return nr_free; From b9a13f0053df20cea78fd87c10c9d0bb1f4a95f1 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 26 Feb 2009 21:50:13 +0000 Subject: [PATCH 279/328] ntfs_inode_open: log all errors --- libntfs-3g/inode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index ae2fe339..6a5ba1e9 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -177,8 +177,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) /* Receive some basic information about inode. */ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - ntfs_log_trace("Failed to receive STANDARD_INFORMATION " - "attribute.\n"); + ntfs_log_perror("No STANDARD_INFORMATION in base record\n"); goto put_err_out; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + @@ -202,6 +201,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) goto put_err_out; if (l > 0x40000) { errno = EIO; + ntfs_log_perror("Too large attrlist (%lld)\n", (long long)l); goto put_err_out; } ni->attr_list_size = l; @@ -213,6 +213,8 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) goto put_err_out; if (l != ni->attr_list_size) { errno = EIO; + ntfs_log_perror("Unexpected attrlist size (%lld <> %lu)\n", + (long long)l, ni->attr_list_size); goto put_err_out; } get_size: From 87055834219d119bf9f03b003f69ae88e0b175f1 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 26 Feb 2009 22:51:22 +0000 Subject: [PATCH 280/328] ntfs_inode_free_space: comment cleanups --- libntfs-3g/inode.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 6a5ba1e9..69c59d81 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -213,7 +213,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) goto put_err_out; if (l != ni->attr_list_size) { errno = EIO; - ntfs_log_perror("Unexpected attrlist size (%lld <> %lu)\n", + ntfs_log_perror("Unexpected attrlist size (%lld <> %u)\n", (long long)l, ni->attr_list_size); goto put_err_out; } @@ -988,8 +988,8 @@ err_out: } /** - * ntfs_inode_free_space - free space in the MFT record of inode - * @ni: ntfs inode in which MFT record free space + * ntfs_inode_free_space - free space in the MFT record of an inode + * @ni: ntfs inode in which MFT record needs more free space * @size: amount of space needed to free * * Return 0 on success or -1 on error with errno set to the error code. @@ -1019,18 +1019,8 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) return -1; /* - * Chkdsk complain if $STANDARD_INFORMATION is not in the base MFT - * record. FIXME: I'm not sure in this, need to recheck. For now simply - * do not move $STANDARD_INFORMATION at all. - * - * Also we can't move $ATTRIBUTE_LIST from base MFT_RECORD, so position - * search context on first attribute after $STANDARD_INFORMATION and - * $ATTRIBUTE_LIST. - * - * Why we reposition instead of simply skip this attributes during - * enumeration? Because in case we have got only in-memory attribute - * list ntfs_attr_lookup will fail when it will try to find - * $ATTRIBUTE_LIST. + * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT + * record, so position search context on the first attribute after them. */ if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { @@ -1047,7 +1037,6 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) while (1) { int record_size; - /* * Check whether attribute is from different MFT record. If so, * find next, because we don't need such. @@ -1079,15 +1068,17 @@ retry: } freed += record_size; - /* Check whether we done. */ + /* Check whether we are done. */ if (size <= freed) { ntfs_attr_put_search_ctx(ctx); return 0; } - /* - * Reposition to first attribute after $STANDARD_INFORMATION and - * $ATTRIBUTE_LIST (see comments upwards). + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find + * $ATTRIBUTE_LIST. */ ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, @@ -1106,7 +1097,7 @@ retry: put_err_out: ntfs_attr_put_search_ctx(ctx); if (err == ENOSPC) - ntfs_log_trace("No attributes left that can be moved out.\n"); + ntfs_log_trace("No attributes left that could be moved out.\n"); errno = err; return -1; } From fbc392830c2b0af7258f57427b4c91c456e05ec0 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 26 Feb 2009 23:41:32 +0000 Subject: [PATCH 281/328] ntfs_inode_free_space: factor out ntfs_attr_position --- include/ntfs-3g/attrib.h | 2 ++ libntfs-3g/attrib.c | 30 ++++++++++++++++++++++++++++++ libntfs-3g/inode.c | 28 ++++++---------------------- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index eafa165a..36096239 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -92,6 +92,8 @@ extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, const VCN lowest_vcn, const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx); +extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); + extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, const ATTR_TYPES type); diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 777258a4..73747c8a 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2350,6 +2350,36 @@ out: return ret; } +/** + * ntfs_attr_position - find given or next attribute type in an ntfs inode + * @type: attribute type to start lookup + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute type in an ntfs inode or the next attribute which is not + * the AT_END attribute. Please see more details at ntfs_attr_lookup. + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * The following error codes are defined: + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + * ENOSPC No attribute was found after 'type', only AT_END. + */ +int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + return -1; + if (ctx->attr->type == AT_END) { + errno = ENOSPC; + return -1; + } + } + return 0; +} + /** * ntfs_attr_init_search_ctx - initialize an attribute search context * @ctx: attribute search context to initialize diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 69c59d81..3d026c32 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -1022,17 +1022,9 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT * record, so position search context on the first attribute after them. */ - if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, - 0, ctx)) { - if (errno != ENOENT) { - err = errno; - ntfs_log_perror("%s: attr lookup failed #2", __FUNCTION__); - goto put_err_out; - } - if (ctx->attr->type == AT_END) { - err = ENOSPC; - goto put_err_out; - } + if (ntfs_attr_position(AT_FILE_NAME, ctx)) { + err = errno; + goto put_err_out; } while (1) { @@ -1081,17 +1073,9 @@ retry: * $ATTRIBUTE_LIST. */ ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, - NULL, 0, ctx)) { - if (errno != ENOENT) { - err = errno; - ntfs_log_perror("Attr lookup #2 failed"); - break; - } - if (ctx->attr->type == AT_END) { - err = ENOSPC; - break; - } + if (ntfs_attr_position(AT_FILE_NAME, ctx)) { + err = errno; + break; } } put_err_out: From c375eb9c28843ce71d9c4347b0aadb28848217ed Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 26 Feb 2009 23:46:55 +0000 Subject: [PATCH 282/328] ntfs_inode_free_space: cleanup --- libntfs-3g/inode.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 3d026c32..1947d22e 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -997,7 +997,7 @@ err_out: int ntfs_inode_free_space(ntfs_inode *ni, int size) { ntfs_attr_search_ctx *ctx; - int freed, err; + int freed; if (!ni || size < 0) { errno = EINVAL; @@ -1022,10 +1022,8 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT * record, so position search context on the first attribute after them. */ - if (ntfs_attr_position(AT_FILE_NAME, ctx)) { - err = errno; + if (ntfs_attr_position(AT_FILE_NAME, ctx)) goto put_err_out; - } while (1) { int record_size; @@ -1037,11 +1035,10 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) retry: if (ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - err = errno; if (errno != ENOENT) { ntfs_log_perror("Attr lookup failed #2"); } else - err = ENOSPC; + errno = ENOSPC; goto put_err_out; } } @@ -1052,9 +1049,7 @@ retry: record_size = le32_to_cpu(ctx->attr->length); - /* Move away attribute. */ if (ntfs_attr_record_move_away(ctx, 0)) { - err = errno; ntfs_log_perror("Failed to move out attribute #2"); break; } @@ -1073,16 +1068,13 @@ retry: * $ATTRIBUTE_LIST. */ ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_position(AT_FILE_NAME, ctx)) { - err = errno; + if (ntfs_attr_position(AT_FILE_NAME, ctx)) break; - } } put_err_out: ntfs_attr_put_search_ctx(ctx); - if (err == ENOSPC) + if (errno == ENOSPC) ntfs_log_trace("No attributes left that could be moved out.\n"); - errno = err; return -1; } From ac85b3e5dda6f18a5ed43861bf3a669b43d9815f Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 27 Feb 2009 01:14:53 +0000 Subject: [PATCH 283/328] ntfs_inode_free_space: more cleanups --- libntfs-3g/inode.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 1947d22e..080a1783 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -1017,7 +1017,6 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) return -1; - /* * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT * record, so position search context on the first attribute after them. @@ -1033,14 +1032,8 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) */ while (ctx->ntfs_ino->mft_no != ni->mft_no) { retry: - if (ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - if (errno != ENOENT) { - ntfs_log_perror("Attr lookup failed #2"); - } else - errno = ENOSPC; + if (ntfs_attr_position(AT_UNUSED, ctx)) goto put_err_out; - } } if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && From 5bb72792c853e985a9b2557ae8433a3351ebb728 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 27 Feb 2009 02:05:28 +0000 Subject: [PATCH 284/328] ntfs_inode_sync_standard_info: cleanup, log error --- libntfs-3g/inode.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 080a1783..bd35c9d7 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -514,20 +514,17 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; - int err; - ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) return -1; if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, - 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - err = errno; - ntfs_log_trace("Failed to receive STANDARD_INFORMATION " - "attribute.\n"); + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to sync standard info (inode %lld)", + (long long)ni->mft_no); ntfs_attr_put_search_ctx(ctx); - errno = err; return -1; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + @@ -688,8 +685,6 @@ int ntfs_inode_sync(ntfs_inode *ni) if (err != EIO) err = EBUSY; } - ntfs_log_perror("Failed to sync standard info (inode %lld)", - (long long)ni->mft_no); } /* Update FILE_NAME's in the index. */ From 1e43ec0fde07f9b668be668f3e9686050f90c7f0 Mon Sep 17 00:00:00 2001 From: szaka Date: Fri, 27 Feb 2009 02:33:05 +0000 Subject: [PATCH 285/328] fix: never move the standard information attribute into an extended MFT record (Faisal Puthuparackat, Szabolcs Szakacsits) --- libntfs-3g/attrib.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 73747c8a..7eb6772c 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3920,14 +3920,15 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); goto put_err_out; } - - /* We can't move out attribute list, thus move out others. */ - if (na->type == AT_ATTRIBUTE_LIST) { + /* + * The standard information and attribute list attributes can't be + * moved out from the base MFT record, so try to move out others. + */ + if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, non_resident_end) + 8)) { - ntfs_log_perror("Couldn't free space in the MFT record to " - "make attribute list non resident"); + ntfs_log_perror("Could not free space in MFT record"); return -1; } return ntfs_resident_attr_resize(na, newsize); From 1f24de3b201c08b631048c8fd1883b71b4faa9e9 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 28 Feb 2009 13:56:46 +0000 Subject: [PATCH 286/328] README update --- README | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/README b/README index b4a1c73f..ecc1943b 100644 --- a/README +++ b/README @@ -3,10 +3,9 @@ INTRODUCTION ============ The NTFS-3G driver is an open source, freely available read/write NTFS driver -for Linux, FreeBSD, Mac OS X, NetBSD, and Haiku. It provides safe and fast -handling of the Windows XP, Windows Server 2003, Windows 2000 and Windows -Vista file systems. Most POSIX file system operations are supported, and -full file ownership and permission support is also coming along fast. +for Linux, FreeBSD, Mac OS X, NetBSD, Solaris and Haiku. It provides safe and +fast handling of the Windows XP, Windows Server 2003, Windows 2000, Windows +Vista, and Windows Server 2008 file systems. The purpose of the project is to develop, continuously quality test and support a trustable, featureful and high performance solution for hardware @@ -15,16 +14,16 @@ with NTFS. Besides this practical goal, the project also aims to explore the limits of the hybrid, kernel/user space filesystem driver approach, performance, reliability and feature richness per invested effort wise. -The driver is in STABLE status. The test methods, the test suites used and -testimonials can be found on +The driver is in STABLE status. The test methods, the test suites used +can be found at - http://www.ntfs-3g.org/quality.html + http://ntfs-3g.org/quality.html News, support answers, problem submission instructions, support and discussion forums, performance numbers and other information are available on the project web site at - http://www.ntfs-3g.org + http://ntfs-3g.org QUICK INSTALLATION @@ -47,21 +46,14 @@ USAGE ===== If there was no error during installation then the NTFS volume can be -read-write mounted for everybody the following way (unmount the volume if -it was already mounted, and replace /dev/sda1 and /mnt/windows, if needed): +read-write mounted for everybody the following way as the root user +(unmount the volume if it was already mounted, and replace /dev/sda1 +and /mnt/windows, if needed): mount -t ntfs-3g /dev/sda1 /mnt/windows or ntfs-3g /dev/sda1 /mnt/windows -If your Operating System vendor didn't setup your language specific settings -then you may also need to set the 'locale' mount option to make all filenames -with national characters visible. Replace the below en_US.UTF-8 with the -appropriate setting. You can find more information about his topic at -http://ntfs-3g.org/support.html#locale - - mount -t ntfs-3g /dev/hda1 /mnt/windows -o locale=en_US.UTF-8 - Please see the ntfs-3g manual page for more options and examples. You can also make NTFS to be mounted during boot by putting the below From a8df23cfa8e301263eba0d430ecd7af96f88f4d2 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 01:34:52 +0000 Subject: [PATCH 287/328] ntfs_resident_attr_resize, ntfs_attr_truncate: tabulated logging --- libntfs-3g/attrib.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 7eb6772c..39555c27 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3810,7 +3810,7 @@ cluster_free_err_out: * ERANGE - @newsize is not valid for the attribute type of @na. * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ -static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) { ntfs_attr_search_ctx *ctx; ntfs_volume *vol; @@ -4007,6 +4007,16 @@ put_err_out: return ret; } +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_resident_attr_resize_i(na, newsize); + ntfs_log_leave("\n"); + return ret; +} + /** * ntfs_attr_make_resident - convert a non-resident to a resident attribute * @na: open ntfs attribute to make resident @@ -4977,7 +4987,7 @@ put_err_out: */ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) { - int ret; + int ret = STATUS_ERROR; if (!na || newsize < 0 || (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { @@ -4986,12 +4996,13 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) return STATUS_ERROR; } - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, size %lld\n", + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, size %lld\n", (unsigned long long)na->ni->mft_no, na->type, newsize); if (na->data_size == newsize) { ntfs_log_trace("Size is already ok\n"); - return STATUS_OK; + ret = STATUS_OK; + goto out; } /* * Encrypted attributes are not supported. We return access denied, @@ -5000,7 +5011,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) if (NAttrEncrypted(na)) { errno = EACCES; ntfs_log_perror("Failed to truncate encrypted attribute"); - return STATUS_ERROR; + goto out; } /* * TODO: Implement making handling of compressed attributes. @@ -5008,7 +5019,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) if (NAttrCompressed(na)) { errno = EOPNOTSUPP; ntfs_log_perror("Failed to truncate compressed attribute"); - return STATUS_ERROR; + goto out; } if (NAttrNonResident(na)) { if (newsize > na->data_size) @@ -5017,8 +5028,8 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) ret = ntfs_non_resident_attr_shrink(na, newsize); } else ret = ntfs_resident_attr_resize(na, newsize); - - ntfs_log_trace("Return status %d\n", ret); +out: + ntfs_log_leave("Return status %d\n", ret); return ret; } From 893ccb0e4014db054e1074105ce75d88ec3a3f1a Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 01:37:56 +0000 Subject: [PATCH 288/328] explicit declaration of ntfs_resident_attr_resize --- libntfs-3g/attrib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 39555c27..73f2cfb8 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3794,6 +3794,9 @@ cluster_free_err_out: return -1; } + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); + /** * ntfs_resident_attr_resize - resize a resident, open ntfs attribute * @na: resident ntfs attribute to resize From f7505dfd2f068b211be9a19ded19b249662bea6b Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 08:15:19 +0000 Subject: [PATCH 289/328] Prefer Support Forums for knowledge sharing --- src/ntfs-3g.8.in | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 532510e3..b9f4d534 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 2006-2009 Szabolcs Szakacsits. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFS-3G 8 "February 2009" "ntfs-3g @VERSION@" +.TH NTFS-3G 8 "March 2009" "ntfs-3g @VERSION@" .SH NAME ntfs-3g \- Third Generation Read/Write NTFS Driver .SH SYNOPSIS @@ -236,8 +236,7 @@ Please see http://ntfs-3g.org/support.html .sp .RE -for common questions and known issues. You can contact the development -team on the ntfs\-3g\-devel@lists.sf.net address. +for common questions, known issues and support. .SH ACKNOWLEDGEMENT Several people made heroic efforts, often over five or more years which resulted the ntfs-3g driver. Most importantly they are From 506813768988e3215685cc882c963015533b5440 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 18:44:26 +0000 Subject: [PATCH 290/328] ntfs_non_resident_attr_expand: tabulated logging --- libntfs-3g/attrib.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 73f2cfb8..0d42b55a 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4774,7 +4774,7 @@ put_err_out: * ERANGE - @newsize is not valid for the attribute type of @na. * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ -static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) +static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) { LCN lcn_seek_from; VCN first_free_vcn; @@ -4784,7 +4784,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) s64 org_alloc_size; int err; - ntfs_log_trace("Inode 0x%llx, attr 0x%x, new size %lld old size %lld\n", + ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n", (unsigned long long)na->ni->mft_no, na->type, (long long)newsize, (long long)na->data_size); @@ -4968,6 +4968,17 @@ put_err_out: return -1; } + +static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_non_resident_attr_expand_i(na, newsize); + ntfs_log_leave("\n"); + return ret; +} + /** * ntfs_attr_truncate - resize an ntfs attribute * @na: open ntfs attribute to resize From c3c4d9080b48842b3f226938a2930c5bd852d2e2 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 18:55:34 +0000 Subject: [PATCH 291/328] ntfs_attr_map_whole_runlist: cleanup, tabulated logging --- libntfs-3g/attrib.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 0d42b55a..80a9e782 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -561,14 +561,14 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) ntfs_attr_search_ctx *ctx; ntfs_volume *vol = na->ni->vol; ATTR_RECORD *a; - int err; + int ret = -1; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (unsigned long long)na->ni->mft_no, na->type); + ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", + (unsigned long long)na->ni->mft_no, na->type); ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) - return -1; + goto out; /* Map all attribute extents one by one. */ next_vcn = last_vcn = highest_vcn = 0; @@ -639,17 +639,13 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) (long long)highest_vcn, (long long)last_vcn); goto err_out; } - err = errno; + if (errno == ENOENT) + ret = 0; +err_out: ntfs_attr_put_search_ctx(ctx); - if (err == ENOENT) - return 0; -out_now: - errno = err; - return -1; -err_out: - err = errno; - ntfs_attr_put_search_ctx(ctx); - goto out_now; +out: + ntfs_log_leave("\n"); + return ret; } /** From 0f2f03418fa9065946970620e77bba8ef322cfbb Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 19:06:35 +0000 Subject: [PATCH 292/328] ntfs_mapping_pairs_decompress: tabulated logging --- libntfs-3g/runlist.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/runlist.c b/libntfs-3g/runlist.c index 6418c143..f39698de 100644 --- a/libntfs-3g/runlist.c +++ b/libntfs-3g/runlist.c @@ -742,7 +742,7 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, * two into one, if that is possible (we check for overlap and discard the new * runlist if overlap present before returning NULL, with errno = ERANGE). */ -runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, +runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, const ATTR_RECORD *attr, runlist_element *old_rl) { VCN vcn; /* Current vcn. */ @@ -966,6 +966,17 @@ err_out: return NULL; } +runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + runlist_element *rle; + + ntfs_log_enter("Entering\n"); + rle = ntfs_mapping_pairs_decompress_i(vol, attr, old_rl); + ntfs_log_leave("\n"); + return rle; +} + /** * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist * @rl: runlist to use for conversion From 13dc7946889c30ccfccc080f20d57985caf3e8e6 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 19:53:14 +0000 Subject: [PATCH 293/328] fix all 64-bit format string warnings --- include/ntfs-3g/mft.h | 4 ++-- libntfs-3g/attrib.c | 30 +++++++++++++++++------------- libntfs-3g/bootsect.c | 6 +++--- libntfs-3g/compress.c | 4 ++-- libntfs-3g/debug.c | 10 ++++++++-- libntfs-3g/device.c | 5 +++-- libntfs-3g/dir.c | 7 ++++--- libntfs-3g/index.c | 12 ++++++------ libntfs-3g/inode.c | 2 +- libntfs-3g/lcnalloc.c | 15 +++++++++------ libntfs-3g/mft.c | 2 +- libntfs-3g/volume.c | 4 ++-- 12 files changed, 58 insertions(+), 43 deletions(-) diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h index 089a195d..bb15f0f3 100644 --- a/include/ntfs-3g/mft.h +++ b/include/ntfs-3g/mft.h @@ -52,7 +52,7 @@ static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, { int ret; - ntfs_log_enter("Entering for inode %lld\n", MREF(mref)); + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); ret = ntfs_mft_records_read(vol, mref, 1, b); ntfs_log_leave("\n"); return ret; @@ -87,7 +87,7 @@ static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, { int ret; - ntfs_log_enter("Entering for inode %lld\n", MREF(mref)); + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); ret = ntfs_mft_records_write(vol, mref, 1, b); ntfs_log_leave("\n"); return ret; diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 80a9e782..8852f3a4 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -905,8 +905,9 @@ res_err_out: to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs); retry: - ntfs_log_trace("Reading 0x%llx bytes from vcn 0x%llx, lcn 0x%llx, " - "ofs 0x%llx.\n", to_read, rl->vcn, rl->lcn, ofs); + ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" + " %lld.\n", (long long)to_read, (long long)rl->vcn, + (long long )rl->lcn, (long long)ofs); br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + ofs, to_read, b); /* If everything ok, update progress counters and continue. */ @@ -1028,7 +1029,8 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits); ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: " - "%lld\n", count, cur_vcn, from_vcn, to_write, *ofs); + "%lld\n", (long long)count, (long long)cur_vcn, + (long long)from_vcn, (long long)to_write, (long long)*ofs); /* Map whole runlist to be able update mapping pairs later. */ if (ntfs_attr_map_whole_runlist(na)) @@ -1166,9 +1168,9 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) unsigned int undo_data_size : 1; } need_to = { 0, 0 }; - ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " - "0x%llx.\n", na->ni->mft_no, na->type, (long long)pos, - (long long)count); + ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " + "0x%llx.\n", (long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { errno = EINVAL; @@ -1343,7 +1345,8 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); retry: ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " - "%lld.\n", to_write, rl->vcn, rl->lcn, ofs); + "%lld.\n", (long long)to_write, (long long)rl->vcn, + (long long)rl->lcn, (long long)ofs); if (!NVolReadOnly(vol)) { s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; @@ -3131,8 +3134,8 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, return -1; } - ntfs_log_trace("Entering for inode 0x%llx, attr %x, size %lld.\n", - (long long) ni->mft_no, type, size); + ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", + (long long)ni->mft_no, type, (long long)size); if (ni->nr_extents == -1) ni = ni->base_ni; @@ -4382,8 +4385,8 @@ retry: */ if (finished_build) { ntfs_log_trace("Mark attr 0x%x for delete in inode " - "0x%llx.\n", (unsigned)le32_to_cpu( - a->type), ctx->ntfs_ino->mft_no); + "%lld.\n", (unsigned)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); ntfs_inode_mark_dirty(ctx->ntfs_ino); continue; @@ -5006,8 +5009,9 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) return STATUS_ERROR; } - ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, size %lld\n", - (unsigned long long)na->ni->mft_no, na->type, newsize); + ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); if (na->data_size == newsize) { ntfs_log_trace("Size is already ok\n"); diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index ae5d60a9..e9bea370 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -195,7 +195,7 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) } sectors = sle64_to_cpu(bs->number_of_sectors); - ntfs_log_debug("NumberOfSectors = %lld\n", sectors); + ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors); if (!sectors) { ntfs_log_error("Volume size is set to zero.\n"); return -1; @@ -213,8 +213,8 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); - ntfs_log_debug("MFT LCN = 0x%llx\n", vol->mft_lcn); - ntfs_log_debug("MFTMirr LCN = 0x%llx\n", vol->mftmirr_lcn); + ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); + ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); if (vol->mft_lcn > vol->nr_clusters || vol->mftmirr_lcn > vol->nr_clusters) { ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c index a6be8275..09f874bb 100644 --- a/libntfs-3g/compress.c +++ b/libntfs-3g/compress.c @@ -100,8 +100,8 @@ static int ntfs_decompress(u8 *dest, const u32 dest_size, ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); do_next_sb: - ntfs_log_debug("Beginning sub-block at offset = 0x%x in the cb.\n", - cb - cb_start); + ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n", + (int)(cb - cb_start)); /* * Have we reached the end of the compression block or the end of the * decompressed data? The latter can happen for example if the current diff --git a/libntfs-3g/debug.c b/libntfs-3g/debug.c index d154fb0e..f1934833 100644 --- a/libntfs-3g/debug.c +++ b/libntfs-3g/debug.c @@ -63,9 +63,15 @@ void ntfs_debug_runlist_dump(const runlist_element *rl) if (idx > -LCN_EINVAL - 1) idx = 4; - ntfs_log_debug("%-16llx %s %-16llx%s\n", rl[i].vcn, lcn_str[idx], rl[i].length, rl[i].length ? "" : " (runlist end)"); + ntfs_log_debug("%-16lld %s %-16lld%s\n", + (long long)rl[i].vcn, lcn_str[idx], + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); } else - ntfs_log_debug("%-16llx %-16llx %-16llx%s\n", rl[i].vcn, rl[i].lcn, rl[i].length, rl[i].length ? "" : " (runlist end)"); + ntfs_log_debug("%-16lld %-16lld %-16lld%s\n", + (long long)rl[i].vcn, (long long)rl[i].lcn, + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); } while (rl[i++].length); } diff --git a/libntfs-3g/device.c b/libntfs-3g/device.c index cf4969c3..c77d8f95 100644 --- a/libntfs-3g/device.c +++ b/libntfs-3g/device.c @@ -178,7 +178,7 @@ s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) s64 br, total; struct ntfs_device_operations *dops; - ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); if (!b || count < 0 || pos < 0) { errno = EINVAL; @@ -229,7 +229,8 @@ s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, s64 written, total, ret = -1; struct ntfs_device_operations *dops; - ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count); + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + if (!b || count < 0 || pos < 0) { errno = EINVAL; goto out; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index a91e1aca..9b0da6e0 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -814,7 +814,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, * or signals an error (both covered by the rc test). */ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { - ntfs_log_debug("In index root, offset 0x%x.\n", (u8*)ie - (u8*)ir); + ntfs_log_debug("In index root, offset %d.\n", (int)((u8*)ie - (u8*)ir)); /* Bounds checks. */ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || @@ -1442,8 +1442,9 @@ search: /* Ignore hard links from other directories */ if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) { ntfs_log_debug("MFT record numbers don't match " - "(%llu != %llu)\n", dir_ni->mft_no, - MREF_LE(fn->parent_directory)); + "(%llu != %llu)\n", + (long long unsigned)dir_ni->mft_no, + (long long unsigned)MREF_LE(fn->parent_directory)); continue; } diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 2e804514..8d3502ef 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -82,7 +82,7 @@ static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) { s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); - ntfs_log_trace("vcn: %lld\n", vcn); + ntfs_log_trace("vcn: %lld\n", (long long)vcn); ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), 1, icx->block_size, ib); @@ -585,7 +585,7 @@ static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) { s64 pos, ret; - ntfs_log_trace("vcn: %lld\n", vcn); + ntfs_log_trace("vcn: %lld\n", (long long)vcn); pos = ntfs_ib_vcn_to_pos(icx, vcn); @@ -746,7 +746,7 @@ descend_into_child_node: } old_vcn = vcn; - ntfs_log_debug("Descend into node with VCN %lld.\n", vcn); + ntfs_log_debug("Descend into node with VCN %lld\n", (long long)vcn); if (ntfs_ib_read(icx, vcn, ib)) goto err_out; @@ -797,7 +797,7 @@ static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, INDEX_BLOCK *ib; int ih_size = sizeof(INDEX_HEADER); - ntfs_log_trace("Entering ib_vcn = %lld ib_size = %u\n", ib_vcn, ib_size); + ntfs_log_trace("ib_vcn: %lld ib_size: %u\n", (long long)ib_vcn, ib_size); ib = ntfs_calloc(ib_size); if (!ib) @@ -893,7 +893,7 @@ static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) ntfs_attr *na; int ret = STATUS_ERROR; - ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", vcn); + ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", (long long)vcn); na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); if (!na) { @@ -970,7 +970,7 @@ static VCN ntfs_ibm_get_free(ntfs_index_context *icx) vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); out: - ntfs_log_trace("allocated vcn: %lld\n", vcn); + ntfs_log_trace("allocated vcn: %lld\n", (long long)vcn); if (ntfs_ibm_set(icx, vcn)) vcn = (VCN)-1; diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index bd35c9d7..198a02b7 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -156,7 +156,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; - ntfs_log_enter("Entering for inode %lld\n", MREF(mref)); + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); if (!vol) { errno = EINVAL; goto out; diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index 6c07ab0d..e95a9eb4 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -56,7 +56,7 @@ static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) { - ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, tc); + ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc); if (tc >= end) *pos = start; @@ -326,8 +326,10 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { ntfs_log_debug("Cluster coalesce: prev_lcn: " "%lld lcn: %lld bmp_pos: %lld " - "prev_run_len: %lld\n", prev_lcn, - lcn, bmp_pos, prev_run_len); + "prev_run_len: %lld\n", + (long long)prev_lcn, + (long long)lcn, (long long)bmp_pos, + (long long)prev_run_len); rl[rlpos - 1].length = ++prev_run_len; } else { if (rlpos) @@ -336,7 +338,7 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, else { rl[rlpos].vcn = start_vcn; ntfs_log_debug("Start_vcn: %lld\n", - start_vcn); + (long long)start_vcn); } rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; @@ -345,8 +347,9 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, } ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", - rl[rlpos - 1].vcn, rl[rlpos - 1].lcn, - rl[rlpos - 1].length); + (long long)rl[rlpos - 1].vcn, + (long long)rl[rlpos - 1].lcn, + (long long)rl[rlpos - 1].length); /* Done? */ if (!--clusters) { if (used_zone_pos) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index b73e9b21..8158f240 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -1025,7 +1025,7 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) "count %lli.\n", (long long)nr); } while (1); - ntfs_log_debug("Allocated %lli clusters.\n", nr); + ntfs_log_debug("Allocated %lld clusters.\n", (long long)nr); rl = ntfs_runlists_merge(mft_na->rl, rl2); if (!rl) { diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 3ae3dc9a..ab8b8803 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -565,9 +565,9 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) * respective zone. */ vol->data1_zone_pos = vol->mft_zone_end; - ntfs_log_debug("data1_zone_pos = 0x%llx\n", vol->data1_zone_pos); + ntfs_log_debug("data1_zone_pos = %lld\n", (long long)vol->data1_zone_pos); vol->data2_zone_pos = 0; - ntfs_log_debug("data2_zone_pos = 0x%llx\n", vol->data2_zone_pos); + ntfs_log_debug("data2_zone_pos = %lld\n", (long long)vol->data2_zone_pos); /* Set the mft data allocation position to mft record 24. */ vol->mft_data_pos = 24; From 9f2d3fce401fc2dcaeede9af6efb20db9be9e310 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 3 Mar 2009 23:55:09 +0000 Subject: [PATCH 294/328] show 'Usage' instead of hint to use 'ntfs-3g --help' --- src/ntfs-3g.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index f1b493af..312362c9 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2320,8 +2320,7 @@ int main(int argc, char *argv[]) ntfs_log_set_handler(ntfs_log_handler_stderr); if (parse_options(argc, argv)) { - ntfs_log_error("Please type '%s --help' for more " - "information.\n", argv[0]); + usage(); return NTFS_VOLUME_SYNTAX_ERROR; } From cda5e8ba4c617c70639777b9d8d66e4f99d43a2f Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 4 Mar 2009 00:01:21 +0000 Subject: [PATCH 295/328] add --version/-V command line option --- src/ntfs-3g.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 312362c9..1fc509d6 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2012,11 +2012,12 @@ static int parse_options(int argc, char *argv[]) { int c; - static const char *sopt = "-o:hv"; + 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 } }; @@ -2063,6 +2064,10 @@ static int parse_options(int argc, char *argv[]) * 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]); From 705cd1c22ffac760124365126665961515bdb7b8 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 5 Mar 2009 22:50:16 +0000 Subject: [PATCH 296/328] ntfs_attr_pwrite: fix attribute initialized size condition (Jean-Pierre Andre) --- libntfs-3g/attrib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 8852f3a4..c1d86da3 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1363,7 +1363,7 @@ retry: */ if ((rounded && (wend < (hole << vol->cluster_size_bits))) || (((to_write % bsize) && - (ofs + to_write == na->initialized_size)))) { + (wend == na->initialized_size)))) { char *cb; From 24b4391bc6ddaa6b47fef5bf51334c1f0a53a757 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 5 Mar 2009 23:04:08 +0000 Subject: [PATCH 297/328] ntfs_attr_pwrite: fix rounding condition --- libntfs-3g/attrib.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index c1d86da3..680f107b 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1352,18 +1352,17 @@ retry: s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; u32 bsize = vol->cluster_size; + /* Byte size needed to zero fill a cluster */ s64 rounded = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; - - /* - * Zero fill to cluster boundary if we're writing to an - * ex-sparse cluster or we're at the end of the attribute. + /** + * Zero fill to cluster boundary if we're writing at the + * end of the attribute or into an ex-sparse cluster. * This will cause the kernel not to seek and read disk * blocks during write(2) to fill the end of the buffer * which increases write speed by 2-10 fold typically. */ - if ((rounded && (wend < (hole << vol->cluster_size_bits))) || - (((to_write % bsize) && - (wend == na->initialized_size)))) { + if (rounded && ((wend == na->initialized_size) || + (wend < (hole << vol->cluster_size_bits)))) { char *cb; From 77096a387a5f0c737736c48230d9557cd56fbdb7 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 5 Mar 2009 23:10:22 +0000 Subject: [PATCH 298/328] ntfs_attr_pwrite: cleanup --- libntfs-3g/attrib.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 680f107b..5ce04214 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1162,7 +1162,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) ntfs_volume *vol; ntfs_attr_search_ctx *ctx = NULL; runlist_element *rl; - s64 eo, hole; + s64 eo, hole_end; struct { unsigned int undo_initialized_size : 1; unsigned int undo_data_size : 1; @@ -1307,7 +1307,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * length. */ ofs = pos - (rl->vcn << vol->cluster_size_bits); - for (hole = 0; count; rl++, ofs = 0, hole = 0) { + for (hole_end = 0; count; rl++, ofs = 0, hole_end = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { @@ -1328,7 +1328,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } if (rl->lcn < (LCN)0) { - hole = rl->vcn + rl->length; + hole_end = rl->vcn + rl->length; if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; @@ -1353,7 +1353,7 @@ retry: s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; u32 bsize = vol->cluster_size; /* Byte size needed to zero fill a cluster */ - s64 rounded = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; + s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; /** * Zero fill to cluster boundary if we're writing at the * end of the attribute or into an ex-sparse cluster. @@ -1361,22 +1361,22 @@ retry: * blocks during write(2) to fill the end of the buffer * which increases write speed by 2-10 fold typically. */ - if (rounded && ((wend == na->initialized_size) || - (wend < (hole << vol->cluster_size_bits)))) { + if (rounding && ((wend == na->initialized_size) || + (wend < (hole_end << vol->cluster_size_bits)))){ char *cb; - rounded += to_write; + rounding += to_write; - cb = ntfs_malloc(rounded); + cb = ntfs_malloc(rounding); if (!cb) goto err_out; memcpy(cb, b, to_write); - memset(cb + to_write, 0, rounded - to_write); + memset(cb + to_write, 0, rounding - to_write); - written = ntfs_pwrite(vol->dev, wpos, rounded, cb); - if (written == rounded) + written = ntfs_pwrite(vol->dev, wpos, rounding, cb); + if (written == rounding) written = to_write; free(cb); From 0f18c0f65f797dd7ae173259442f617976fcc4e2 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 18 Mar 2009 00:47:58 +0000 Subject: [PATCH 299/328] ntfs_pathname_to_inode: cleanup perror logging --- libntfs-3g/dir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 9b0da6e0..ebc0803b 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -482,8 +482,8 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, len = ntfs_mbstoucs(p, &unicode); if (len < 0) { - ntfs_log_perror("Couldn't convert filename to Unicode: " - "'%s'.\n", p); + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); err = errno; goto close; } else if (len > NTFS_MAX_NAME_LEN) { From 647d06a4346b1fc3e0a1c0507c28303a3d08d755 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 18 Mar 2009 00:53:21 +0000 Subject: [PATCH 300/328] ntfs_attr_pwrite: fix wrong variable type --- libntfs-3g/attrib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 5ce04214..ecfb3b8a 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1162,7 +1162,8 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) ntfs_volume *vol; ntfs_attr_search_ctx *ctx = NULL; runlist_element *rl; - s64 eo, hole_end; + s64 hole_end; + int eo; struct { unsigned int undo_initialized_size : 1; unsigned int undo_data_size : 1; From bf661570a54b81387f7a7a649fc95d48f6325088 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 18 Mar 2009 00:56:28 +0000 Subject: [PATCH 301/328] ntfs_attr_pread: fix wrong variable type (Jean-Pierre Andre) --- libntfs-3g/attrib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index ecfb3b8a..5e2d5420 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -958,7 +958,7 @@ rl_err_out: */ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) { - int ret; + s64 ret; if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { errno = EINVAL; From 02f8eb12c26f47395a534a7e975ad8df8a808fbc Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 19 Mar 2009 17:42:58 +0000 Subject: [PATCH 302/328] better explanation for the 'silent' mount option --- src/ntfs-3g.8.in | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index b9f4d534..d2c75646 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -157,7 +157,15 @@ The default is infinite. Note that the size of read requests is limited anyway to 32 pages (which is 128kbyte on i386). .TP .B silent -Do nothing on chmod and chown operations, but do not return error. +Do not return error for chown and chmod unless access right +handling is turned on by either of the +.B uid, +.B gid, +.B umask, +.B fmask, +or +.B dmask +option. This option is on by default. .TP .BI locale= value From bac5d23b7bff64fcc1d23b4b0538b8fec7644fa6 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 22 Mar 2009 00:06:10 +0000 Subject: [PATCH 303/328] ntfs_ir_make_space: prefer INDEX_ALLOCATION instead of ATTRIBUTE_LIST --- libntfs-3g/index.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 8d3502ef..f759b17d 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1229,15 +1229,11 @@ static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) ntfs_log_trace("Entering\n"); - ret = ntfs_ir_truncate(icx, data_size); - if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { - - ret = ntfs_ir_reparent(icx); - if (ret == STATUS_OK) - ret = STATUS_KEEP_SEARCHING; - else - ntfs_log_perror("Failed to nodify INDEX_ROOT"); - } + ret = ntfs_ir_reparent(icx); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + else + ntfs_log_perror("Failed to nodify INDEX_ROOT"); return ret; } From 6b04b569ee7a9136e380958e4dc0adb2906f7194 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 22 Mar 2009 00:08:50 +0000 Subject: [PATCH 304/328] don't move INDEX_ROOT to an extent MFT record --- libntfs-3g/attrib.c | 9 +++++++++ libntfs-3g/inode.c | 3 +++ 2 files changed, 12 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 5e2d5420..c0cac743 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3922,6 +3922,15 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); goto put_err_out; } + + /* Prefer to add AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ + if (na->type == AT_INDEX_ROOT) { + err = ENOSPC; + ntfs_log_trace("INDEX_ROOT can not be enlarged\n"); + ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + goto put_err_out; + } + /* * The standard information and attribute list attributes can't be * moved out from the base MFT record, so try to move out others. diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 198a02b7..e1c24ce8 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -1035,6 +1035,9 @@ retry: ctx->attr->type == AT_DATA) goto retry; + if (ctx->attr->type == AT_INDEX_ROOT) + goto retry; + record_size = le32_to_cpu(ctx->attr->length); if (ntfs_attr_record_move_away(ctx, 0)) { From 31a807d4fd55aa0ae4b12e0f19a97f96d25b2255 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 22 Mar 2009 13:32:34 +0000 Subject: [PATCH 305/328] enlarge INDEX_ROOT if ATTRIBUTE_LIST addition will be avoidable --- libntfs-3g/attrib.c | 8 ++++++++ libntfs-3g/index.c | 14 +++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index c0cac743..d5a43578 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3400,6 +3400,14 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) "(%u > %u)\n", new_muse, alloc_size); return -1; } + + if (a->type == AT_INDEX_ROOT && new_size > attr_size && + new_muse + 120 > alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", + new_muse, alloc_size); + return -1; + } /* Move attributes following @a to their new location. */ memmove((u8 *)a + new_size, (u8 *)a + attr_size, diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index f759b17d..acd6394c 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1228,12 +1228,16 @@ static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) int ret; ntfs_log_trace("Entering\n"); + + ret = ntfs_ir_truncate(icx, data_size); + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { - ret = ntfs_ir_reparent(icx); - if (ret == STATUS_OK) - ret = STATUS_KEEP_SEARCHING; - else - ntfs_log_perror("Failed to nodify INDEX_ROOT"); + ret = ntfs_ir_reparent(icx); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + else + ntfs_log_perror("Failed to nodify INDEX_ROOT"); + } return ret; } From a80efdeb880cca8438d97b5770cdc0dd47c9b5d0 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 22 Mar 2009 13:34:02 +0000 Subject: [PATCH 306/328] release 2009.3.6 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8ea3430f..96542339 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2009.2.1],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="49" +AC_INIT([ntfs-3g],[2009.3.6],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="51" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From 95868ac632ebbf3009e983bdac272ddbf520f9c1 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 25 Mar 2009 00:10:38 +0000 Subject: [PATCH 307/328] fix "no space left" when there is free disk space (Antonio Pineda, Szabolcs Szakacsits) --- libntfs-3g/lcnalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index e95a9eb4..3a8d5ed7 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -451,7 +451,7 @@ switch_to_data1_zone: search_zone = 2; search_zone = 1; zone_start = vol->mft_zone_pos; zone_end = vol->mft_zone_end; - if (!zone_start == vol->mft_zone_start) + if (zone_start == vol->mft_zone_start) pass = 2; break; } From 895b64f4158738ea5850c86f15494642e49980b2 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 25 Mar 2009 20:52:24 +0000 Subject: [PATCH 308/328] fix "no space on device" error during file creation introduced in 2009.3.6 (Jean-Pierre Andre, Szabolcs Szakacsits) --- libntfs-3g/attrib.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index d5a43578..fa448af7 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3402,11 +3402,11 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) } if (a->type == AT_INDEX_ROOT && new_size > attr_size && - new_muse + 120 > alloc_size) { + new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { errno = ENOSPC; ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", new_muse, alloc_size); - return -1; + return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; } /* Move attributes following @a to their new location. */ @@ -3440,12 +3440,14 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, const u32 new_size) { + int ret; + ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); /* Resize the resident part of the attribute record. */ - if (ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + - new_size + 7) & ~7) < 0) - return -1; + if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + + new_size + 7) & ~7)) < 0) + return ret; /* * If we made the attribute value bigger, clear the area between the * old size and @new_size. @@ -3860,8 +3862,8 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) */ if (newsize < vol->mft_record_size) { /* Perform the resize of the attribute record. */ - if (!ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, - newsize)) { + if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + newsize))) { /* Update attribute size everywhere. */ na->data_size = na->initialized_size = newsize; na->allocated_size = (newsize + 7) & ~7; @@ -3874,6 +3876,11 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) } goto resize_done; } + /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + err = errno; + goto put_err_out; + } } /* There is not enough space in the mft record to perform the resize. */ @@ -3931,14 +3938,6 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) goto put_err_out; } - /* Prefer to add AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ - if (na->type == AT_INDEX_ROOT) { - err = ENOSPC; - ntfs_log_trace("INDEX_ROOT can not be enlarged\n"); - ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; - goto put_err_out; - } - /* * The standard information and attribute list attributes can't be * moved out from the base MFT record, so try to move out others. From 5710a76d2cf81dfb9a3a60e38599aabde7fd7db0 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 25 Mar 2009 23:42:49 +0000 Subject: [PATCH 309/328] ntfs_mst_post_read_fixup: more detailed error reporting --- libntfs-3g/mst.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/mst.c b/libntfs-3g/mst.c index b6beb39d..470942d6 100644 --- a/libntfs-3g/mst.c +++ b/libntfs-3g/mst.c @@ -63,7 +63,9 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) (u32)(usa_ofs + (usa_count * 2)) > size || (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { errno = EINVAL; - ntfs_log_perror("%s", __FUNCTION__); + ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d " + "usa_count: %d", __FUNCTION__, *(le32 *)b, + size, usa_ofs, usa_count); return -1; } /* Position of usn in update sequence array. */ @@ -90,9 +92,12 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) * Set the magic to "BAAD" and return failure. * Note that magic_BAAD is already converted to le32. */ - b->magic = magic_BAAD; errno = EIO; - ntfs_log_perror("Incomplete multi-sector transfer"); + ntfs_log_perror("Incomplete multi-sector transfer: " + "magic: 0x%08x size: %d usa_ofs: %d usa_count:" + " %d data: %d usn: %d", *(le32 *)b, size, + usa_ofs, usa_count, *data_pos, usn); + b->magic = magic_BAAD; return -1; } data_pos += NTFS_BLOCK_SIZE/sizeof(u16); From bda841aa097e21967b3710f62952ead04c2a4a93 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 25 Mar 2009 23:45:11 +0000 Subject: [PATCH 310/328] release 2009.3.8 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 96542339..cef26b03 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2009.3.6],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="51" +AC_INIT([ntfs-3g],[2009.3.8],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="52" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From fc372f7316d21823df75479f28aa26fb62671d86 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 26 Mar 2009 15:55:50 +0000 Subject: [PATCH 311/328] ntfs_fuse_filler: fix misleading error message if filename decoding failed --- src/ntfs-3g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 1fc509d6..8785bae0 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -636,7 +636,7 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, return 0; if (ntfs_ucstombs(name, name_len, &filename, 0) < 0) { - ntfs_log_perror("Skipping unrepresentable filename (inode %llu)", + ntfs_log_perror("Filename decoding failed (inode %llu)", (unsigned long long)MREF(mref)); return -1; } From cb1b5b67dbd503ac692d278a32d9d569e49456b7 Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 30 Mar 2009 10:57:34 +0000 Subject: [PATCH 312/328] fix potential heap corruption when allocating an extent mft record --- libntfs-3g/mft.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 8158f240..dc47f61f 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -1459,7 +1459,7 @@ found_free_rec: free(ni); goto undo_mftbmp_alloc; } - if (base_ni->extent_nis) { + if (base_ni->nr_extents) { memcpy(extent_nis, base_ni->extent_nis, i - 4 * sizeof(ntfs_inode *)); free(base_ni->extent_nis); @@ -1761,7 +1761,7 @@ found_free_rec: free(ni); goto undo_mftbmp_alloc; } - if (base_ni->extent_nis) { + if (base_ni->nr_extents) { memcpy(extent_nis, base_ni->extent_nis, i - 4 * sizeof(ntfs_inode *)); free(base_ni->extent_nis); From fd8e63faa3f31e308b9f567322a5337ca3d9fd0b Mon Sep 17 00:00:00 2001 From: szaka Date: Mon, 30 Mar 2009 11:02:24 +0000 Subject: [PATCH 313/328] ntfs_attr_pwrite: fix potential corruption and crash after partial write --- libntfs-3g/attrib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index fa448af7..edd70df6 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1390,8 +1390,9 @@ retry: total += written; count -= written; b = (const u8*)b + written; - continue; } + if (written == to_write) + continue; /* If the syscall was interrupted, try again. */ if (written == (s64)-1 && errno == EINTR) goto retry; From a5c720e21bb57797e97729e8a3995116c986141b Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 1 Apr 2009 14:52:08 +0000 Subject: [PATCH 314/328] fix mount hang if kernel 2.6.29 audit is enabled: we ignore mtab update error as mount(8) --- libfuse-lite/mount_util.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c index 5e162bf6..75a7ee63 100644 --- a/libfuse-lite/mount_util.c +++ b/libfuse-lite/mount_util.c @@ -58,7 +58,6 @@ int fuse_mnt_add_mount(const char *progname, const char *fsname, const char *mnt, const char *type, const char *opts) { int res; - int status; if (!mtab_needs_update(mnt)) return 0; @@ -66,7 +65,7 @@ int fuse_mnt_add_mount(const char *progname, const char *fsname, res = fork(); if (res == -1) { fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); - return -1; + return 0; } if (res == 0) { char templ[] = "/tmp/fusermountXXXXXX"; @@ -96,14 +95,6 @@ int fuse_mnt_add_mount(const char *progname, const char *fsname, strerror(errno)); exit(1); } - res = waitpid(res, &status, 0); - if (res == -1) { - fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); - return -1; - } - if (status != 0) - return -1; - return 0; } From e82c6f0329abbd758ea5b91cd5d8fa269dd69656 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 2 Apr 2009 11:00:58 +0000 Subject: [PATCH 315/328] release 2009.4.4 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index cef26b03..8e86c950 100644 --- a/configure.ac +++ b/configure.ac @@ -23,8 +23,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2009.3.8],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="52" +AC_INIT([ntfs-3g],[2009.4.4],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="54" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From e030b6985f2480da454da92f6b8c69ed95c08506 Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 25 Apr 2009 13:13:29 +0000 Subject: [PATCH 316/328] ntfs_upcase_table_build: fix endianness (Jean-Pierre Andre, Szabolcs Szakacsits) --- libntfs-3g/unistr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index e3dac625..b1263925 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -1013,6 +1013,8 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) uc[i + 1]--; for (r = 0; uc_byte_table[r][0]; r++) uc[uc_byte_table[r][0]] = uc_byte_table[r][1]; + for (i = 0; (u32)i < uc_len; i++) + uc[i] = cpu_to_le16(uc[i]); } /** From 5883b194d5d24a4e27f3ed6778b25bd3ba095a8e Mon Sep 17 00:00:00 2001 From: szaka Date: Sat, 25 Apr 2009 13:20:03 +0000 Subject: [PATCH 317/328] fix passing buffer after partial read/write (Jean-Pierre Andre, Szabolcs Szakacsits) --- src/ntfs-3g.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 8785bae0..74c79447 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -752,7 +752,7 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, size = na->data_size - offset; } while (size > 0) { - s64 ret = ntfs_attr_pread(na, offset, size, buf); + s64 ret = ntfs_attr_pread(na, offset, size, buf + total); if (ret != (s64)size) ntfs_log_perror("ntfs_attr_pread error reading '%s' at " "offset %lld: %lld <> %lld", org_path, @@ -804,7 +804,7 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, goto exit; } while (size) { - s64 ret = ntfs_attr_pwrite(na, offset, size, buf); + s64 ret = ntfs_attr_pwrite(na, offset, size, buf + total); if (0 <= ret && ret < (s64)size) ntfs_log_perror("ntfs_attr_pwrite partial write to '%s'" " (%lld: %lld <> %lld)", path, (long long)offset, From 4536b53092c8d0ea63b618cf12f9fb364c218c5a Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 12 May 2009 17:51:55 +0000 Subject: [PATCH 318/328] ntfs_fuse_filler: truncate too large filenames on OS X (Erik Larsson) --- src/ntfs-3g.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 74c79447..cb5f6e39 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -83,6 +83,10 @@ #include #endif +#if defined(__APPLE__) || defined(__DARWIN__) +#include +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + #include "compat.h" #include "attrib.h" #include "inode.h" @@ -631,11 +635,12 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, { char *filename = NULL; int ret = 0; + int filenamelen = -1; if (name_type == FILE_NAME_DOS) return 0; - if (ntfs_ucstombs(name, name_len, &filename, 0) < 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; @@ -658,6 +663,20 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, 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__) */ + ret = fill_ctx->filler(fill_ctx->buf, filename, &st, 0); } From 10229de22cf9ef7072cf688f884cc217d271c24a Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 17 May 2009 10:20:31 +0000 Subject: [PATCH 319/328] Mac OS X true fsync (Erik Larsson) --- libntfs-3g/unix_io.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/unix_io.c b/libntfs-3g/unix_io.c index 75aebcec..f830eaf6 100644 --- a/libntfs-3g/unix_io.c +++ b/libntfs-3g/unix_io.c @@ -68,6 +68,40 @@ # define O_EXCL 0 #endif +/** + * fsync replacement which makes every effort to try to get the data down to + * disk, using different means for different operating systems. Specifically, + * it issues the proper fcntl for Mac OS X or does fdatasync where it is + * available or as a last resort calls the fsync function. + * Information on this problem was retrieved from: + * http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf + */ +static int ntfs_fsync(int fildes) +{ + int ret = -1; +#if defined(__APPLE__) || defined(__DARWIN__) +# ifndef F_FULLFSYNC +# error "Mac OS X: F_FULLFSYNC is not defined. Either you didn't include fcntl.h or you're using an older, unsupported version of Mac OS X (pre-10.3)." +# endif + /* + * Apple has disabled fsync() for internal disk drives in OS X. + * To force a synchronization of disk contents, we use a Mac OS X + * specific fcntl, F_FULLFSYNC. + */ + ret = fcntl(fildes, F_FULLFSYNC, NULL); + if (ret) { + /* + * If we are not on a file system that supports this, + * then fall back to a plain fsync. + */ + ret = fsync(fildes); + } +#else + ret = fsync(fildes); +#endif + return ret; +} + /** * ntfs_device_unix_io_open - Open a device and lock it exclusively * @dev: @@ -155,7 +189,7 @@ static int ntfs_device_unix_io_close(struct ntfs_device *dev) return -1; } if (NDevDirty(dev)) - if (fsync(DEV_FD(dev))) { + if (ntfs_fsync(DEV_FD(dev))) { ntfs_log_perror("Failed to fsync device %s", dev->d_name); return -1; } @@ -281,7 +315,7 @@ static int ntfs_device_unix_io_sync(struct ntfs_device *dev) int res = 0; if (!NDevReadOnly(dev)) { - res = fsync(DEV_FD(dev)); + res = ntfs_fsync(DEV_FD(dev)); if (res) ntfs_log_perror("Failed to sync device %s", dev->d_name); else From cfd23388b91744cdd9d4d140f12c38402b23dcf1 Mon Sep 17 00:00:00 2001 From: szaka Date: Sun, 17 May 2009 13:39:57 +0000 Subject: [PATCH 320/328] ntfs_fsync: fix comment (Erik Larsson) --- libntfs-3g/unix_io.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/unix_io.c b/libntfs-3g/unix_io.c index f830eaf6..64b41d3e 100644 --- a/libntfs-3g/unix_io.c +++ b/libntfs-3g/unix_io.c @@ -71,9 +71,9 @@ /** * fsync replacement which makes every effort to try to get the data down to * disk, using different means for different operating systems. Specifically, - * it issues the proper fcntl for Mac OS X or does fdatasync where it is - * available or as a last resort calls the fsync function. - * Information on this problem was retrieved from: + * it issues the proper fcntl for Mac OS X or does fsync where it is available + * or as a last resort calls the fsync function. Information on this problem + * was retrieved from: * http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf */ static int ntfs_fsync(int fildes) From 955b1f3e1889de8139013be6d6cb9c9ea9696e45 Mon Sep 17 00:00:00 2001 From: szaka Date: Thu, 21 May 2009 19:42:36 +0000 Subject: [PATCH 321/328] Mac OS X Unicode normalization form conversion (Erik Larsson) --- configure.ac | 32 +++++++++ include/ntfs-3g/unistr.h | 40 +++++++++++ libntfs-3g/unistr.c | 152 +++++++++++++++++++++++++++++++++++++++ src/ntfs-3g.c | 18 +++++ 4 files changed, 242 insertions(+) diff --git a/configure.ac b/configure.ac index 8e86c950..d4c305d0 100644 --- a/configure.ac +++ b/configure.ac @@ -113,6 +113,18 @@ AC_ARG_ENABLE( [enable_device_default_io_ops="yes"] ) +AC_ARG_ENABLE( + [nfconv], + [AS_HELP_STRING([--disable-nfconv],[disable the 'nfconv' patch, which adds support for Unicode normalization form conversion when built on Mac OS X @<:@default=enabled for Mac OS X@:>@])], + [enable_nfconv="no"], + [ + case "${target_os}" in + darwin*) enable_nfconv="yes" ;; + *) enable_nfconv="no" ;; + esac + ] +) + # pthread_rwlock_t requires _GNU_SOURCE AC_GNU_SOURCE @@ -263,6 +275,26 @@ AC_CHECK_MEMBERS([struct stat.st_rdev]) AC_CHECK_MEMBERS([struct stat.st_atim]) AC_CHECK_MEMBERS([struct stat.st_atimespec]) +# For the 'nfconv' patch (Mac OS X only): +case "${target_os}" in +darwin*) + if test "${enable_nfconv}" = "yes"; then + AC_CHECK_HEADER( + [CoreFoundation/CoreFoundation.h], + [ + LDFLAGS="${LDFLAGS} -framework CoreFoundation" + AC_DEFINE( + [ENABLE_NFCONV], + [1], + [Define to 1 if the nfconv patch should be enabled] + ) + ], + AC_MSG_ERROR([[Cannot find CoreFoundation required for 'nfconv' functionality Mac OS X. You may use the --disable-nfconv 'configure' option to avoid this error.]]) + ) + fi + ;; +esac + # Checks for library functions. AC_FUNC_GETMNTENT AC_FUNC_MBRTOWC diff --git a/include/ntfs-3g/unistr.h b/include/ntfs-3g/unistr.h index 6de4bacc..5e77d778 100644 --- a/include/ntfs-3g/unistr.h +++ b/include/ntfs-3g/unistr.h @@ -65,5 +65,45 @@ extern ntfschar *ntfs_str2ucs(const char *s, int *len); extern void ntfs_ucsfree(ntfschar *ucs); +#if defined(__APPLE__) || defined(__DARWIN__) +/** + * Mac OS X only. + * + * Sets file name Unicode normalization form conversion on or off. + * normalize=0 : Off + * normalize=1 : On + * If set to on, all filenames returned by ntfs-3g will be converted to the NFD + * normalization form, while all filenames recieved by ntfs-3g will be converted to the NFC + * normalization form. Since Windows and most other OS:es use the NFC form while Mac OS X + * mostly uses NFD, this conversion increases compatibility between Mac applications and + * NTFS-3G. + * + * @param normalize decides whether or not the string functions will do automatic filename + * normalization when converting to and from UTF-8. 0 means normalization is disabled, + * 1 means it is enabled. + * @return -1 if the argument was invalid or an error occurred, 0 if all went well. + */ +extern int ntfs_macosx_normalize_filenames(int normalize); + +/** + * Mac OS X only. + * + * Normalizes the input string "utf8_string" to one of the normalization forms NFD or NFC. + * The parameter "composed" decides whether output should be in composed, NFC, form + * (composed == 1) or decomposed, NFD, form (composed == 0). + * Input is assumed to be properly UTF-8 encoded and null-terminated. Output will be a newly + * ntfs_calloc'ed string encoded in UTF-8. It is the callers responsibility to free(...) the + * allocated string when it's no longer needed. + * + * @param utf8_string the input string, which may be in any normalization form. + * @param target a pointer where the resulting string will be stored. + * @param composed decides which composition form to normalize the input string to. 0 means + * composed form (NFC), 1 means decomposed form (NFD). + * @return -1 if the normalization failed for some reason, otherwise the length of the + * normalized string stored in target. + */ +extern int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, int composed); +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + #endif /* defined _NTFS_UNISTR_H */ diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index b1263925..e9fbd114 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -45,6 +45,12 @@ #include #endif +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +#include +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + #include "compat.h" #include "attrib.h" #include "types.h" @@ -65,6 +71,18 @@ static int use_utf8 = 1; /* use UTF-8 encoding for file names */ +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +/** + * This variable controls whether or not automatic normalization form conversion + * should be performed when translating NTFS unicode file names to UTF-8. + * Defaults to on, but can be controlled from the outside using the function + * int ntfs_macosx_normalize_filenames(int normalize); + */ +static int nfconvert_utf8 = 1; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + /* * This is used by the name collation functions to quickly determine what * characters are (in)valid. @@ -473,6 +491,13 @@ fail: static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, char **outs, int outs_len) { +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *original_outs_value = *outs; + int original_outs_len = outs_len; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + char *t; int i, size, ret = -1; ntfschar halfpair; @@ -528,6 +553,36 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, } } *t = '\0'; + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(nfconvert_utf8 && (t - *outs) > 0) { + char *new_outs = NULL; + int new_outs_len = ntfs_macosx_normalize_utf8(*outs, &new_outs, 0); // Normalize to decomposed form + if(new_outs_len >= 0 && new_outs != NULL) { + if(original_outs_value != *outs) { + // We have allocated outs ourselves. + free(*outs); + *outs = new_outs; + t = *outs + new_outs_len; + } + else { + // We need to copy new_outs into the fixed outs buffer. + memset(*outs, 0, original_outs_len); + strncpy(*outs, new_outs, original_outs_len-1); + t = *outs + original_outs_len; + free(new_outs); + } + } + else { + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFD: %s\n", *outs); + ntfs_log_error(" new_outs=0x%p\n", new_outs); + ntfs_log_error(" new_outs_len=%d\n", new_outs_len); + } + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + ret = t - *outs; out: return ret; @@ -662,6 +717,19 @@ fail: */ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) { +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *new_ins = NULL; + if(nfconvert_utf8) { + int new_ins_len; + new_ins_len = ntfs_macosx_normalize_utf8(ins, &new_ins, 1); // Normalize to composed form + if(new_ins_len >= 0) + ins = new_ins; + else + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFC: %s\n", ins); + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ const char *t = ins; u32 wc; ntfschar *outpos; @@ -697,6 +765,12 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) ret = --outpos - *outs; fail: +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(new_ins != NULL) + free(new_ins); +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ return ret; } @@ -1089,3 +1163,81 @@ int ntfs_set_char_encoding(const char *locale) return 0; /* always successful */ } +#if defined(__APPLE__) || defined(__DARWIN__) + +int ntfs_macosx_normalize_filenames(int normalize) { +#ifdef ENABLE_NFCONV + if(normalize == 0 || normalize == 1) { + nfconvert_utf8 = normalize; + return 0; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} + +int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, + int composed) { +#ifdef ENABLE_NFCONV + /* For this code to compile, the CoreFoundation framework must be fed to the linker. */ + CFStringRef cfSourceString; + CFMutableStringRef cfMutableString; + CFRange rangeToProcess; + CFIndex requiredBufferLength; + char *result = NULL; + int resultLength = -1; + + /* Convert the UTF-8 string to a CFString. */ + cfSourceString = CFStringCreateWithCString(kCFAllocatorDefault, utf8_string, kCFStringEncodingUTF8); + if(cfSourceString == NULL) { + ntfs_log_error("CFStringCreateWithCString failed!\n"); + return -2; + } + + /* Create a mutable string from cfSourceString that we are free to modify. */ + cfMutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfSourceString); + CFRelease(cfSourceString); /* End-of-life. */ + if(cfMutableString == NULL) { + ntfs_log_error("CFStringCreateMutableCopy failed!\n"); + return -3; + } + + /* Normalize the mutable string to the desired normalization form. */ + CFStringNormalize(cfMutableString, (composed != 0 ? kCFStringNormalizationFormC : kCFStringNormalizationFormD)); + + /* Store the resulting string in a '\0'-terminated UTF-8 encoded char* buffer. */ + rangeToProcess = CFRangeMake(0, CFStringGetLength(cfMutableString)); + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredBufferLength) > 0) { + resultLength = sizeof(char)*(requiredBufferLength + 1); + result = ntfs_calloc(resultLength); + + if(result != NULL) { + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, + 0, false, (UInt8*)result, resultLength-1, &requiredBufferLength) <= 0) { + ntfs_log_error("Could not perform UTF-8 conversion of normalized CFMutableString.\n"); + free(result); + result = NULL; + } + } + else + ntfs_log_error("Could not perform a ntfs_calloc of %d bytes for char *result.\n", resultLength); + } + else + ntfs_log_error("Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n"); + + + CFRelease(cfMutableString); + + if(result != NULL) { + *target = result; + return resultLength - 1; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index cb5f6e39..24d7143e 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1933,6 +1933,24 @@ static char *parse_mount_options(const char *orig_opts) if (missing_option_value(val, "locale")) goto err_exit; setlocale(LC_ALL, 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; From 08dccd58fb8b1f978516fec4f1b29ce964fd26f1 Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 21 Jul 2009 12:55:08 +0000 Subject: [PATCH 322/328] fix format breaking new lines in error messages --- libntfs-3g/index.c | 2 +- libntfs-3g/inode.c | 11 +++++++---- libntfs-3g/security.c | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index acd6394c..4f2dbcf3 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -556,7 +556,7 @@ static int ntfs_ie_lookup(const void *key, const int key_len, *vcn = ntfs_ie_get_vcn(ie); if (*vcn < 0) { errno = EINVAL; - ntfs_log_perror("Negative vcn in inode %llu\n", + ntfs_log_perror("Negative vcn in inode %llu", (unsigned long long)icx->ni->mft_no); return STATUS_ERROR; } diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index e1c24ce8..c83fe6c7 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -177,7 +177,8 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) /* Receive some basic information about inode. */ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - ntfs_log_perror("No STANDARD_INFORMATION in base record\n"); + ntfs_log_perror("No STANDARD_INFORMATION in base record %lld", + (long long)MREF(mref)); goto put_err_out; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + @@ -201,7 +202,8 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) goto put_err_out; if (l > 0x40000) { errno = EIO; - ntfs_log_perror("Too large attrlist (%lld)\n", (long long)l); + ntfs_log_perror("Too large attrlist attribute (%lld), inode " + "%lld", (long long)l, (long long)MREF(mref)); goto put_err_out; } ni->attr_list_size = l; @@ -213,8 +215,9 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) goto put_err_out; if (l != ni->attr_list_size) { errno = EIO; - ntfs_log_perror("Unexpected attrlist size (%lld <> %u)\n", - (long long)l, ni->attr_list_size); + ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " + "%lld", (long long)l, ni->attr_list_size, + (long long)MREF(mref)); goto put_err_out; } get_size: diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index 4b54b699..c9beb99a 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -333,7 +333,7 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8 *)sd, sd_len); if (ret) - ntfs_log_perror("Failed to add SECURITY_DESCRIPTOR\n"); + ntfs_log_perror("Failed to add SECURITY_DESCRIPTOR"); free(sd); return ret; From f7a072bc10aea7e06fdc887d2ed505b8058ef09d Mon Sep 17 00:00:00 2001 From: szaka Date: Tue, 21 Jul 2009 13:12:24 +0000 Subject: [PATCH 323/328] ntfs_inode_open: don't log "No STANDARD_INFORMATION" for extended MFT records --- libntfs-3g/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index c83fe6c7..a57173bb 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -177,8 +177,9 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) /* Receive some basic information about inode. */ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - ntfs_log_perror("No STANDARD_INFORMATION in base record %lld", - (long long)MREF(mref)); + if (!ni->mrec->base_mft_record) + ntfs_log_perror("No STANDARD_INFORMATION in base record" + " %lld", (long long)MREF(mref)); goto put_err_out; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + From aba99dec292feb5fe5b08dfdf73441a308485068 Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 29 Jul 2009 00:13:44 +0000 Subject: [PATCH 324/328] OS X: implement setchgtime(), it fixes Firefox errors (Erik Larsson) --- src/ntfs-3g.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 24d7143e..d923cd90 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -417,14 +417,35 @@ int ntfs_macfuse_setbkuptime(const char *path, const struct timespec *tv) return -errno; /* - * Doing nothing while pretending to do something. NTFS has no backup - * time. If this function is not implemented then some apps break. + * Only pretending to set backup time successfully to please the APIs of + * Mac OS X. In reality, NTFS has no backup time. */ if (ntfs_inode_close(ni)) set_fuse_error(&res); return res; } + +int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv) +{ + ntfs_inode *ni; + int res = 0; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + if (tv) { + ni->last_mft_change_time = tv->tv_sec; + ntfs_fuse_update_times(ni, 0); + } + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} #endif /* defined(__APPLE__) || defined(__DARWIN__) */ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) @@ -1708,7 +1729,8 @@ static struct fuse_operations ntfs_3g_ops = { /* MacFUSE extensions. */ .getxtimes = ntfs_macfuse_getxtimes, .setcrtime = ntfs_macfuse_setcrtime, - .setbkuptime = ntfs_macfuse_setbkuptime + .setbkuptime = ntfs_macfuse_setbkuptime, + .setchgtime = ntfs_macfuse_setchgtime, #endif /* defined(__APPLE__) || defined(__DARWIN__) */ }; From 5263033f852aa1f74687f8e61ebf0c142efa89f2 Mon Sep 17 00:00:00 2001 From: jpandre Date: Fri, 18 Dec 2009 08:43:44 +0000 Subject: [PATCH 325/328] Implemented low-level interface to fuse --- src/lowntfs-3g.c | 4342 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4342 insertions(+) create mode 100644 src/lowntfs-3g.c diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c new file mode 100644 index 00000000..e104997d --- /dev/null +++ b/src/lowntfs-3g.c @@ -0,0 +1,4342 @@ +/** + * 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" + +#define PERMSCONFIG 5 /* 5 for standard release */ + +#if PERMSCONFIG + +#define KERNELACLS ((PERMSCONFIG > 6) & (PERMSCONFIG < 10)) /* want ACLs checked by fuse kernel */ +#define KERNELPERMS (((PERMSCONFIG - 1) % 6) < 3) /* want permissions checked by kernel */ +#define CACHEING (!(PERMSCONFIG % 3)) /* want to use fuse cacheing */ + +#else + +#define KERNELACLS 0 /* do not want ACLs checked by fuse kernel */ + /* fuse patch required for KERNELACLS ! */ +#define KERNELPERMS 0 /* do not want permissions checked by kernel */ +#define CACHEING 0 /* do not want fuse cacheing */ + +#endif + +#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; +} + From eb8da77471026d76fd688c8341da94844a04d84a Mon Sep 17 00:00:00 2001 From: jpandre Date: Fri, 18 Dec 2009 10:31:43 +0000 Subject: [PATCH 326/328] Grouped permission mode setting in the parameter file --- src/lowntfs-3g.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index e104997d..d7f3091b 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -104,22 +104,17 @@ #include "logging.h" #include "misc.h" -#define PERMSCONFIG 5 /* 5 for standard release */ +/* + * The following permission checking modes are governed by + * the LPERMSCONFIG value in param.h + */ -#if PERMSCONFIG - -#define KERNELACLS ((PERMSCONFIG > 6) & (PERMSCONFIG < 10)) /* want ACLs checked by fuse kernel */ -#define KERNELPERMS (((PERMSCONFIG - 1) % 6) < 3) /* want permissions checked by kernel */ -#define CACHEING (!(PERMSCONFIG % 3)) /* want to use fuse cacheing */ - -#else - -#define KERNELACLS 0 /* do not want ACLs checked by fuse kernel */ - /* fuse patch required for KERNELACLS ! */ -#define KERNELPERMS 0 /* do not want permissions checked by kernel */ -#define CACHEING 0 /* do not want fuse cacheing */ - -#endif +/* 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" From 53ba420bda8e4d245c7589f30b5d6a209fc6ad23 Mon Sep 17 00:00:00 2001 From: jpandre Date: Sat, 19 Dec 2009 07:46:20 +0000 Subject: [PATCH 327/328] Remove mis-tagged file --- src/lowntfs-3g.c | 4337 ---------------------------------------------- 1 file changed, 4337 deletions(-) delete mode 100644 src/lowntfs-3g.c 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; -} - From 7b802d193c043b4a73ab9c0cc090a88602f3aec4 Mon Sep 17 00:00:00 2001 From: jpandre Date: Sat, 19 Dec 2009 08:01:18 +0000 Subject: [PATCH 328/328] Driver for fuse low-level interface --- src/lowntfs-3g.c | 4337 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4337 insertions(+) create mode 100644 src/lowntfs-3g.c diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c new file mode 100644 index 00000000..d7f3091b --- /dev/null +++ b/src/lowntfs-3g.c @@ -0,0 +1,4337 @@ +/** + * 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; +} +