From 7ad6b054583ef5bb088d795ddf9104e6dd023baa Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 27 May 2022 16:55:18 +0100 Subject: [PATCH 1/4] gfileinfo: Split out a g_file_info_remove_value() helper function This introduces no functional changes but will make refactoring a bit easier in the following commit. Signed-off-by: Philip Withnall --- gio/gfileinfo.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/gio/gfileinfo.c b/gio/gfileinfo.c index 36f8e231b..ed700e3b1 100644 --- a/gio/gfileinfo.c +++ b/gio/gfileinfo.c @@ -688,6 +688,28 @@ g_file_info_get_attribute_type (GFileInfo *info, return G_FILE_ATTRIBUTE_TYPE_INVALID; } +static void +g_file_info_remove_value (GFileInfo *info, + guint32 attr_id) +{ + GFileAttribute *attrs; + guint i; + + if (info->mask != NO_ATTRIBUTE_MASK && + !_g_file_attribute_matcher_matches_id (info->mask, attr_id)) + return; + + i = g_file_info_find_place (info, attr_id); + + attrs = (GFileAttribute *)info->attributes->data; + if (i < info->attributes->len && + attrs[i].attribute == attr_id) + { + _g_file_attribute_value_clear (&attrs[i].value); + g_array_remove_index (info->attributes, i); + } +} + /** * g_file_info_remove_attribute: * @info: a #GFileInfo. @@ -700,22 +722,13 @@ g_file_info_remove_attribute (GFileInfo *info, const char *attribute) { guint32 attr_id; - GFileAttribute *attrs; - guint i; g_return_if_fail (G_IS_FILE_INFO (info)); g_return_if_fail (attribute != NULL && *attribute != '\0'); attr_id = lookup_attribute (attribute); - i = g_file_info_find_place (info, attr_id); - attrs = (GFileAttribute *)info->attributes->data; - if (i < info->attributes->len && - attrs[i].attribute == attr_id) - { - _g_file_attribute_value_clear (&attrs[i].value); - g_array_remove_index (info->attributes, i); - } + g_file_info_remove_value (info, attr_id); } /** From b33ef610deefc4ba54bd1404b0fd292459e255f3 Mon Sep 17 00:00:00 2001 From: nitinosiris Date: Fri, 2 Jul 2021 18:40:44 +0530 Subject: [PATCH 2/4] 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 383cfd1a1..603338ffb 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 9d3a061c7..8fc085973 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 ed700e3b1..f709bf4f1 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 058bccbaf..28bddfebc 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 43a6eb79d..ed7a663d2 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 59869735f..edec7bdde 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 dab2de8ed..f7045093e 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 a849e83cf..04248208c 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 57a71f769..cecea7271 100644 --- a/meson.build +++ b/meson.build @@ -586,6 +586,7 @@ functions = [ 'unsetenv', 'uselocale', 'utimes', + 'utimensat', 'valloc', 'vasprintf', 'vsnprintf', From b7b10cc7e051baa211351896dcfdfa94c135ccc6 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 27 May 2022 16:56:04 +0100 Subject: [PATCH 3/4] tests: Add additional tests for nanosecond precision file timestamps Signed-off-by: Philip Withnall Helps: #369 --- gio/tests/g-file-info.c | 181 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 3 deletions(-) diff --git a/gio/tests/g-file-info.c b/gio/tests/g-file-info.c index 59411c3a8..8a93353b3 100644 --- a/gio/tests/g-file-info.c +++ b/gio/tests/g-file-info.c @@ -149,6 +149,9 @@ test_g_file_info_modification_time (void) GFileInfo *info = NULL; GDateTime *dt = NULL, *dt_usecs = NULL, *dt_new = NULL, *dt_new_usecs = NULL; GTimeSpan ts; + gboolean nsecs_supported; + gint usecs; + guint32 nsecs; GError *error = NULL; g_test_summary ("Test that getting the modification time of a file works."); @@ -181,6 +184,24 @@ test_g_file_info_modification_time (void) g_assert_cmpint (ts, >=, 0); g_assert_cmpint (ts, <, G_USEC_PER_SEC); + /* Try again with nanosecond precision. */ + g_clear_object (&info); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + g_assert_no_error (error); + + nsecs_supported = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC); + if (nsecs_supported) + { + usecs = g_date_time_get_microsecond (dt_usecs); + nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC); + + g_assert_cmpuint (nsecs, >=, usecs * 1000); + g_assert_cmpuint (nsecs, <, (usecs + 1) * 1000); + } + /* Try round-tripping the modification time. */ dt_new = g_date_time_add (dt_usecs, G_USEC_PER_SEC + 50); g_file_info_set_modification_date_time (info, dt_new); @@ -189,6 +210,39 @@ test_g_file_info_modification_time (void) ts = g_date_time_difference (dt_new_usecs, dt_new); g_assert_cmpint (ts, ==, 0); + /* Setting the modification time with usec-precision should have cleared nsecs. */ + g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC), ==, 0); + + /* Try setting the modification time with nsec-precision and it should set the + * usecs too. */ + if (nsecs_supported) + { + gint new_usecs; + guint32 new_nsecs; + GDateTime *new_dt_usecs = NULL; + + g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, nsecs + 100, + G_FILE_QUERY_INFO_NONE, NULL, &error); + g_assert_no_error (error); + + g_clear_object (&info); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + g_assert_no_error (error); + + new_dt_usecs = g_file_info_get_modification_date_time (info); + g_assert_nonnull (new_dt_usecs); + + new_usecs = g_date_time_get_microsecond (new_dt_usecs); + new_nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC); + + g_assert_cmpuint (new_nsecs, ==, nsecs + 100); + g_assert_cmpuint (new_nsecs, >=, new_usecs * 1000); + g_assert_cmpuint (new_nsecs, <, (new_usecs + 1) * 1000); + } + /* Clean up. */ g_clear_object (&io_stream); g_file_delete (file, NULL, NULL); @@ -210,6 +264,9 @@ test_g_file_info_access_time (void) GDateTime *dt = NULL, *dt_usecs = NULL, *dt_new = NULL, *dt_new_usecs = NULL, *dt_before_epoch = NULL, *dt_before_epoch_returned = NULL; GTimeSpan ts; + gboolean nsecs_supported; + gint usecs; + guint32 nsecs; GError *error = NULL; g_test_summary ("Test that getting the access time of a file works."); @@ -242,6 +299,24 @@ test_g_file_info_access_time (void) g_assert_cmpint (ts, >, 0); g_assert_cmpint (ts, <, G_USEC_PER_SEC); + /* Try again with nanosecond precision. */ + g_clear_object (&info); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_ACCESS "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + g_assert_no_error (error); + + nsecs_supported = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC); + if (nsecs_supported) + { + usecs = g_date_time_get_microsecond (dt_usecs); + nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC); + + g_assert_cmpuint (nsecs, >=, usecs * 1000); + g_assert_cmpuint (nsecs, <, (usecs + 1) * 1000); + } + /* Try round-tripping the access time. */ dt_new = g_date_time_add (dt_usecs, G_USEC_PER_SEC + 50); g_file_info_set_access_date_time (info, dt_new); @@ -257,6 +332,39 @@ test_g_file_info_access_time (void) ts = g_date_time_difference (dt_before_epoch, dt_before_epoch_returned); g_assert_cmpint (ts, ==, 0); + /* Setting the access time with usec-precision should have cleared nsecs. */ + g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC), ==, 0); + + /* Try setting the access time with nsec-precision and it should set the + * usecs too. */ + if (nsecs_supported) + { + gint new_usecs; + guint32 new_nsecs; + GDateTime *new_dt_usecs = NULL; + + g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC, nsecs + 100, + G_FILE_QUERY_INFO_NONE, NULL, &error); + g_assert_no_error (error); + + g_clear_object (&info); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_ACCESS "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC "," G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + g_assert_no_error (error); + + new_dt_usecs = g_file_info_get_access_date_time (info); + g_assert_nonnull (new_dt_usecs); + + new_usecs = g_date_time_get_microsecond (new_dt_usecs); + new_nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_NSEC); + + g_assert_cmpuint (new_nsecs, ==, nsecs + 100); + g_assert_cmpuint (new_nsecs, >=, new_usecs * 1000); + g_assert_cmpuint (new_nsecs, <, (new_usecs + 1) * 1000); + } + /* Clean up. */ g_clear_object (&io_stream); g_file_delete (file, NULL, NULL); @@ -280,6 +388,9 @@ test_g_file_info_creation_time (void) GDateTime *dt = NULL, *dt_usecs = NULL, *dt_new = NULL, *dt_new_usecs = NULL, *dt_before_epoch = NULL, *dt_before_epoch_returned = NULL; GTimeSpan ts; + gboolean nsecs_supported; + gint usecs; + guint32 nsecs; GError *error = NULL; g_test_summary ("Test that getting the creation time of a file works."); @@ -319,6 +430,24 @@ test_g_file_info_creation_time (void) g_assert_cmpint (ts, >, 0); g_assert_cmpint (ts, <, G_USEC_PER_SEC); + /* Try again with nanosecond precision. */ + g_clear_object (&info); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_CREATED "," G_FILE_ATTRIBUTE_TIME_CREATED_USEC "," G_FILE_ATTRIBUTE_TIME_CREATED_NSEC, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + g_assert_no_error (error); + + nsecs_supported = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC); + if (nsecs_supported) + { + usecs = g_date_time_get_microsecond (dt_usecs); + nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC); + + g_assert_cmpuint (nsecs, >=, usecs * 1000); + g_assert_cmpuint (nsecs, <, (usecs + 1) * 1000); + } + /* Try round-tripping the creation time. */ dt_new = g_date_time_add (dt_usecs, G_USEC_PER_SEC + 50); g_file_info_set_creation_date_time (info, dt_new); @@ -334,6 +463,47 @@ test_g_file_info_creation_time (void) ts = g_date_time_difference (dt_before_epoch, dt_before_epoch_returned); g_assert_cmpint (ts, ==, 0); + /* Setting the creation time with usec-precision should have cleared nsecs. */ + g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC), ==, 0); + + /* Try setting the creation time with nsec-precision and it should set the + * usecs too. */ + if (nsecs_supported) + { + gint new_usecs; + guint32 new_nsecs; + GDateTime *new_dt_usecs = NULL; + + /* This can fail on some platforms, even if reading CREATED_NSEC works */ + g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC, nsecs + 100, + G_FILE_QUERY_INFO_NONE, NULL, &error); + if (error == NULL) + { + g_clear_object (&info); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_CREATED "," G_FILE_ATTRIBUTE_TIME_CREATED_USEC "," G_FILE_ATTRIBUTE_TIME_CREATED_NSEC, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + g_assert_no_error (error); + + new_dt_usecs = g_file_info_get_creation_date_time (info); + g_assert_nonnull (new_dt_usecs); + + new_usecs = g_date_time_get_microsecond (new_dt_usecs); + new_nsecs = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_NSEC); + + g_assert_cmpuint (new_nsecs, ==, nsecs + 100); + g_assert_cmpuint (new_nsecs, >=, new_usecs * 1000); + g_assert_cmpuint (new_nsecs, <, (new_usecs + 1) * 1000); + } + else + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) + g_clear_error (&error); + g_assert_no_error (error); + } + } + /* Clean up. */ g_clear_object (&io_stream); g_file_delete (file, NULL, NULL); @@ -711,7 +881,8 @@ test_internal_enhanced_stdio (void) G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE "," G_FILE_ATTRIBUTE_ID_FILE "," G_FILE_ATTRIBUTE_TIME_MODIFIED "," - G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, + G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," + G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, G_FILE_QUERY_INFO_NONE, NULL, NULL); @@ -720,7 +891,8 @@ test_internal_enhanced_stdio (void) G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE "," G_FILE_ATTRIBUTE_ID_FILE "," G_FILE_ATTRIBUTE_TIME_MODIFIED "," - G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, + G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," + G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, G_FILE_QUERY_INFO_NONE, NULL, NULL); @@ -729,12 +901,14 @@ test_internal_enhanced_stdio (void) g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_ID_FILE)); g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED)); g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC)); + g_assert_true (g_file_info_has_attribute (fi_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC)); g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_SIZE)); g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE)); g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_ID_FILE)); g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED)); g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC)); + g_assert_true (g_file_info_has_attribute (fi_p1, G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC)); size_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_SIZE); alsize_p0 = g_file_info_get_attribute_uint64 (fi_p0, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE); @@ -782,7 +956,8 @@ test_internal_enhanced_stdio (void) g_object_unref (fi_p0); fi_p0 = g_file_query_info (gf_p0, G_FILE_ATTRIBUTE_TIME_MODIFIED "," - G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, + G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," + G_FILE_ATTRIBUTE_TIME_MODIFIED_NSEC, G_FILE_QUERY_INFO_NONE, NULL, NULL); dt2 = g_file_info_get_modification_date_time (fi_p0); From 18c0088678a2b0f794c76018057f466f05fad5aa Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 27 May 2022 17:09:31 +0100 Subject: [PATCH 4/4] tests: Fix a memory leak when abandoning a file creation time test Signed-off-by: Philip Withnall --- gio/tests/g-file-info.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gio/tests/g-file-info.c b/gio/tests/g-file-info.c index 8a93353b3..d97b78b71 100644 --- a/gio/tests/g-file-info.c +++ b/gio/tests/g-file-info.c @@ -409,6 +409,7 @@ test_g_file_info_creation_time (void) if (!dt) { g_test_skip ("Skipping testing creation time as it’s not supported by the kernel"); + g_clear_object (&io_stream); g_file_delete (file, NULL, NULL); g_clear_object (&file); g_clear_object (&info);