From 528894e72f973c5db5dc76c975620754f7bbe5aa Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Fri, 21 Oct 2016 22:19:46 -0700 Subject: [PATCH] On UN*X, st_ctime is the last status change time, not the creation time. That's the time the file's inode last changed, so size changes, permission changes, etc. affect it. It's *not* the time the file was created; most UN*Xes don't provide that. Newer versions of FreeBSD, NetBSD, OpenBSD, and macOS do, but other UN*Xes don't appear to. On Windows, at least according to Microsoft's documentation, st_ctime *is* the creation time. Hopefully that's not the result of confusion on the part of somebody at Microsoft. Change-Id: I20743703f6ef66e40dff9004dc91bed46af6fad0 Reviewed-on: https://code.wireshark.org/review/18378 Reviewed-by: Guy Harris --- ConfigureChecks.cmake | 8 +++++--- cmakeconfig.h.in | 6 ++++++ configure.ac | 6 ++++++ fileset.c | 24 ++++++++++++++++++++++++ ui/gtk/fileset_dlg.c | 28 +++++++++++++++++++--------- ui/qt/file_set_dialog.cpp | 14 ++++++++++++-- 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 73c24df239..01273f5cbd 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -134,9 +134,11 @@ endif() #Struct members include(CheckStructHasMember) -check_struct_has_member("struct sockaddr" sa_len sys/socket.h HAVE_SOCKADDR_SA_LEN) -check_struct_has_member("struct stat" st_flags sys/stat.h HAVE_STAT_ST_FLAGS) -check_struct_has_member("struct tm" tm_zone time.h HAVE_STRUCT_TM_TM_ZONE) +check_struct_has_member("struct sockaddr" sa_len sys/socket.h HAVE_SOCKADDR_SA_LEN) +check_struct_has_member("struct stat" st_flags sys/stat.h HAVE_STAT_ST_FLAGS) +check_struct_has_member("struct stat" st_birthtime sys/stat.h HAVE_STAT_ST_BIRTHTIME) +check_struct_has_member("struct stat" __st_birthtime sys/stat.h HAVE_STAT___ST_BIRTHTIME) +check_struct_has_member("struct tm" tm_zone time.h HAVE_STRUCT_TM_TM_ZONE) #Symbols but NOT enums or types check_symbol_exists(tzname "time.h" HAVE_TZNAME) diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index 71ea10cbd0..c206545335 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -318,9 +318,15 @@ /* Define if you have the 'strptime' function. */ #cmakedefine HAVE_STRPTIME 1 +/* Define to 1 if `st_birthtime' is a member of `struct stat'. */ +#cmakedefine HAVE_STRUCT_STAT_ST_BIRTHTIME 1 + /* Define if st_flags field exists in struct stat */ #cmakedefine HAVE_STAT_ST_FLAGS 1 +/* Define to 1 if `__st_birthtime' is a member of `struct stat'. */ +#cmakedefine HAVE_STRUCT_STAT___ST_BIRTHTIME 1 + /* Define to 1 if you have the `sysconf' function. */ #cmakedefine HAVE_SYSCONF 1 diff --git a/configure.ac b/configure.ac index 9805e38f44..d03b3cccfa 100644 --- a/configure.ac +++ b/configure.ac @@ -2426,6 +2426,12 @@ AC_STRUCT_TIMEZONE AC_CHECK_MEMBERS([struct stat.st_flags]) +# We need to know whether "struct stat" has an "st_birthtime" member +# or an "__st_birthtime" member for the file set dialog. + +AC_CHECK_MEMBERS([struct stat.st_birthtime]) +AC_CHECK_MEMBERS([struct stat.__st_birthtime]) + # We need to know whether "struct sockaddr" has an "sa_len" member # for get_interface_list(). diff --git a/fileset.c b/fileset.c index b1e636c3d5..06cf335f9c 100644 --- a/fileset.c +++ b/fileset.c @@ -186,7 +186,19 @@ fileset_update_file(const char *path) if (entry_list) { entry = (fileset_entry *) entry_list->data; +#ifdef __WIN32 + /* Microsoft's documentation says this is the creation time */ entry->ctime = buf.st_ctime; +#else /* _WIN32 */ + /* UN*X - do we have a creation time? */ +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) + entry->ctime = buf.st_birthtime; +#elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME) + entry->ctime = buf.__st_birthtime; +#else /* nothing */ + entry->ctime = 0; +#endif /* creation time on UN*X */ +#endif /* _WIN32 */ entry->mtime = buf.st_mtime; entry->size = buf.st_size; } @@ -220,7 +232,19 @@ fileset_add_file(const char *dirname, const char *fname, gboolean current) entry->fullname = g_strdup(path); entry->name = g_strdup(fname); +#ifdef __WIN32 + /* Microsoft's documentation says this is the creation time */ entry->ctime = buf.st_ctime; +#else /* _WIN32 */ + /* UN*X - do we have a creation time? */ +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) + entry->ctime = buf.st_birthtime; +#elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME) + entry->ctime = buf.__st_birthtime; +#else /* nothing */ + entry->ctime = 0; +#endif /* creation time on UN*X */ +#endif /* _WIN32 */ entry->mtime = buf.st_mtime; entry->size = buf.st_size; entry->current = current; diff --git a/ui/gtk/fileset_dlg.c b/ui/gtk/fileset_dlg.c index cc28f2b152..540da82fdd 100644 --- a/ui/gtk/fileset_dlg.c +++ b/ui/gtk/fileset_dlg.c @@ -158,15 +158,25 @@ fileset_dlg_add_file(fileset_entry *entry, void *window _U_) { created = fileset_dlg_name2date_dup(entry->name); if (!created) { /* if this file doesn't follow the file set pattern, */ - /* use the creation time of that file */ - local = localtime(&entry->ctime); - if (local != NULL) { - created = g_strdup_printf("%04u-%02u-%02u %02u:%02u:%02u", - local->tm_year+1900, local->tm_mon+1, local->tm_mday, - local->tm_hour, local->tm_min, local->tm_sec); - } else { - created = g_strdup("Time not representable"); - } + /* use the creation time of that file if available */ + /* + * macOS provides 0 if the file system doesn't support the + * creation time; FreeBSD provides -1. + * + * If this OS doesn't provide the creation time with stat(), + * it will be 0. + */ + if (entry->ctime > 0) { + local = localtime(&entry->ctime); + if (local != NULL) { + created = g_strdup_printf("%04u-%02u-%02u %02u:%02u:%02u", + local->tm_year+1900, local->tm_mon+1, local->tm_mday, + local->tm_hour, local->tm_min, local->tm_sec); + } else { + created = g_strdup("Time not representable"); + } + } else + created = g_strdup("Not available"); } local = localtime(&entry->mtime); diff --git a/ui/qt/file_set_dialog.cpp b/ui/qt/file_set_dialog.cpp index 525d446e7d..f62f8ce8c9 100644 --- a/ui/qt/file_set_dialog.cpp +++ b/ui/qt/file_set_dialog.cpp @@ -103,9 +103,19 @@ void FileSetDialog::addFile(fileset_entry *entry) { created = nameToDate(entry->name); if(created.length() < 1) { /* if this file doesn't follow the file set pattern, */ - /* use the creation time of that file */ + /* use the creation time of that file if available */ /* http://en.wikipedia.org/wiki/ISO_8601 */ - created = QDateTime::fromTime_t(entry->ctime).toLocalTime().toString("yyyy-MM-dd HH:mm:ss"); + /* + * macOS provides 0 if the file system doesn't support the + * creation time; FreeBSD provides -1. + * + * If this OS doesn't provide the creation time with stat(), + * it will be 0. + */ + if (entry->ctime > 0) + created = QDateTime::fromTime_t(entry->ctime).toLocalTime().toString("yyyy-MM-dd HH:mm:ss"); + else + created = "Not available"; } modified = QDateTime::fromTime_t(entry->mtime).toLocalTime().toString("yyyy-MM-dd HH:mm:ss");