Hi Samuel

On Sat, 19 Sep 2015 at 14:22 Samuel Thibault <samuel.thiba...@gnu.org>
wrote:

> Sorry I didn't think about it at first, but inside the fallback on
> file_utimes:
>
> Flávio Cruz, le Thu 17 Sep 2015 02:05:33 +0000, a écrit :
> > +  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
> > +    {
> > +      time_value_t atim, mtim;
> > +
> > +      if (tsp == NULL)
> > +        /* Setting the number of microseconds to `-1' tells the
> > +           underlying filesystems to use the current time.  */
> > +        atim.microseconds = mtim.microseconds = -1;
> > +      else
> > +        {
> > +          TIMESPEC_TO_TIME_VALUE (&atim, &(tsp[0]));
> > +          TIMESPEC_TO_TIME_VALUE (&mtim, &(tsp[1]));
> > +        }
> > +
> > +      err = HURD_DPORT_USE (fd, __file_utimes (port, atim, mtim));
>
> We should additionally check for tv_nsec being UTIME_OMIT, and in that
> case return EOPNOTSUPP.  Otherwise we'd be telling old translators a very
> odd date.


Gotcha. I'm also checking for UTIME_NOW and then setting the time_value_t
microseconds field to -1 so that old translators use the current time.

Flávio


>
> Samuel
>
diff --git a/sysdeps/mach/hurd/bits/stat.h b/sysdeps/mach/hurd/bits/stat.h
index f60a58a..c2d0cc2 100644
--- a/sysdeps/mach/hurd/bits/stat.h
+++ b/sysdeps/mach/hurd/bits/stat.h
@@ -246,6 +246,10 @@ struct stat64
 # define SF_NOUNLINK	0x00100000	/* file may not be removed or renamed */
 # define SF_SNAPSHOT	0x00200000	/* snapshot inode */
 
+/* Time flags for futimens. */
+#define UTIME_NOW   -1  /* corresponds to the current time */
+#define UTIME_OMIT  -2  /* target time is omitted */
+
 __BEGIN_DECLS
 
 /* Set file flags for FILE to FLAGS.  */
diff --git a/sysdeps/mach/hurd/futimens.c b/sysdeps/mach/hurd/futimens.c
index 4f82f1e..3159cb0 100644
--- a/sysdeps/mach/hurd/futimens.c
+++ b/sysdeps/mach/hurd/futimens.c
@@ -27,24 +27,51 @@
 int
 __futimens (int fd, const struct timespec tsp[2])
 {
-  time_value_t atime, mtime;
+  struct timespec atime, mtime;
   error_t err;
 
   if (tsp == NULL)
     {
-      /* Setting the number of microseconds to `-1' tells the
+      /* Setting the number of nanoseconds to UTIME_NOW tells the
          underlying filesystems to use the current time.  */
-      atime.microseconds = mtime.microseconds = -1;
+      atime.tv_sec = 0;
+      atime.tv_nsec = UTIME_NOW;
+      mtime.tv_sec = 0;
+      mtime.tv_nsec = UTIME_NOW;
     }
   else
     {
-      atime.seconds = tsp[0].tv_sec;
-      atime.microseconds = tsp[0].tv_nsec / 1000;
-      mtime.seconds = tsp[1].tv_sec;
-      mtime.microseconds = tsp[1].tv_nsec / 1000;
+      atime = tsp[0];
+      mtime = tsp[1];
     }
 
-  err = HURD_DPORT_USE (fd, __file_utimes (port, atime, mtime));
+  err = HURD_DPORT_USE (fd, __file_utimens (port, atime, mtime));
+
+  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      time_value_t atim, mtim;
+
+      if (tsp == NULL)
+        /* Setting the number of microseconds to `-1' tells the
+           underlying filesystems to use the current time.  */
+        atim.microseconds = mtim.microseconds = -1;
+      else if (tsp[0].tv_nsec == UTIME_OMIT || tsp[1].tv_nsec == UTIME_OMIT)
+        return EOPNOTSUPP;
+      else
+        {
+          if (tsp[0].tv_nsec == UTIME_NOW)
+            atim.microseconds = -1;
+          else
+            TIMESPEC_TO_TIME_VALUE (&atim, &(tsp[0]));
+          if (tsp[1].tv_nsec == UTIME_NOW)
+            mtim.microseconds = -1;
+          else
+            TIMESPEC_TO_TIME_VALUE (&mtim, &(tsp[1]));
+        }
+
+      err = HURD_DPORT_USE (fd, __file_utimes (port, atim, mtim));
+  }
+
   return err ? __hurd_dfail (fd, err) : 0;
 }
 weak_alias (__futimens, futimens)
diff --git a/sysdeps/mach/hurd/futimes.c b/sysdeps/mach/hurd/futimes.c
index c325d44..dc8ae61 100644
--- a/sysdeps/mach/hurd/futimes.c
+++ b/sysdeps/mach/hurd/futimes.c
@@ -27,24 +27,44 @@
 int
 __futimes (int fd, const struct timeval tvp[2])
 {
-  union tv
-  {
-    struct timeval tv;
-    time_value_t tvt;
-  };
-  const union tv *u = (const union tv *) tvp;
-  union tv nulltv[2];
+  struct timespec atime, mtime;
   error_t err;
 
   if (tvp == NULL)
     {
-      /* Setting the number of microseconds to `-1' tells the
+      /* Setting the number of nanoseconds to UTIME_NOW tells the
          underlying filesystems to use the current time.  */
-      nulltv[0].tvt.microseconds = nulltv[1].tvt.microseconds = -1;
-      u = nulltv;
+      atime.tv_sec = 0;
+      atime.tv_nsec = UTIME_NOW;
+      mtime.tv_sec = 0;
+      mtime.tv_nsec = UTIME_NOW;
+    }
+  else
+    {
+      TIMEVAL_TO_TIMESPEC (&tvp[0], &atime);
+      TIMEVAL_TO_TIMESPEC (&tvp[1], &mtime);
+    }
+
+  err = HURD_DPORT_USE (fd, __file_utimens (port, atime, mtime));
+  if (err == EMIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      time_value_t atim, mtim;
+
+      if (tvp == NULL)
+        /* Setting the number of microseconds to `-1' tells the
+           underlying filesystems to use the current time.  */
+        atim.microseconds = mtim.microseconds = -1;
+      else
+        {
+          atim.seconds = tvp[0].tv_sec;
+          atim.microseconds = tvp[0].tv_usec;
+          mtim.seconds = tvp[1].tv_sec;
+          mtim.microseconds = tvp[1].tv_usec;
+        }
+
+      err = HURD_DPORT_USE (fd, __file_utimes (port, atim, mtim));
     }
 
-  err = HURD_DPORT_USE (fd, __file_utimes (port, u[0].tvt, u[1].tvt));
   return err ? __hurd_dfail (fd, err) : 0;
 }
 weak_alias (__futimes, futimes)
diff --git a/sysdeps/mach/hurd/lutimes.c b/sysdeps/mach/hurd/lutimes.c
index 260842d..c1d5566 100644
--- a/sysdeps/mach/hurd/lutimes.c
+++ b/sysdeps/mach/hurd/lutimes.c
@@ -27,28 +27,50 @@
 int
 __lutimes (const char *file, const struct timeval tvp[2])
 {
-  union tv
-  {
-    struct timeval tv;
-    time_value_t tvt;
-  };
-  const union tv *u = (const union tv *) tvp;
-  union tv nulltv[2];
+  struct timespec atime, mtime;
   error_t err;
   file_t port;
 
+  port = __file_name_lookup (file, O_NOLINK, 0);
+  if (port == MACH_PORT_NULL)
+    return -1;
+
   if (tvp == NULL)
     {
-      /* Setting the number of microseconds to `-1' tells the
+      /* Setting the number of nanoseconds to UTIME_NOW tells the
          underlying filesystems to use the current time.  */
-      nulltv[0].tvt.microseconds = nulltv[1].tvt.microseconds = -1;
-      u = nulltv;
+      atime.tv_sec = 0;
+      atime.tv_nsec = UTIME_NOW;
+      mtime.tv_sec = 0;
+      mtime.tv_nsec = UTIME_NOW;
+    }
+  else
+    {
+      TIMEVAL_TO_TIMESPEC (&tvp[0], &atime);
+      TIMEVAL_TO_TIMESPEC (&tvp[1], &mtime);
+    }
+
+  err = __file_utimens (port, atime, mtime);
+
+  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      time_value_t atim, mtim;
+
+      if (tvp == NULL)
+        /* Setting the number of microseconds to `-1' tells the
+           underlying filesystems to use the current time.  */
+        atim.microseconds = mtim.microseconds = -1;
+      else
+        {
+          atim.seconds = tvp[0].tv_sec;
+          atim.microseconds = tvp[0].tv_usec;
+          mtim.seconds = tvp[1].tv_sec;
+          mtim.microseconds = tvp[1].tv_usec;
+        }
+
+      err = __file_utimes (port, atim, mtim);
     }
 
-  port = __file_name_lookup (file, O_NOLINK, 0);
-  if (port == MACH_PORT_NULL)
-    return -1;
-  err = __file_utimes (port, u[0].tvt, u[1].tvt);
   __mach_port_deallocate (__mach_task_self (), port);
   if (err)
     return __hurd_fail (err);
diff --git a/sysdeps/mach/hurd/utimes.c b/sysdeps/mach/hurd/utimes.c
index 6739b79..1578637 100644
--- a/sysdeps/mach/hurd/utimes.c
+++ b/sysdeps/mach/hurd/utimes.c
@@ -27,28 +27,50 @@ __utimes (file, tvp)
      const char *file;
      const struct timeval tvp[2];
 {
-  union tv
-  {
-    struct timeval tv;
-    time_value_t tvt;
-  };
-  const union tv *u = (const union tv *) tvp;
-  union tv nulltv[2];
+  struct timespec atime, mtime;
   error_t err;
   file_t port;
 
+  port = __file_name_lookup (file, 0, 0);
+  if (port == MACH_PORT_NULL)
+    return -1;
+
   if (tvp == NULL)
     {
-      /* Setting the number of microseconds to `-1' tells the
+      /* Setting the number of nanoseconds to UTIME_NOW tells the
          underlying filesystems to use the current time.  */
-      nulltv[0].tvt.microseconds = nulltv[1].tvt.microseconds = -1;
-      u = nulltv;
+      atime.tv_sec = 0;
+      atime.tv_nsec = UTIME_NOW;
+      mtime.tv_sec = 0;
+      mtime.tv_nsec = UTIME_NOW;
+    }
+  else
+    {
+      TIMEVAL_TO_TIMESPEC (&tvp[0], &atime);
+      TIMEVAL_TO_TIMESPEC (&tvp[1], &mtime);
+    }
+
+  err = __file_utimens (port, atime, mtime);
+
+  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      time_value_t atim, mtim;
+
+      if (tvp == NULL)
+        /* Setting the number of microseconds to `-1' tells the
+           underlying filesystems to use the current time.  */
+        atim.microseconds = mtim.microseconds = -1;
+      else
+        {
+          atim.seconds = tvp[0].tv_sec;
+          atim.microseconds = tvp[0].tv_usec;
+          mtim.seconds = tvp[1].tv_sec;
+          mtim.microseconds = tvp[1].tv_usec;
+        }
+
+      err = __file_utimes (port, atim, mtim);
     }
 
-  port = __file_name_lookup (file, 0, 0);
-  if (port == MACH_PORT_NULL)
-    return -1;
-  err = __file_utimes (port, u[0].tvt, u[1].tvt);
   __mach_port_deallocate (__mach_task_self (), port);
   if (err)
     return __hurd_fail (err);
diff --git a/console-client/trans.c b/console-client/trans.c
index 224229e..4c78e46 100644
--- a/console-client/trans.c
+++ b/console-client/trans.c
@@ -186,14 +186,10 @@ netfs_attempt_utimes (struct iouser *cred, struct node *np,
   if (! err)
     {
       if (mtime)
-	np->nn_stat.st_mtim = *mtime;
-      else
-        flags |= TOUCH_MTIME;
+        np->nn_stat.st_mtim = *mtime;
       
       if (atime)
-	np->nn_stat.st_atim = *atime;
-      else
-        flags |= TOUCH_ATIME;
+        np->nn_stat.st_atim = *atime;
       
       fshelp_touch (&np->nn_stat, flags, console_maptime);
     }
diff --git a/console/console.c b/console/console.c
index 57ae813..1be1112 100644
--- a/console/console.c
+++ b/console/console.c
@@ -506,14 +506,10 @@ netfs_attempt_utimes (struct iouser *cred, struct node *node,
   if (! err)
     {
       if (mtime)
-	node->nn_stat.st_mtim = *mtime;
-      else
-	flags |= TOUCH_MTIME;
+        node->nn_stat.st_mtim = *mtime;
       
       if (atime)
-	node->nn_stat.st_atim = *atime;
-      else
-	flags |= TOUCH_ATIME;
+        node->nn_stat.st_atim = *atime;
       
       fshelp_touch (&node->nn_stat, flags, console_maptime);
     }
diff --git a/doc/hurd.texi b/doc/hurd.texi
index 2f36bdc..26e51e1 100644
--- a/doc/hurd.texi
+++ b/doc/hurd.texi
@@ -2723,6 +2723,14 @@ the file.  Making this call must cause the @var{ctime} to be updated as
 well, even if no actual change to either the @var{mtime} or the
 @var{atime} occurs.
 
+@findex file_utimens
+The @code{file_utimens} RPC changes the @var{atime} and @var{mtime} of
+the file with nanosecond precision. Making this call must cause the
+@var{ctime} to be updated as well, even if no actual change to either the
+@var{mtime} or the @var{atime} occurs. The arguments @var{atime} and
+@var{mtime} follow the POSIX standard and may use the flags
+@code{UTIME_OMIT} and @code{UTIME_NOW}.
+
 @findex file_set_size
 The @code{file_set_size} RPC is special; not only does it change the
 status word specifying the size of the file, but it also changes the
diff --git a/ftpfs/netfs.c b/ftpfs/netfs.c
index cf5d907..b0c80db 100644
--- a/ftpfs/netfs.c
+++ b/ftpfs/netfs.c
@@ -77,14 +77,10 @@ netfs_attempt_utimes (struct iouser *cred, struct node *node,
   if (! err)
     {
       if (atime)
-	node->nn_stat.st_atim = *atime;
-      else
-	flags |= TOUCH_ATIME;
+        node->nn_stat.st_atim = *atime;
 
       if (mtime)
-	node->nn_stat.st_mtim = *mtime;
-      else
-	flags |= TOUCH_MTIME;
+        node->nn_stat.st_mtim = *mtime;
 
       fshelp_touch (&node->nn_stat, flags, ftpfs_maptime);
     }
diff --git a/hostmux/node.c b/hostmux/node.c
index 7167300..f1e1776 100644
--- a/hostmux/node.c
+++ b/hostmux/node.c
@@ -78,16 +78,12 @@ netfs_attempt_utimes (struct iouser *cred, struct node *node,
   
   if (! err)
     {
-      if (mtime)
-	node->nn_stat.st_mtim = *mtime;
-      else
-	flags |= TOUCH_MTIME;
-      
       if (atime)
-	node->nn_stat.st_atim = *atime;
-      else
-	flags |= TOUCH_ATIME;
+	      node->nn_stat.st_atim = *atime;
       
+      if (mtime)
+	      node->nn_stat.st_mtim = *mtime;
+
       fshelp_touch (&node->nn_stat, flags, hostmux_maptime);
     }
   return err;
diff --git a/hurd/fs.defs b/hurd/fs.defs
index a4a48cc..171e43f 100644
--- a/hurd/fs.defs
+++ b/hurd/fs.defs
@@ -371,3 +371,13 @@ routine file_get_source (
 	file: file_t;
 	RPT
 	out source: string_t);
+
+/* Change access and modify times with nanosecond precision */
+/* If the nanoseconds value is UTIME_NOW then the time should be
+   set to the current time and the remainder of the time_value_t ignored.
+   If the nanoseconds value is UTIME_OMIT then the time is ignored. */
+routine file_utimens (
+	utimes_file: file_t;
+	RPT
+	new_atime: timespec_t;
+	new_mtime: timespec_t);
diff --git a/libdiskfs/file-utimes.c b/libdiskfs/file-utimes.c
index 39fac50..f45a4ed 100644
--- a/libdiskfs/file-utimes.c
+++ b/libdiskfs/file-utimes.c
@@ -24,25 +24,54 @@ diskfs_S_file_utimes (struct protid *cred,
 		      time_value_t atime,
 		      time_value_t mtime)
 {
+  struct timespec atim, mtim;
+
+  if (atime.microseconds == -1)
+    {
+      atim.tv_sec = 0;
+      atim.tv_nsec = UTIME_NOW;
+    }
+  else
+    TIME_VALUE_TO_TIMESPEC (&atime, &atim);
+
+  if (mtime.microseconds == -1)
+    {
+      mtim.tv_sec = 0;
+      mtim.tv_nsec = UTIME_NOW;
+    }
+  else
+    TIME_VALUE_TO_TIMESPEC (&mtime, &mtim);
+
+  return diskfs_S_file_utimens (cred, atim, mtim);
+}
+
+/* Implement file_utimens as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_utimens (struct protid *cred,
+		      struct timespec atime,
+		      struct timespec mtime)
+{
   CHANGE_NODE_FIELD (cred,
 		   ({
 		     if (!(err = fshelp_isowner (&np->dn_stat, cred->user)))
 		       {
-			 if (atime.microseconds == -1)
+			 if (atime.tv_nsec == UTIME_NOW)
 			   np->dn_set_atime = 1;
+			 else if (atime.tv_nsec == UTIME_OMIT)
+			   ; /* do nothing */
 			 else
 			   {
-			     np->dn_stat.st_atim.tv_sec = atime.seconds;
-			     np->dn_stat.st_atim.tv_nsec = atime.microseconds * 1000;
+			     np->dn_stat.st_atim = atime;
 			     np->dn_set_atime = 0;
 			   }
 			 
-			 if (mtime.microseconds == -1)
+			 if (mtime.tv_nsec == UTIME_NOW)
 			   np->dn_set_mtime = 1;
+			 else if (mtime.tv_nsec == UTIME_OMIT)
+			   ; /* do nothing */
 			 else
 			   {
-			     np->dn_stat.st_mtim.tv_sec = mtime.seconds;
-			     np->dn_stat.st_mtim.tv_nsec = mtime.microseconds * 1000;
+			     np->dn_stat.st_mtim = mtime;
 			     np->dn_set_mtime = 0;
 			   }
 			 
diff --git a/libnetfs/file-utimes.c b/libnetfs/file-utimes.c
index 1915609..042c43e 100644
--- a/libnetfs/file-utimes.c
+++ b/libnetfs/file-utimes.c
@@ -26,28 +26,52 @@ netfs_S_file_utimes (struct protid *user,
 		     time_value_t atimein,
 		     time_value_t mtimein)
 {
-  struct timespec atime, mtime;
-  error_t err;
+  struct timespec atim, mtim;
 
-  if (atimein.microseconds != -1)
+  if (atimein.microseconds == -1)
     {
-      atime.tv_sec = atimein.seconds;
-      atime.tv_nsec = atimein.microseconds * 1000;
+      atim.tv_sec = 0;
+      atim.tv_nsec = UTIME_NOW;
     }
-  
-  if (mtimein.microseconds != -1)
+  else
+    TIME_VALUE_TO_TIMESPEC (&atimein, &atim);
+
+  if (mtimein.microseconds == -1)
     {
-      mtime.tv_sec = mtimein.seconds;
-      mtime.tv_nsec = mtimein.microseconds * 1000;
+      mtim.tv_sec = 0;
+      mtim.tv_nsec = UTIME_NOW;
     }
-  
+  else
+    TIME_VALUE_TO_TIMESPEC (&mtimein, &mtim);
+
+  return netfs_S_file_utimens (user, atim, mtim);
+}
+
+error_t
+netfs_S_file_utimens (struct protid *user,
+		     struct timespec atimein,
+		     struct timespec mtimein)
+{
+  error_t err;
+  struct timeval t;
+
   if (!user)
     return EOPNOTSUPP;
-  
+
+  if (atimein.tv_nsec == UTIME_NOW || mtimein.tv_nsec == UTIME_NOW)
+    {
+      maptime_read (netfs_mtime, &t);
+
+      if (atimein.tv_nsec == UTIME_NOW)
+        TIMEVAL_TO_TIMESPEC (&t, &atimein);
+      if (mtimein.tv_nsec == UTIME_NOW)
+        TIMEVAL_TO_TIMESPEC (&t, &mtimein);
+    }
+
   pthread_mutex_lock (&user->po->np->lock);
-  err = netfs_attempt_utimes (user->user, user->po->np, 
-			      atimein.microseconds != -1 ? &atime : 0, 
-			      mtimein.microseconds != -1 ? &mtime : 0);
+  err = netfs_attempt_utimes (user->user, user->po->np,
+                  (atimein.tv_nsec == UTIME_OMIT) ? 0 : &atimein,
+                  (mtimein.tv_nsec == UTIME_OMIT) ? 0 : &mtimein);
   pthread_mutex_unlock (&user->po->np->lock);
   return err;
 }
diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
index a088ad5..4b749f5 100644
--- a/libnetfs/init-init.c
+++ b/libnetfs/init-init.c
@@ -32,11 +32,17 @@ struct port_class *netfs_protid_class = 0;
 struct port_class *netfs_control_class = 0;
 auth_t netfs_auth_server_port = 0;
 mach_port_t netfs_fsys_identity;
+volatile struct mapped_time_value *netfs_mtime;
 
 
 void
 netfs_init ()
 {
+  error_t err;
+  err = maptime_map (0, 0, &netfs_mtime);
+  if (err)
+    error (2, err, "mapping time");
+
   netfs_protid_class = ports_create_class (netfs_release_protid, 0);
   netfs_control_class = ports_create_class (0, 0);
   netfs_port_bucket = ports_create_bucket ();
diff --git a/libnetfs/netfs.h b/libnetfs/netfs.h
index fbe2c60..3774070 100644
--- a/libnetfs/netfs.h
+++ b/libnetfs/netfs.h
@@ -173,7 +173,7 @@ error_t netfs_attempt_chflags (struct iouser *cred, struct node *np,
 /* The user must define this function.  This should attempt a utimes
    call for the user specified by CRED on locked node NP, to change
    the atime to ATIME and the mtime to MTIME.  If ATIME or MTIME is
-   null, then set to the current time.  */
+   null, then do not change it.  */
 error_t netfs_attempt_utimes (struct iouser *cred, struct node *np,
 			      struct timespec *atime, struct timespec *mtime);
 
diff --git a/libnetfs/priv.h b/libnetfs/priv.h
index 3c5bcd4..3871da8 100644
--- a/libnetfs/priv.h
+++ b/libnetfs/priv.h
@@ -25,6 +25,8 @@
 
 #include "netfs.h"
 
+volatile struct mapped_time_value *netfs_mtime;
+
 static inline struct protid * __attribute__ ((unused))
 begin_using_protid_port (file_t port)
 {
diff --git a/libtreefs/s-file.c b/libtreefs/s-file.c
index c24d645..6ba67e3 100644
--- a/libtreefs/s-file.c
+++ b/libtreefs/s-file.c
@@ -231,5 +231,33 @@ treefs_S_file_utimes (struct treefs_protid *cred,
 {
   if (!cred)
     return EOPNOTSUPP;
-  return treefs_s_file_utimes (cred, atime, mtime);
+
+  struct timespec atim, mtim;
+
+  if (atime.microseconds == -1)
+    {
+      atim.tv_sec = 0;
+      atim.tv_nsec = UTIME_NOW;
+    }
+  else
+    TIME_VALUE_TO_TIMESPEC (&atime, &atim);
+
+  if (mtime.microseconds == -1)
+    {
+      mtim.tv_sec = 0;
+      mtim.tv_nsec = UTIME_NOW;
+    }
+  else
+    TIME_VALUE_TO_TIMESPEC (&mtime, &mtim);
+
+  return treefs_s_file_utimens (cred, atim, mtim);
+}
+
+error_t
+treefs_S_file_utimens (struct treefs_protid *cred,
+		      struct timespec atime, struct timespec mtime)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+  return treefs_s_file_utimens (cred, atime, mtime);
 }
diff --git a/libtreefs/treefs-hooks.h b/libtreefs/treefs-hooks.h
index 6dc2f73..49dbb41 100644
--- a/libtreefs/treefs-hooks.h
+++ b/libtreefs/treefs-hooks.h
@@ -33,7 +33,7 @@ enum
   /* file rpcs */
   TREEFS_HOOK_S_FILE_EXEC, TREEFS_HOOK_S_FILE_CHOWN,
   TREEFS_HOOK_S_FILE_CHAUTHOR, TREEFS_HOOK_S_FILE_CHMOD,
-  TREEFS_HOOK_S_FILE_CHFLAGS, TREEFS_HOOK_S_FILE_UTIMES,
+  TREEFS_HOOK_S_FILE_CHFLAGS, TREEFS_HOOK_S_FILE_UTIMENS,
   TREEFS_HOOK_S_FILE_SET_SIZE, TREEFS_HOOK_S_FILE_LOCK,
   TREEFS_HOOK_S_FILE_LOCK_STAT, TREEFS_HOOK_S_FILE_ACCESS,
   TREEFS_HOOK_S_FILE_NOTICE, TREEFS_HOOK_S_FILE_SYNC,
diff --git a/libtreefs/treefs-s-hooks.h b/libtreefs/treefs-s-hooks.h
index 2ea9e7a..9b2489d 100644
--- a/libtreefs/treefs-s-hooks.h
+++ b/libtreefs/treefs-s-hooks.h
@@ -53,9 +53,9 @@ DHH(s_file_chmod, error_t, mode_t)
 DHH(s_file_chflags, error_t, int)
 #define treefs_s_file_chflags(h, args...)				      \
   _TREEFS_CHH(h, S_FILE_CHFLAGS, s_file_chflags , ##args)
-DHH(s_file_utimes, error_t, time_value_t, time_value_t)
-#define treefs_s_file_utimes(h, args...)				      \
-  _TREEFS_CHH(h, S_FILE_UTIMES, s_file_utimes , ##args)
+DHH(s_file_utimens, error_t, struct timespec, struct timespec)
+#define treefs_s_file_utimens(h, args...)				      \
+  _TREEFS_CHH(h, S_FILE_UTIMENS, s_file_utimens , ##args)
 DHH(s_file_truncate, error_t, off_t)
 #define treefs_s_file_truncate(h, args...)				      \
   _TREEFS_CHH(h, S_FILE_TRUNCATE, s_file_truncate , ##args)
diff --git a/libtrivfs/file-utimes.c b/libtrivfs/file-utimes.c
index 827c055..ceee891 100644
--- a/libtrivfs/file-utimes.c
+++ b/libtrivfs/file-utimes.c
@@ -25,3 +25,35 @@ trivfs_S_file_utimes (struct trivfs_protid *cred,
 {
   return cred ? file_utimes (cred->realnode, atime, mtime) : EOPNOTSUPP;
 }
+
+kern_return_t
+trivfs_S_file_utimens (struct trivfs_protid *cred,
+		      mach_port_t reply, mach_msg_type_name_t reply_type,
+		      struct timespec atime, struct timespec mtime)
+{
+  kern_return_t err;
+
+  if (!cred)
+    return EOPNOTSUPP;
+
+  err = file_utimens (cred->realnode, atime, mtime);
+
+  if (err == EMIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      time_value_t atim, mtim;
+
+      if (atime.tv_nsec == UTIME_NOW)
+        atim.microseconds = -1;
+      else
+        TIMESPEC_TO_TIME_VALUE (&atim, &atime);
+
+      if (mtime.tv_nsec == UTIME_NOW)
+        mtim.microseconds = -1;
+      else
+        TIMESPEC_TO_TIME_VALUE (&mtim, &mtime);
+
+      err = file_utimes (cred->realnode, atim, mtim);
+    }
+
+  return err;
+}
diff --git a/libtrivfs/times.c b/libtrivfs/times.c
index 5f08cb1..2bd594d 100644
--- a/libtrivfs/times.c
+++ b/libtrivfs/times.c
@@ -20,29 +20,57 @@
 error_t
 trivfs_set_atime (struct trivfs_control *cntl)
 {
-  struct stat st;
-  time_value_t atime;
-  time_value_t mtime;
+  struct timespec atime;
+  struct timespec mtime;
+  error_t err;
   
-  io_stat (cntl->underlying, &st);
-  mtime.seconds = st.st_mtim.tv_sec;
-  mtime.microseconds = st.st_mtim.tv_nsec / 1000;
-  atime.microseconds = -1;
-  file_utimes (cntl->underlying, atime, mtime);
-  return 0;
+  atime.tv_sec = 0;
+  atime.tv_nsec = UTIME_NOW;
+  mtime.tv_sec = 0;
+  mtime.tv_nsec = UTIME_OMIT;
+
+  err = file_utimens (cntl->underlying, atime, mtime);
+
+  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      struct stat st;
+      time_value_t atim, mtim;
+
+      io_stat (cntl->underlying, &st);
+
+      atim.microseconds = -1;
+      TIMESPEC_TO_TIME_VALUE (&mtim, &st.st_mtim);
+      err = file_utimes (cntl->underlying, atim, mtim);
+    }
+
+  return err;
 }
 
 error_t
 trivfs_set_mtime (struct trivfs_control *cntl)
 {
-  struct stat st;
-  time_value_t atime;
-  time_value_t mtime;
-
-  io_stat (cntl->underlying, &st);
-  atime.seconds = st.st_atim.tv_sec;
-  atime.microseconds = st.st_atim.tv_nsec / 1000;
-  mtime.microseconds = -1;
-  file_utimes (cntl->underlying, atime, mtime);
-  return 0;
+  struct timespec atime;
+  struct timespec mtime;
+  error_t err;
+
+  atime.tv_sec = 0;
+  atime.tv_nsec = UTIME_OMIT;
+  mtime.tv_sec = 0;
+  mtime.tv_nsec = UTIME_NOW;
+
+  err = file_utimens (cntl->underlying, atime, mtime);
+
+  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      struct stat st;
+      time_value_t atim, mtim;
+
+      io_stat (cntl->underlying, &st);
+
+      TIMESPEC_TO_TIME_VALUE (&atim, &st.st_atim);
+      mtim.microseconds = -1;
+      err = file_utimes (cntl->underlying, atim, mtim);
+    }
+
+  return err;
 }
diff --git a/nfs/nfs.c b/nfs/nfs.c
index 4916df6..ba2891a 100644
--- a/nfs/nfs.c
+++ b/nfs/nfs.c
@@ -273,10 +273,26 @@ xdr_encode_sattr_times (int *p, struct timespec *atime, struct timespec *mtime)
       *(p++) = -1;			/* uid */
       *(p++) = -1;			/* gid */
       *(p++) = -1;			/* size */
-      *(p++) = htonl (atime->tv_sec);
-      *(p++) = htonl (atime->tv_nsec / 1000);
-      *(p++) = htonl (mtime->tv_sec);
-      *(p++) = htonl (mtime->tv_nsec / 1000);
+      if (atime)
+       {
+        *(p++) = htonl (atime->tv_sec);
+        *(p++) = htonl (atime->tv_nsec / 1000);
+       }
+      else
+       {
+        *(p++) = -1; /* no atime */
+        *(p++) = -1;
+       }
+      if (mtime)
+       {
+        *(p++) = htonl (mtime->tv_sec);
+        *(p++) = htonl (mtime->tv_nsec / 1000);
+       }
+      else
+       {
+         *(p++) = -1; /* no mtime */
+         *(p++) = -1;
+       }
     }
   else
     {
@@ -284,12 +300,22 @@ xdr_encode_sattr_times (int *p, struct timespec *atime, struct timespec *mtime)
       *(p++) = 0;			/* no uid */
       *(p++) = 0;			/* no gid */
       *(p++) = 0;			/* no size */
-      *(p++) = htonl (SET_TO_CLIENT_TIME); /* atime */
-      *(p++) = htonl (atime->tv_sec);
-      *(p++) = htonl (atime->tv_nsec);
-      *(p++) = htonl (SET_TO_CLIENT_TIME); /* mtime */
-      *(p++) = htonl (mtime->tv_sec);
-      *(p++) = htonl (mtime->tv_nsec);
+      if (atime)
+        {
+          *(p++) = htonl (SET_TO_CLIENT_TIME); /* atime */
+          *(p++) = htonl (atime->tv_sec);
+          *(p++) = htonl (atime->tv_nsec);
+        }
+      else
+        *(p++) = DONT_CHANGE;	/* no atime */
+      if (mtime)
+        {
+          *(p++) = htonl (SET_TO_CLIENT_TIME); /* mtime */
+          *(p++) = htonl (mtime->tv_sec);
+          *(p++) = htonl (mtime->tv_nsec);
+        }
+      else
+        *(p++) = DONT_CHANGE;	/* no mtime */
     }
   return p;
 }
diff --git a/nfs/ops.c b/nfs/ops.c
index a4d6ac7..faae658 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -298,17 +298,13 @@ netfs_attempt_utimes (struct iouser *cred, struct node *np,
   int *p;
   void *rpcbuf;
   error_t err;
-  struct timeval tv;
   struct timespec current;
 
+  if (!atime && !mtime)
+    return 0; /* nothing to update */
+
   /* XXX For version 3 we can actually do this right, but we don't
      just yet. */
-  if (!atime || !mtime)
-    {
-      maptime_read (mapped_time, &tv);
-      current.tv_sec = tv.tv_sec;
-      current.tv_nsec = tv.tv_usec * 1000;
-    }
 
   p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
 			  cred, 0, &rpcbuf, np, -1);
@@ -316,9 +312,7 @@ netfs_attempt_utimes (struct iouser *cred, struct node *np,
     return errno;
 
   p = xdr_encode_fhandle (p, &np->nn->handle);
-  p = xdr_encode_sattr_times (p,
-			      atime ?: &current,
-			      mtime ?: &current);
+  p = xdr_encode_sattr_times (p, atime, mtime);
   if (protocol_version == 3)
     *(p++) = 0;			/* guard check == 0 */
 
diff --git a/nfsd/ops.c b/nfsd/ops.c
index 6e2cbb1..2b75a18 100644
--- a/nfsd/ops.c
+++ b/nfsd/ops.c
@@ -63,7 +63,7 @@ complete_setattr (mach_port_t port,
 {
   uid_t uid, gid;
   off_t size;
-  time_value_t atime, mtime;
+  struct timespec atime, mtime;
   struct stat st;
   error_t err;
 
@@ -91,34 +91,46 @@ complete_setattr (mach_port_t port,
   if (err)
     return err;
 
-  atime.seconds = ntohl (*p);
+  atime.tv_sec = ntohl (*p);
   p++;
-  atime.microseconds = ntohl (*p);
+  atime.tv_nsec = ntohl (*p) * 1000;
   p++;
-  mtime.seconds = ntohl (*p);
+  mtime.tv_sec = ntohl (*p);
   p++;
-  mtime.microseconds = ntohl (*p);
+  mtime.tv_nsec = ntohl (*p) * 1000;
   p++;
 
-  if (atime.seconds != -1 && atime.microseconds == -1)
-    atime.microseconds = 0;
-  if (mtime.seconds != -1 && mtime.microseconds == -1)
-    mtime.microseconds = 0;
-
-  if (atime.seconds == -1)
-    atime.seconds = st.st_atim.tv_sec;
-  if (atime.microseconds == -1)
-    atime.microseconds = st.st_atim.tv_nsec / 1000;
-  if (mtime.seconds == -1)
-    mtime.seconds = st.st_mtim.tv_sec;
-  if (mtime.microseconds == -1)
-    mtime.microseconds = st.st_mtim.tv_nsec / 1000;
-
-  if (atime.seconds != st.st_atim.tv_sec
-      || atime.microseconds != st.st_atim.tv_nsec / 1000
-      || mtime.seconds != st.st_mtim.tv_sec
-      || mtime.microseconds != st.st_mtim.tv_nsec / 1000)
-    err = file_utimes (port, atime, mtime);
+  if (atime.tv_sec != -1 && atime.tv_nsec == -1)
+    atime.tv_nsec = 0;
+  if (mtime.tv_sec != -1 && mtime.tv_nsec == -1)
+    mtime.tv_nsec = 0;
+
+  if (atime.tv_nsec == -1)
+    atime.tv_sec = st.st_atim.tv_sec;
+  if (atime.tv_nsec == -1)
+    atime.tv_nsec = st.st_atim.tv_nsec;
+  if (mtime.tv_sec == -1)
+    mtime.tv_sec = st.st_mtim.tv_sec;
+  if (mtime.tv_nsec == -1)
+    mtime.tv_nsec = st.st_mtim.tv_nsec;
+
+  if (atime.tv_sec != st.st_atim.tv_sec
+      || atime.tv_nsec != st.st_atim.tv_nsec
+      || mtime.tv_sec != st.st_mtim.tv_sec
+      || mtime.tv_nsec != st.st_mtim.tv_nsec)
+    {
+      err = file_utimens (port, atime, mtime);
+
+      if (err == MIG_BAD_ID)
+        {
+          time_value_t atim, mtim;
+
+          TIMESPEC_TO_TIME_VALUE (&atim, &atime);
+          TIMESPEC_TO_TIME_VALUE (&mtim, &mtime);
+
+          err = file_utimes (port, atim, mtim);
+        }
+    }
 
   return err;
 }
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
index 76fc901..6ef11d0 100644
--- a/trans/fakeroot.c
+++ b/trans/fakeroot.c
@@ -618,26 +618,45 @@ error_t
 netfs_attempt_utimes (struct iouser *cred, struct node *np,
 		      struct timespec *atime, struct timespec *mtime)
 {
-  union tv
-  {
-    struct timeval tv;
-    time_value_t tvt;
-  };
-  union tv a, m;
+  struct timespec tatime, tmtime;
+  error_t err;
+
   if (atime)
+    tatime = *atime;
+  else
     {
-      TIMESPEC_TO_TIMEVAL (&a.tv, atime);
+      tatime.tv_sec = 0;
+      tatime.tv_nsec = UTIME_OMIT;
     }
-  else
-    a.tv.tv_sec = a.tv.tv_usec = -1;
+
   if (mtime)
+    tmtime = *mtime;
+  else
     {
-      TIMESPEC_TO_TIMEVAL (&m.tv, mtime);
+      tmtime.tv_sec = 0;
+      tmtime.tv_nsec = UTIME_OMIT;
     }
-  else
-    m.tv.tv_sec = m.tv.tv_usec = -1;
 
-  return file_utimes (netfs_node_netnode (np)->file, a.tvt, m.tvt);
+  err = file_utimens (netfs_node_netnode (np)->file, tatime, tmtime);
+
+  if(err == EMIG_BAD_ID || err == EOPNOTSUPP)
+    {
+      time_value_t atim, mtim;
+
+      if(atime)
+        TIMESPEC_TO_TIME_VALUE (&atim, atime);
+      else
+        atim.seconds = atim.microseconds = -1;
+
+      if (mtime)
+        TIMESPEC_TO_TIME_VALUE (&mtim, mtime);
+      else
+        mtim.seconds = mtim.microseconds = -1;
+
+      err = file_utimes (netfs_node_netnode (np)->file, atim, mtim);
+    }
+
+  return err;
 }
 
 error_t
diff --git a/usermux/node.c b/usermux/node.c
index 2341714..66bf79b 100644
--- a/usermux/node.c
+++ b/usermux/node.c
@@ -81,14 +81,10 @@ netfs_attempt_utimes (struct iouser *cred, struct node *node,
   if (! err)
     {
       if (mtime)
-	node->nn_stat.st_mtim = *mtime;
-      else
-	flags |= TOUCH_MTIME;
+        node->nn_stat.st_mtim = *mtime;
 
       if (atime)
-	node->nn_stat.st_atim = *atime;
-      else
-	flags |= TOUCH_ATIME;
+        node->nn_stat.st_atim = *atime;
 
       fshelp_touch (&node->nn_stat, flags, usermux_maptime);
     }

Reply via email to