--- ntfs-3g-2009.11.14AC.2/configure.ac 2009-12-16 08:33:26.000000000 +0100 +++ ntfslow/ntfs-3g/configure.ac 2009-12-23 10:24:29.000000000 +0100 @@ -314,7 +314,7 @@ 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 \ + sysconf utime utimensat fork \ ]) AC_SYS_LARGEFILE --- ntfs-3g-2009.11.14AC.2/include/fuse-lite/fuse.h 2009-07-30 15:11:50.000000000 +0200 +++ ntfslow/ntfs-3g/include/fuse-lite/fuse.h 2009-12-23 08:22:10.000000000 +0100 @@ -67,7 +67,23 @@ * Changed in fuse 2.8.0 (regardless of API version) * Previously, paths were limited to a length of PATH_MAX. */ + +#define HAS_UTIME_OMIT_OK 1 + struct fuse_operations { + unsigned int flag_nullpath_ok : 1; + + /** + * Flag indicating that the filesystem accepts special + * UTIME_NOW and UTIME_OMIT values in its utimens operation. + */ + unsigned int flag_utime_omit_ok : 1; + + /** + * Reserved flags, don't set + */ + unsigned int flag_reserved : 30; + /** Get file attributes. * * Similar to stat(). The 'st_dev' and 'st_blksize' fields are --- ntfs-3g-2009.11.14AC.2/include/fuse-lite/fuse_lowlevel.h 2009-07-30 15:11:50.000000000 +0200 +++ ntfslow/ntfs-3g/include/fuse-lite/fuse_lowlevel.h 2009-12-22 18:24:54.000000000 +0100 @@ -114,6 +114,8 @@ #define FUSE_SET_ATTR_SIZE (1 << 3) #define FUSE_SET_ATTR_ATIME (1 << 4) #define FUSE_SET_ATTR_MTIME (1 << 5) +#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) +#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) /* ----------------------------------------------------------- * * Request methods and replies * --- ntfs-3g-2009.11.14AC.2/libfuse-lite/fuse.c 2009-07-30 15:11:50.000000000 +0200 +++ ntfslow/ntfs-3g/libfuse-lite/fuse.c 2009-12-23 10:06:50.000000000 +0100 @@ -75,6 +75,7 @@ struct fuse_config conf; int intr_installed; struct fuse_fs *fs; + int utime_omit_ok; }; struct lock { @@ -1187,6 +1188,29 @@ else err = fuse_fs_truncate(f->fs, path, attr->st_size); } +#ifdef HAVE_UTIMENSAT + if (!err && f->utime_omit_ok && + (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) { + struct timespec tv[2]; + + tv[0].tv_sec = 0; + tv[1].tv_sec = 0; + tv[0].tv_nsec = UTIME_OMIT; + tv[1].tv_nsec = UTIME_OMIT; + + if (valid & FUSE_SET_ATTR_ATIME_NOW) + tv[0].tv_nsec = UTIME_NOW; + else if (valid & FUSE_SET_ATTR_ATIME) + tv[0] = attr->st_atim; + + if (valid & FUSE_SET_ATTR_MTIME_NOW) + tv[1].tv_nsec = UTIME_NOW; + else if (valid & FUSE_SET_ATTR_MTIME) + tv[1] = attr->st_mtim; + + err = fuse_fs_utimens(f->fs, path, tv); + } else +#endif if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { struct timespec tv[2]; @@ -2606,6 +2630,7 @@ goto out_free; f->fs = fs; + f->utime_omit_ok = fs->op.flag_utime_omit_ok; /* Oh f**k, this is ugly! */ if (!fs->op.lock) { @@ -2639,6 +2664,8 @@ fuse_session_add_chan(f->se, ch); + if (f->conf.debug) + fprintf(stderr, "utime_omit_ok: %i\n", f->utime_omit_ok); f->ctr = 0; f->generation = 0; /* FIXME: Dynamic hash table */ --- ntfs-3g-2009.11.14AC.2/src/ntfs-3g.c 2009-12-16 08:33:25.000000000 +0100 +++ ntfslow/ntfs-3g/src/ntfs-3g.c 2009-12-23 10:28:15.000000000 +0100 @@ -2171,6 +2171,63 @@ return ntfs_fuse_rm(path); } +#ifdef HAVE_UTIMENSAT + +static int ntfs_fuse_utimens(const char *path, const struct timespec tv[2]) +{ + ntfs_inode *ni; + int res = 0; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path, + (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + /* no check or update if both UTIME_OMIT */ + if ((tv[0].tv_nsec != UTIME_OMIT) || (tv[1].tv_nsec != UTIME_OMIT)) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (ntfs_allowed_access(&security, ni, S_IWRITE) + || ((tv[0].tv_nsec == UTIME_NOW) + && (tv[0].tv_nsec == UTIME_NOW) + && ntfs_allowed_as_owner(&security, ni))) { +#endif + ntfs_time_update_flags mask = NTFS_UPDATE_CTIME; + + if (tv[0].tv_nsec == UTIME_NOW) + mask |= NTFS_UPDATE_ATIME; + else + if (tv[0].tv_nsec != UTIME_OMIT) + ni->last_access_time = tv[0].tv_sec; + if (tv[1].tv_nsec == UTIME_NOW) + mask |= NTFS_UPDATE_MTIME; + else + if (tv[1].tv_nsec != UTIME_OMIT) + ni->last_data_change_time = tv[1].tv_sec; + ntfs_inode_update_times(ni, mask); +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -errno; +#endif + } + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +#else /* HAVE_UTIMENSAT */ + static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) { ntfs_inode *ni; @@ -2238,6 +2295,8 @@ return res; } +#endif /* HAVE_UTIMENSAT */ + static int ntfs_fuse_bmap(const char *path, size_t blocksize, uint64_t *idx) { ntfs_inode *ni; @@ -3372,6 +3431,9 @@ } static struct fuse_operations ntfs_3g_ops = { +#if defined(HAVE_UTIMENSAT) && HAS_UTIME_OMIT_OK + .flag_utime_omit_ok = 1, /* accept UTIME_NOW and UTIME_OMIT in utimens */ +#endif .getattr = ntfs_fuse_getattr, .readlink = ntfs_fuse_readlink, .readdir = ntfs_fuse_readdir, @@ -3392,7 +3454,11 @@ .rename = ntfs_fuse_rename, .mkdir = ntfs_fuse_mkdir, .rmdir = ntfs_fuse_rmdir, +#ifdef HAVE_UTIMENSAT + .utimens = ntfs_fuse_utimens, +#else .utime = ntfs_fuse_utime, +#endif .bmap = ntfs_fuse_bmap, .destroy = ntfs_fuse_destroy2, #if !KERNELPERMS | (POSIXACLS & !KERNELACLS)