Re: [fuse-devel] utimensat fails to update ctime

Miklos Szeredi
2009-12-22T13:01:24Z

On Tue, 22 Dec 2009, Jean-Pierre André wrote:
> With ntfs-3g this is not directly possible, because
> the interface does not provide flags telling which
> timestamps should be updated. The only way would
> be fuse feeding both values (even though unchanged)
> before calling ntfs-3g. This is true for all versions of
> ntfs-3g.

Here's a patch for the high level lib. If the flag_utime_omit_ok flag
is set then the ->utimes() method will be supplied with arguments that
correspond to utimensat (i.e. it can have UTIME_NOW and UTIME_OMIT
values).

Thanks,
Miklos

Index: fuse/configure.in
===================================================================
--- fuse.orig/configure.in 2009-12-22 12:46:44.000000000 +0100
+++ fuse/configure.in 2009-12-22 13:16:07.000000000 +0100
@@ -53,7 +53,7 @@ fi
if test "$enable_mtab" = "no"; then
AC_DEFINE(IGNORE_MTAB, 1, [Don't update /etc/mtab])
fi
-AC_CHECK_FUNCS([fork setxattr fdatasync])
+AC_CHECK_FUNCS([fork setxattr fdatasync utimensat])
AC_CHECK_MEMBERS([struct stat.st_atim])
AC_CHECK_MEMBERS([struct stat.st_atimespec])

Index: fuse/include/fuse.h
===================================================================
--- fuse.orig/include/fuse.h 2009-12-22 12:46:44.000000000 +0100
+++ fuse/include/fuse.h 2009-12-22 13:16:07.000000000 +0100
@@ -458,9 +458,15 @@ 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 : 31;
+ unsigned int flag_reserved : 30;

/**
* Ioctl
Index: fuse/lib/fuse.c
===================================================================
--- fuse.orig/lib/fuse.c 2009-12-22 12:46:44.000000000 +0100
+++ fuse/lib/fuse.c 2009-12-22 13:16:07.000000000 +0100
@@ -97,6 +97,7 @@ struct fuse {
int intr_installed;
struct fuse_fs *fs;
int nullpath_ok;
+ int utime_omit_ok;
int curr_ticket;
struct lock_queue_element *lockq;
};
@@ -2119,6 +2120,29 @@ static void fuse_lib_setattr(fuse_req_t
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)) {
@@ -3637,6 +3661,7 @@ static int fuse_push_module(struct fuse
newfs->m = m;
f->fs = newfs;
f->nullpath_ok = newfs->op.flag_nullpath_ok && f->nullpath_ok;
+ f->utime_omit_ok = newfs->op.flag_utime_omit_ok && f->utime_omit_ok;
return 0;
}

@@ -3687,6 +3712,7 @@ struct fuse *fuse_new_common(struct fuse
fs->compat = compat;
f->fs = fs;
f->nullpath_ok = fs->op.flag_nullpath_ok;
+ f->utime_omit_ok = fs->op.flag_utime_omit_ok;

/* Oh f**k, this is ugly! */
if (!fs->op.lock) {
@@ -3743,8 +3769,10 @@ struct fuse *fuse_new_common(struct fuse

fuse_session_add_chan(f->se, ch);

- if (f->conf.debug)
+ if (f->conf.debug) {
fprintf(stderr, "nullpath_ok: %i\n", f->nullpath_ok);
+ fprintf(stderr, "utime_omit_ok: %i\n", f->utime_omit_ok);
+ }

/* Trace topmost layer by default */
f->fs->debug = f->conf.debug;
Index: fuse/example/fusexmp_fh.c
===================================================================
--- fuse.orig/example/fusexmp_fh.c 2009-06-19 12:24:25.000000000 +0200
+++ fuse/example/fusexmp_fh.c 2009-12-22 13:55:04.000000000 +0100
@@ -283,6 +283,9 @@ static int xmp_ftruncate(const char *pat
static int xmp_utimens(const char *path, const struct timespec ts[2])
{
int res;
+#ifdef HAVE_UTIMENSAT
+ res = utimensat(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW);
+#else
struct timeval tv[2];

tv[0].tv_sec = ts[0].tv_sec;
@@ -291,6 +294,7 @@ static int xmp_utimens(const char *path,
tv[1].tv_usec = ts[1].tv_nsec / 1000;

res = utimes(path, tv);
+#endif
if (res == -1)
return -errno;

@@ -486,6 +490,9 @@ static struct fuse_operations xmp_oper =
.lock = xmp_lock,

.flag_nullpath_ok = 1,
+#if HAVE_UTIMENSAT
+ .flag_utime_omit_ok = 1,
+#endif
};

int main(int argc, char *argv[])