[glib: 2/5] Add functionality to preserve nanosecond timestamps




commit b33ef610deefc4ba54bd1404b0fd292459e255f3
Author: nitinosiris <nitinwartkar58 gmail com>
Date:   Fri Jul 2 18:40:44 2021 +0530

    Add functionality to preserve nanosecond timestamps
    
    file copy doesn't preserve nanosecond timestamps
    
    Closes #369

 docs/reference/gio/gio-sections-common.txt |   4 +
 gio/gfileinfo-priv.h                       |   4 +
 gio/gfileinfo.c                            |  45 ++++++++-
 gio/gfileinfo.h                            |  48 ++++++++++
 gio/glocalfile.c                           |  14 ++-
 gio/glocalfileinfo.c                       | 144 +++++++++++++++++++++++++----
 gio/gzlibdecompressor.c                    |   3 +
 gio/tests/file.c                           |  14 +++
 meson.build                                |   1 +
 9 files changed, 252 insertions(+), 25 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt
index 383cfd1a16..603338ffbe 100644
--- a/docs/reference/gio/gio-sections-common.txt
+++ b/docs/reference/gio/gio-sections-common.txt
@@ -314,12 +314,16 @@ G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL
 G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC
 G_FILE_ATTRIBUTE_TIME_MODIFIED
 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC
+G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC
 G_FILE_ATTRIBUTE_TIME_ACCESS
 G_FILE_ATTRIBUTE_TIME_ACCESS_USEC
+G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC
 G_FILE_ATTRIBUTE_TIME_CHANGED
 G_FILE_ATTRIBUTE_TIME_CHANGED_USEC
+G_FILE_ATTRIBUTE_TIME_CHANGED_NSEC
 G_FILE_ATTRIBUTE_TIME_CREATED
 G_FILE_ATTRIBUTE_TIME_CREATED_USEC
+G_FILE_ATTRIBUTE_TIME_CREATED_NSEC
 G_FILE_ATTRIBUTE_UNIX_DEVICE
 G_FILE_ATTRIBUTE_UNIX_INODE
 G_FILE_ATTRIBUTE_UNIX_MODE
diff --git a/gio/gfileinfo-priv.h b/gio/gfileinfo-priv.h
index 9d3a061c70..8fc085973b 100644
--- a/gio/gfileinfo-priv.h
+++ b/gio/gfileinfo-priv.h
@@ -75,6 +75,10 @@
 #define G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC (6291456 + 6)
 #define G_FILE_ATTRIBUTE_ID_TIME_CREATED (6291456 + 7)
 #define G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC (6291456 + 8)
+#define G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_NSEC (6291456 + 9)
+#define G_FILE_ATTRIBUTE_ID_TIME_ACCESS_NSEC (6291456 + 10)
+#define G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC (6291456 + 11)
+#define G_FILE_ATTRIBUTE_ID_TIME_CHANGED_NSEC (6291456 + 12)
 #define G_FILE_ATTRIBUTE_ID_UNIX_DEVICE (7340032 + 1)
 #define G_FILE_ATTRIBUTE_ID_UNIX_INODE (7340032 + 2)
 #define G_FILE_ATTRIBUTE_ID_UNIX_MODE (7340032 + 3)
diff --git a/gio/gfileinfo.c b/gio/gfileinfo.c
index ed700e3b1a..f709bf4f1f 100644
--- a/gio/gfileinfo.c
+++ b/gio/gfileinfo.c
@@ -241,6 +241,10 @@ ensure_attribute_hash (void)
   REGISTER_ATTRIBUTE (TIME_CHANGED_USEC);
   REGISTER_ATTRIBUTE (TIME_CREATED);
   REGISTER_ATTRIBUTE (TIME_CREATED_USEC);
+  REGISTER_ATTRIBUTE (TIME_MODIFIED_NSEC);
+  REGISTER_ATTRIBUTE (TIME_ACCESS_NSEC);
+  REGISTER_ATTRIBUTE (TIME_CREATED_NSEC);
+  REGISTER_ATTRIBUTE (TIME_CHANGED_NSEC);
   REGISTER_ATTRIBUTE (UNIX_DEVICE);
   REGISTER_ATTRIBUTE (UNIX_INODE);
   REGISTER_ATTRIBUTE (UNIX_MODE);
@@ -1820,6 +1824,9 @@ G_GNUC_END_IGNORE_DEPRECATIONS
  * %G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC is provided, the resulting #GDateTime
  * will have microsecond precision.
  *
+ * If nanosecond precision is needed, %G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC must
+ * be queried separately using g_file_info_get_attribute_uint32().
+ *
  * Returns: (transfer full) (nullable): modification time, or %NULL if unknown
  * Since: 2.62
  */
@@ -1865,6 +1872,9 @@ g_file_info_get_modification_date_time (GFileInfo *info)
  * %G_FILE_ATTRIBUTE_TIME_ACCESS_USEC is provided, the resulting #GDateTime
  * will have microsecond precision.
  *
+ * If nanosecond precision is needed, %G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC must
+ * be queried separately using g_file_info_get_attribute_uint32().
+ *
  * Returns: (transfer full) (nullable): access time, or %NULL if unknown
  * Since: 2.70
  */
@@ -1910,6 +1920,9 @@ g_file_info_get_access_date_time (GFileInfo *info)
  * %G_FILE_ATTRIBUTE_TIME_CREATED_USEC is provided, the resulting #GDateTime
  * will have microsecond precision.
  *
+ * If nanosecond precision is needed, %G_FILE_ATTRIBUTE_TIME_CREATED_NSEC must
+ * be queried separately using g_file_info_get_attribute_uint32().
+ *
  * Returns: (transfer full) (nullable): creation time, or %NULL if unknown
  * Since: 2.70
  */
@@ -2283,6 +2296,8 @@ g_file_info_set_size (GFileInfo *info,
  * %G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC attributes in the file info to the
  * given time value.
  *
+ * %G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC will be cleared.
+ *
  * Deprecated: 2.62: Use g_file_info_set_modification_date_time() instead, as
  *    #GTimeVal is deprecated due to the year 2038 problem.
  **/
@@ -2291,7 +2306,7 @@ void
 g_file_info_set_modification_time (GFileInfo *info,
                                   GTimeVal  *mtime)
 {
-  static guint32 attr_mtime = 0, attr_mtime_usec;
+  static guint32 attr_mtime = 0, attr_mtime_usec = 0, attr_mtime_nsec = 0;
   GFileAttributeValue *value;
 
   g_return_if_fail (G_IS_FILE_INFO (info));
@@ -2301,6 +2316,7 @@ g_file_info_set_modification_time (GFileInfo *info,
     {
       attr_mtime = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED);
       attr_mtime_usec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
+      attr_mtime_nsec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
     }
 
   value = g_file_info_create_value (info, attr_mtime);
@@ -2309,6 +2325,9 @@ g_file_info_set_modification_time (GFileInfo *info,
   value = g_file_info_create_value (info, attr_mtime_usec);
   if (value)
     _g_file_attribute_value_set_uint32 (value, mtime->tv_usec);
+
+  /* nsecs can’t be known from a #GTimeVal, so remove them */
+  g_file_info_remove_value (info, attr_mtime_nsec);
 }
 G_GNUC_END_IGNORE_DEPRECATIONS
 
@@ -2321,13 +2340,15 @@ G_GNUC_END_IGNORE_DEPRECATIONS
  * %G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC attributes in the file info to the
  * given date/time value.
  *
+ * %G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC will be cleared.
+ *
  * Since: 2.62
  */
 void
 g_file_info_set_modification_date_time (GFileInfo *info,
                                         GDateTime *mtime)
 {
-  static guint32 attr_mtime = 0, attr_mtime_usec;
+  static guint32 attr_mtime = 0, attr_mtime_usec = 0, attr_mtime_nsec = 0;
   GFileAttributeValue *value;
 
   g_return_if_fail (G_IS_FILE_INFO (info));
@@ -2337,6 +2358,7 @@ g_file_info_set_modification_date_time (GFileInfo *info,
     {
       attr_mtime = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED);
       attr_mtime_usec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
+      attr_mtime_nsec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
     }
 
   value = g_file_info_create_value (info, attr_mtime);
@@ -2345,6 +2367,9 @@ g_file_info_set_modification_date_time (GFileInfo *info,
   value = g_file_info_create_value (info, attr_mtime_usec);
   if (value)
     _g_file_attribute_value_set_uint32 (value, g_date_time_get_microsecond (mtime));
+
+  /* nsecs can’t be known from a #GDateTime, so remove them */
+  g_file_info_remove_value (info, attr_mtime_nsec);
 }
 
 /**
@@ -2356,13 +2381,15 @@ g_file_info_set_modification_date_time (GFileInfo *info,
  * %G_FILE_ATTRIBUTE_TIME_ACCESS_USEC attributes in the file info to the
  * given date/time value.
  *
+ * %G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC will be cleared.
+ *
  * Since: 2.70
  */
 void
 g_file_info_set_access_date_time (GFileInfo *info,
                                   GDateTime *atime)
 {
-  static guint32 attr_atime = 0, attr_atime_usec;
+  static guint32 attr_atime = 0, attr_atime_usec = 0, attr_atime_nsec = 0;
   GFileAttributeValue *value;
 
   g_return_if_fail (G_IS_FILE_INFO (info));
@@ -2372,6 +2399,7 @@ g_file_info_set_access_date_time (GFileInfo *info,
     {
       attr_atime = lookup_attribute (G_FILE_ATTRIBUTE_TIME_ACCESS);
       attr_atime_usec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
+      attr_atime_nsec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC);
     }
 
   value = g_file_info_create_value (info, attr_atime);
@@ -2380,6 +2408,9 @@ g_file_info_set_access_date_time (GFileInfo *info,
   value = g_file_info_create_value (info, attr_atime_usec);
   if (value)
     _g_file_attribute_value_set_uint32 (value, g_date_time_get_microsecond (atime));
+
+  /* nsecs can’t be known from a #GDateTime, so remove them */
+  g_file_info_remove_value (info, attr_atime_nsec);
 }
 
 /**
@@ -2391,13 +2422,15 @@ g_file_info_set_access_date_time (GFileInfo *info,
  * %G_FILE_ATTRIBUTE_TIME_CREATED_USEC attributes in the file info to the
  * given date/time value.
  *
+ * %G_FILE_ATTRIBUTE_TIME_CREATED_NSEC will be cleared.
+ *
  * Since: 2.70
  */
 void
 g_file_info_set_creation_date_time (GFileInfo *info,
                                     GDateTime *creation_time)
 {
-  static guint32 attr_ctime = 0, attr_ctime_usec;
+  static guint32 attr_ctime = 0, attr_ctime_usec = 0, attr_ctime_nsec = 0;
   GFileAttributeValue *value;
 
   g_return_if_fail (G_IS_FILE_INFO (info));
@@ -2407,6 +2440,7 @@ g_file_info_set_creation_date_time (GFileInfo *info,
     {
       attr_ctime = lookup_attribute (G_FILE_ATTRIBUTE_TIME_CREATED);
       attr_ctime_usec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_CREATED_USEC);
+      attr_ctime_nsec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_CREATED_NSEC);
     }
 
   value = g_file_info_create_value (info, attr_ctime);
@@ -2415,6 +2449,9 @@ g_file_info_set_creation_date_time (GFileInfo *info,
   value = g_file_info_create_value (info, attr_ctime_usec);
   if (value)
     _g_file_attribute_value_set_uint32 (value, g_date_time_get_microsecond (creation_time));
+
+  /* nsecs can’t be known from a #GDateTime, so remove them */
+  g_file_info_remove_value (info, attr_ctime_nsec);
 }
 
 /**
diff --git a/gio/gfileinfo.h b/gio/gfileinfo.h
index 058bccbaf2..28bddfebcd 100644
--- a/gio/gfileinfo.h
+++ b/gio/gfileinfo.h
@@ -562,6 +562,18 @@ typedef struct _GFileInfoClass   GFileInfoClass;
  **/
 #define G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "time::modified-usec" /* uint32 */
 
+/**
+ * G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC:
+ *
+ * A key in the "time" namespace for getting the nanoseconds of the time
+ * the file was last modified. This should be used in conjunction with
+ * #G_FILE_ATTRIBUTE_TIME_MODIFIED. Corresponding #GFileAttributeType is
+ * %G_FILE_ATTRIBUTE_TYPE_UINT32.
+ *
+ * Since: 2.74
+ **/
+#define G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC "time::modified-nsec" /* uint32 */
+
 /**
  * G_FILE_ATTRIBUTE_TIME_ACCESS:
  *
@@ -586,6 +598,18 @@ typedef struct _GFileInfoClass   GFileInfoClass;
  **/
 #define G_FILE_ATTRIBUTE_TIME_ACCESS_USEC "time::access-usec"     /* uint32 */
 
+/**
+ * G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC:
+ *
+ * A key in the "time" namespace for getting the nanoseconds of the time
+ * the file was last accessed. This should be used in conjunction with
+ * #G_FILE_ATTRIBUTE_TIME_ACCESS. Corresponding #GFileAttributeType is
+ * %G_FILE_ATTRIBUTE_TYPE_UINT32.
+ *
+ * Since: 2.74
+ **/
+#define G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC "time::access-nsec"     /* uint32 */
+
 /**
  * G_FILE_ATTRIBUTE_TIME_CHANGED:
  *
@@ -612,6 +636,18 @@ typedef struct _GFileInfoClass   GFileInfoClass;
  **/
 #define G_FILE_ATTRIBUTE_TIME_CHANGED_USEC "time::changed-usec"   /* uint32 */
 
+/**
+ * G_FILE_ATTRIBUTE_TIME_CHANGED_NSEC:
+ *
+ * A key in the "time" namespace for getting the nanoseconds of the time
+ * the file was last changed. This should be used in conjunction with
+ * #G_FILE_ATTRIBUTE_TIME_CHANGED. Corresponding #GFileAttributeType is
+ * %G_FILE_ATTRIBUTE_TYPE_UINT32.
+ *
+ * Since: 2.74
+ **/
+#define G_FILE_ATTRIBUTE_TIME_CHANGED_NSEC "time::changed-nsec"   /* uint32 */
+
 /**
  * G_FILE_ATTRIBUTE_TIME_CREATED:
  *
@@ -638,6 +674,18 @@ typedef struct _GFileInfoClass   GFileInfoClass;
  **/
 #define G_FILE_ATTRIBUTE_TIME_CREATED_USEC "time::created-usec"   /* uint32 */
 
+/**
+ * G_FILE_ATTRIBUTE_TIME_CREATED_NSEC:
+ *
+ * A key in the "time" namespace for getting the nanoseconds of the time
+ * the file was created. This should be used in conjunction with
+ * #G_FILE_ATTRIBUTE_TIME_CREATED. Corresponding #GFileAttributeType is
+ * %G_FILE_ATTRIBUTE_TYPE_UINT32.
+ *
+ * Since: 2.74
+ **/
+#define G_FILE_ATTRIBUTE_TIME_CREATED_NSEC "time::created-nsec"   /* uint32 */
+
 /* Unix specific attributes */
 
 /**
diff --git a/gio/glocalfile.c b/gio/glocalfile.c
index 43a6eb79d6..ed7a663d2a 100644
--- a/gio/glocalfile.c
+++ b/gio/glocalfile.c
@@ -168,7 +168,7 @@ g_local_file_class_init (GLocalFileClass *klass)
                                  0);
 #endif
   
-#ifdef HAVE_UTIMES
+#if defined(HAVE_UTIMES) || defined(HAVE_UTIMENSAT)
   g_file_attribute_info_list_add (list,
                                  G_FILE_ATTRIBUTE_TIME_MODIFIED,
                                  G_FILE_ATTRIBUTE_TYPE_UINT64,
@@ -190,6 +190,18 @@ g_local_file_class_init (GLocalFileClass *klass)
                                  G_FILE_ATTRIBUTE_TIME_ACCESS_USEC,
                                  G_FILE_ATTRIBUTE_TYPE_UINT32,
                                  G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+#endif  /* HAVE_UTIMES || HAVE_UTIMENSAT */
+
+#ifdef HAVE_UTIMENSAT
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT32,
+                                 G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
+                                 G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT32,
+                                 G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
 #endif
 
   local_writable_attributes = list;
diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c
index 59869735fd..edec7bddea 100644
--- a/gio/glocalfileinfo.c
+++ b/gio/glocalfileinfo.c
@@ -123,25 +123,29 @@ static GHashTable *gid_cache = NULL;
 char *
 _g_local_file_info_create_etag (GLocalFileStat *statbuf)
 {
-  glong sec, usec;
+  glong sec, usec, nsec;
 
   g_return_val_if_fail (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_MTIME), NULL);
 
 #if defined (G_OS_WIN32)
   sec = statbuf->st_mtim.tv_sec;
   usec = statbuf->st_mtim.tv_nsec / 1000;
+  nsec = statbuf->st_mtim.tv_nsec;
 #else
   sec = _g_stat_mtime (statbuf);
 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
   usec = statbuf->st_mtimensec / 1000;
+  nsec = statbuf->st_mtimensec;
 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
   usec = _g_stat_mtim_nsec (statbuf) / 1000;
+  nsec = _g_stat_mtim_nsec (statbuf);
 #else
   usec = 0;
+  nsec = 0;
 #endif
 #endif
 
-  return g_strdup_printf ("%lu:%lu", sec, usec);
+  return g_strdup_printf ("%lu:%lu:%lu", sec, usec, nsec);
 }
 
 static char *
@@ -1018,14 +1022,18 @@ set_info_from_stat (GFileInfo             *info,
 #if defined (G_OS_WIN32)
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, statbuf->st_mtim.tv_sec);
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, 
statbuf->st_mtim.tv_nsec / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_NSEC, 
statbuf->st_mtim.tv_nsec);
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, statbuf->st_atim.tv_sec);
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, 
statbuf->st_atim.tv_nsec / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_NSEC, 
statbuf->st_atim.tv_nsec);
 #else
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED, _g_stat_mtime (statbuf));
 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, 
statbuf->st_mtimensec / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_NSEC, 
statbuf->st_mtimensec);
 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC, _g_stat_mtim_nsec 
(statbuf) / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_NSEC, _g_stat_mtim_nsec 
(statbuf));
 #endif
 
   if (_g_stat_has_field (statbuf, G_LOCAL_FILE_STAT_FIELD_ATIME))
@@ -1033,8 +1041,10 @@ set_info_from_stat (GFileInfo             *info,
       _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS, _g_stat_atime 
(statbuf));
 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
       _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, 
statbuf->st_atimensec / 1000);
+      _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_NSEC, 
statbuf->st_atimensec);
 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
       _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_USEC, _g_stat_atim_nsec 
(statbuf) / 1000);
+      _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_ACCESS_NSEC, _g_stat_atim_nsec 
(statbuf));
 #endif
     }
 #endif
@@ -1048,8 +1058,10 @@ set_info_from_stat (GFileInfo             *info,
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED, _g_stat_ctime (statbuf));
 #if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, 
statbuf->st_ctimensec / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_NSEC, 
statbuf->st_ctimensec);
 #elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_USEC, _g_stat_ctim_nsec 
(statbuf) / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CHANGED_NSEC, _g_stat_ctim_nsec 
(statbuf));
 #endif
 #endif
 
@@ -1058,13 +1070,16 @@ set_info_from_stat (GFileInfo             *info,
     {
       _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, 
statbuf->stx_btime.tv_sec);
       _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, 
statbuf->stx_btime.tv_nsec / 1000);
+      _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, 
statbuf->stx_btime.tv_nsec);
     }
 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC)
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, 
statbuf->st_birthtimensec / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, 
statbuf->st_birthtimensec);
 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM) && defined (HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, 
statbuf->st_birthtim.tv_sec);
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, 
statbuf->st_birthtim.tv_nsec / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, 
statbuf->st_birthtim.tv_nsec);
 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIME)
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_birthtime);
 #elif defined (HAVE_STRUCT_STAT_ST_BIRTHTIM)
@@ -1072,6 +1087,7 @@ set_info_from_stat (GFileInfo             *info,
 #elif defined (G_OS_WIN32)
   _g_file_info_set_attribute_uint64_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED, statbuf->st_ctim.tv_sec);
   _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_USEC, 
statbuf->st_ctim.tv_nsec / 1000);
+  _g_file_info_set_attribute_uint32_by_id (info, G_FILE_ATTRIBUTE_ID_TIME_CREATED_NSEC, 
statbuf->st_ctim.tv_nsec);
 #endif
 
   if (_g_file_attribute_matcher_matches_id (attribute_matcher,
@@ -2185,7 +2201,7 @@ get_uint32 (const GFileAttributeValue  *value,
   return TRUE;
 }
 
-#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
+#if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
 static gboolean
 get_uint64 (const GFileAttributeValue  *value,
            guint64                    *val_out,
@@ -2505,8 +2521,10 @@ static gboolean
 set_mtime_atime (const char                 *filename,
                 const GFileAttributeValue  *mtime_value,
                 const GFileAttributeValue  *mtime_usec_value,
+                const GFileAttributeValue  *mtime_nsec_value,
                 const GFileAttributeValue  *atime_value,
                 const GFileAttributeValue  *atime_usec_value,
+                const GFileAttributeValue  *atime_nsec_value,
                 GError                    **error)
 {
   BOOL res;
@@ -2528,6 +2546,7 @@ set_mtime_atime (const char                 *filename,
       if (!get_uint64 (atime_value, &val, error))
         return FALSE;
       val_usec = 0;
+      val_nsec = 0;
       if (atime_usec_value &&
           !get_uint32 (atime_usec_value, &val_usec, error))
         return FALSE;
@@ -2537,8 +2556,19 @@ set_mtime_atime (const char                 *filename,
        * _g_win32_unix_time_to_filetime() anyway. */
       val_nsec = (val_usec > G_MAXINT32 / 1000) ? G_MAXINT32 : (val_usec * 1000);
 
-      if (!_g_win32_unix_time_to_filetime (val, val_nsec, &atime, error))
-        return FALSE;
+      if (atime_nsec_value &&
+          !get_uint32 (atime_nsec_value, &val_nsec, error))
+             return FALSE;
+      if (val_nsec > 0)
+        {
+          if (!_g_win32_unix_time_to_filetime (val, val_nsec, &atime, error))
+            return FALSE;
+        }
+      else
+        {
+          if (!_g_win32_unix_time_to_filetime (val, val_usec, &atime, error))
+            return FALSE;
+        }
       p_atime = &atime;
     }
 
@@ -2548,6 +2578,7 @@ set_mtime_atime (const char                 *filename,
       if (!get_uint64 (mtime_value, &val, error))
        return FALSE;
       val_usec = 0;
+      val_nsec = 0;
       if (mtime_usec_value &&
           !get_uint32 (mtime_usec_value, &val_usec, error))
         return FALSE;
@@ -2557,8 +2588,19 @@ set_mtime_atime (const char                 *filename,
        * _g_win32_unix_time_to_filetime() anyway. */
       val_nsec = (val_usec > G_MAXINT32 / 1000) ? G_MAXINT32 : (val_usec * 1000);
 
-      if (!_g_win32_unix_time_to_filetime (val, val_nsec, &mtime, error))
-        return FALSE;
+      if (mtime_nsec_value &&
+          !get_uint32 (mtime_nsec_value, &val_nsec, error))
+             return FALSE;
+      if (val_nsec > 0)
+        {
+          if (!_g_win32_unix_time_to_filetime (val, val_nsec, &mtime, error))
+            return FALSE;
+        }
+      else
+        {
+          if (!_g_win32_unix_time_to_filetime (val, val_usec, &mtime, error))
+            return FALSE;
+        }
       p_mtime = &mtime;
     }
 
@@ -2604,7 +2646,7 @@ set_mtime_atime (const char                 *filename,
 
   return res;
 }
-#elif defined (HAVE_UTIMES)
+#elif defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT)
 static int
 lazy_stat (char        *filename, 
            struct stat *statbuf, 
@@ -2628,23 +2670,31 @@ static gboolean
 set_mtime_atime (char                       *filename,
                 const GFileAttributeValue  *mtime_value,
                 const GFileAttributeValue  *mtime_usec_value,
+                const GFileAttributeValue  *mtime_nsec_value,
                 const GFileAttributeValue  *atime_value,
                 const GFileAttributeValue  *atime_usec_value,
+                const GFileAttributeValue  *atime_nsec_value,
                 GError                    **error)
 {
   int res;
   guint64 val = 0;
   guint32 val_usec = 0;
+  guint32 val_nsec = 0;
   struct stat statbuf;
   gboolean got_stat = FALSE;
   struct timeval times[2] = { {0, 0}, {0, 0} };
-
+#ifdef HAVE_UTIMENSAT
+  struct timespec times_n[2] = { {0, 0}, {0, 0} };
+#endif
   /* ATIME */
   if (atime_value)
     {
       if (!get_uint64 (atime_value, &val, error))
        return FALSE;
       times[0].tv_sec = val;
+#if defined (HAVE_UTIMENSAT)
+      times_n[0].tv_sec = val;
+#endif
     }
   else
     {
@@ -2653,8 +2703,14 @@ set_mtime_atime (char                       *filename,
          times[0].tv_sec = statbuf.st_atime;
 #if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
          times[0].tv_usec = statbuf.st_atimensec / 1000;
+#if defined (HAVE_UTIMENSAT)
+          times_n[0].tv_nsec = statbuf.st_atimensec;
+#endif  /* HAVE_UTIMENSAT */
 #elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
          times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
+#if defined (HAVE_UTIMENSAT)
+          times_n[0].tv_nsec = statbuf.st_atim.tv_nsec;
+#endif  /* HAVE_UTIMENSAT */
 #endif
        }
     }
@@ -2666,12 +2722,24 @@ set_mtime_atime (char                       *filename,
       times[0].tv_usec = val_usec;
     }
 
+  if (atime_nsec_value)
+    {
+      if (!get_uint32 (atime_nsec_value, &val_nsec, error))
+        return FALSE;
+#if defined (HAVE_UTIMENSAT)
+      times_n[0].tv_nsec = val_nsec;
+#endif
+    }
+
   /* MTIME */
   if (mtime_value)
     {
       if (!get_uint64 (mtime_value, &val, error))
        return FALSE;
       times[1].tv_sec = val;
+#if defined (HAVE_UTIMENSAT)
+      times_n[1].tv_sec = val;
+#endif
     }
   else
     {
@@ -2680,8 +2748,14 @@ set_mtime_atime (char                       *filename,
          times[1].tv_sec = statbuf.st_mtime;
 #if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
          times[1].tv_usec = statbuf.st_mtimensec / 1000;
+#if defined (HAVE_UTIMENSAT)
+          times_n[1].tv_nsec = statbuf.st_mtimensec;
+#endif  /* HAVE_UTIMENSAT */
 #elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
          times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
+#if defined (HAVE_UTIMENSAT)
+          times_n[1].tv_nsec = statbuf.st_mtim.tv_nsec;
+#endif  /* HAVE_UTIMENSAT */
 #endif
        }
     }
@@ -2692,6 +2766,14 @@ set_mtime_atime (char                       *filename,
        return FALSE;
       times[1].tv_usec = val_usec;
     }
+  if (mtime_nsec_value)
+    {
+      if (!get_uint32 (mtime_nsec_value, &val_nsec, error))
+        return FALSE;
+#if defined (HAVE_UTIMENSAT)
+      times_n[1].tv_nsec = val_nsec;
+#endif
+    }
   
   res = utimes (filename, times);
   if (res == -1)
@@ -2702,7 +2784,19 @@ set_mtime_atime (char                       *filename,
                   g_io_error_from_errno (errsv),
                   _("Error setting modification or access time: %s"),
                   g_strerror (errsv));
-         return FALSE;
+      return FALSE;
+    }
+
+  res = utimensat (AT_FDCWD, filename, times_n, 0);
+  if (res == -1)
+    {
+      int errsv = errno;
+
+      g_set_error (error, G_IO_ERROR,
+                   g_io_error_from_errno (errsv),
+                   _("Error setting modification or access time: %s"),
+                   g_strerror (errsv));
+      return FALSE;
     }
   return TRUE;
 }
@@ -2780,15 +2874,19 @@ _g_local_file_info_set_attribute (char                 *filename,
     return set_symlink (filename, &value, error);
 #endif
 
-#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
+#if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
-    return set_mtime_atime (filename, &value, NULL, NULL, NULL, error);
+    return set_mtime_atime (filename, &value, NULL, NULL, NULL, NULL, NULL, error);
   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
-    return set_mtime_atime (filename, NULL, &value, NULL, NULL, error);
+    return set_mtime_atime (filename, NULL, &value, NULL, NULL, NULL, NULL, error);
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC) == 0)
+    return set_mtime_atime (filename, NULL, NULL, &value, NULL, NULL, NULL, error);
   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
-    return set_mtime_atime (filename, NULL, NULL, &value, NULL, error);
+    return set_mtime_atime (filename, NULL, NULL, NULL, &value, NULL, NULL, error);
   else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
-    return set_mtime_atime (filename, NULL, NULL, NULL, &value, error);
+    return set_mtime_atime (filename, NULL, NULL, NULL, NULL, &value, NULL, error);
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC) == 0)
+    return set_mtime_atime (filename, NULL, NULL, NULL, NULL, NULL, &value, error);
 #endif
 
 #ifdef HAVE_XATTR
@@ -2848,8 +2946,8 @@ _g_local_file_info_set_attributes  (char                 *filename,
 #ifdef G_OS_UNIX
   GFileAttributeValue *uid, *gid;
 #endif
-#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
-  GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
+#if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
+  GFileAttributeValue *mtime, *mtime_usec, *mtime_nsec, *atime, *atime_usec, *atime_nsec;
 #endif
 #if defined (G_OS_UNIX) || defined (G_OS_WIN32)
   GFileAttributeStatus status;
@@ -2922,19 +3020,21 @@ _g_local_file_info_set_attributes  (char                 *filename,
        
     }
 
-#if defined (HAVE_UTIMES) || defined (G_OS_WIN32)
+#if defined (HAVE_UTIMES) || defined (HAVE_UTIMENSAT) || defined (G_OS_WIN32)
   /* Group all time settings into one call
    * Change times as the last thing to avoid it changing due to metadata changes
    */
   
   mtime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
   mtime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
+  mtime_nsec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC);
   atime = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
   atime_usec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
+  atime_nsec = _g_file_info_get_attribute_value (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC);
 
-  if (mtime || mtime_usec || atime || atime_usec)
+  if (mtime || mtime_usec || mtime_nsec || atime || atime_usec || atime_nsec)
     {
-      if (!set_mtime_atime (filename, mtime, mtime_usec, atime, atime_usec, error))
+      if (!set_mtime_atime (filename, mtime, mtime_usec, mtime_nsec, atime, atime_usec, atime_nsec, error))
        {
          status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
          res = FALSE;
@@ -2948,10 +3048,14 @@ _g_local_file_info_set_attributes  (char                 *filename,
        mtime->status = status;
       if (mtime_usec)
        mtime_usec->status = status;
+      if (mtime_nsec)
+       mtime_nsec->status = status;
       if (atime)
        atime->status = status;
       if (atime_usec)
        atime_usec->status = status;
+      if (atime_nsec)
+       atime_nsec->status = status;
     }
 #endif
 
diff --git a/gio/gzlibdecompressor.c b/gio/gzlibdecompressor.c
index dab2de8ed3..f7045093e6 100644
--- a/gio/gzlibdecompressor.c
+++ b/gio/gzlibdecompressor.c
@@ -392,6 +392,9 @@ g_zlib_decompressor_convert (GConverter *converter,
       g_file_info_set_attribute_uint32 (data->file_info,
                                         G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
                                         0);
+      g_file_info_set_attribute_uint32 (data->file_info,
+                                        G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC,
+                                        0);
 
       if (data->filename[0] != '\0')
         g_file_info_set_attribute_byte_string (data->file_info,
diff --git a/gio/tests/file.c b/gio/tests/file.c
index a849e83cf4..04248208ca 100644
--- a/gio/tests/file.c
+++ b/gio/tests/file.c
@@ -2998,6 +2998,20 @@ test_build_attribute_list_for_copy (void)
           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ","));
         }
+#endif
+#ifdef HAVE_UTIMENSAT
+      g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED ","));
+      g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC ","));
+      if (flags & G_FILE_COPY_ALL_METADATA)
+        {
+          g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
+          g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC ","));
+        }
+      else
+        {
+          g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
+          g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC ","));
+        }
 #endif
       g_free (attrs_with_commas);
     }
diff --git a/meson.build b/meson.build
index 57a71f7694..cecea72710 100644
--- a/meson.build
+++ b/meson.build
@@ -586,6 +586,7 @@ functions = [
   'unsetenv',
   'uselocale',
   'utimes',
+  'utimensat',
   'valloc',
   'vasprintf',
   'vsnprintf',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]