[linux] Fix zero file creation (birth) time

Observed in Ubuntu 24 via Orb on arm-based Mac

Signed-off-by: Alexander Borsuk <me@alex.bio>
This commit is contained in:
Alexander Borsuk
2025-07-26 20:10:04 +02:00
committed by Konstantin Pastbin
parent fbaa59ce3b
commit 674abcf02e
5 changed files with 56 additions and 19 deletions

View File

@@ -1029,9 +1029,9 @@ UNIT_CLASS_TEST(Runner, Bookmarks_SpecialXMLNames)
BookmarkManager bmManager(BM_CALLBACKS);
bmManager.EnableTestMode(true);
auto const file1Name = "file1";
auto const fileNameFromMemory = "";
BookmarkManager::KMLDataCollection kmlDataCollection1;
kmlDataCollection1.emplace_back(file1Name /* filePath */,
kmlDataCollection1.emplace_back(fileNameFromMemory /* filePath */,
LoadKmlData(MemReader(kmlString3, strlen(kmlString3)), KmlFileType::Text));
bmManager.CreateCategories(std::move(kmlDataCollection1));
@@ -1056,29 +1056,29 @@ UNIT_CLASS_TEST(Runner, Bookmarks_SpecialXMLNames)
bmManager.GetEditSession().DeleteBmCategory(catId, true);
auto const file2Name = "file2";
auto const fileNameOnDisk = "file1";
BookmarkManager::KMLDataCollection kmlDataCollection2;
kmlDataCollection2.emplace_back(file1Name /* filePath */, LoadKmlFile(fileNameTmp, GetActiveKmlFileType()));
kmlDataCollection2.emplace_back(fileNameOnDisk /* filePath */, LoadKmlFile(fileNameTmp, GetActiveKmlFileType()));
bmManager.CreateCategories(std::move(kmlDataCollection2));
BookmarkManager::KMLDataCollection kmlDataCollection3;
kmlDataCollection3.emplace_back(file2Name /* filePath */,
kmlDataCollection3.emplace_back(fileNameFromMemory /* filePath */,
LoadKmlData(MemReader(kmlString3, strlen(kmlString3)), KmlFileType::Text));
bmManager.CreateCategories(std::move(kmlDataCollection3));
TEST_EQUAL(bmManager.GetBmGroupsCount(), 2, ());
auto const catId2 = bmManager.GetSortedBmGroupIdList().front();
auto const catId3 = bmManager.GetSortedBmGroupIdList().back();
auto const lastModifiedCatId = bmManager.GetSortedBmGroupIdList().front();
auto const olderCatId = bmManager.GetSortedBmGroupIdList().back();
TEST_EQUAL(bmManager.GetUserMarkIds(catId2).size(), 1, ());
TEST_EQUAL(bmManager.GetCategoryName(catId2), expectedName, ());
TEST_EQUAL(bmManager.GetCategoryFileName(catId2), file1Name, ());
TEST_EQUAL(bmManager.GetCategoryFileName(catId3), file2Name, ());
TEST_EQUAL(bmManager.GetUserMarkIds(lastModifiedCatId).size(), 1, ());
TEST_EQUAL(bmManager.GetCategoryName(olderCatId), expectedName, ());
TEST_EQUAL(bmManager.GetCategoryFileName(lastModifiedCatId), fileNameFromMemory, ());
TEST_EQUAL(bmManager.GetCategoryFileName(olderCatId), fileNameOnDisk, ());
auto const bmId1 = *bmManager.GetUserMarkIds(catId2).begin();
auto const bmId1 = *bmManager.GetUserMarkIds(lastModifiedCatId).begin();
auto const * bm1 = bmManager.GetBookmark(bmId1);
auto const bmId2 = *bmManager.GetUserMarkIds(catId3).begin();
auto const bmId2 = *bmManager.GetUserMarkIds(olderCatId).begin();
auto const * bm2 = bmManager.GetBookmark(bmId2);
TEST(EqualBookmarks(*bm1, *bm2), ());
TEST_EQUAL(kml::GetDefaultStr(bm1->GetName()), "![X1]{X2}(X3)", ());

View File

@@ -232,6 +232,9 @@ time_t Platform::GetFileCreationTime(std::string const & path)
struct stat st;
if (0 == stat(path.c_str(), &st))
return st.st_atim.tv_sec;
LOG(LERROR, ("GetFileCreationTime stat failed for", path, "with error", strerror(errno)));
// TODO(AB): Refactor to return std::optional<time_t>.
return 0;
}
@@ -241,5 +244,8 @@ time_t Platform::GetFileModificationTime(std::string const & path)
struct stat st;
if (0 == stat(path.c_str(), &st))
return st.st_mtim.tv_sec;
LOG(LERROR, ("GetFileModificationTime stat failed for", path, "with error", strerror(errno)));
// TODO(AB): Refactor to return std::optional<time_t>.
return 0;
}

View File

@@ -260,16 +260,30 @@ void Platform::GetSystemFontNames(FilesList & res) const
// static
time_t Platform::GetFileCreationTime(std::string const & path)
{
// In older Linux versions there is no reliable way to get file creation time.
#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 28
// In older Linux versions there is no reliable way to get file creation time with GLIBC.
// Musl supports statx since 2018.
#if !defined(__GLIBC__) || (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 28)
struct statx st;
if (0 == statx(AT_FDCWD, path.c_str(), 0, STATX_BTIME, &st))
return st.stx_btime.tv_sec;
{
// Orbstack on MacOS returns zero birth time, see https://github.com/orbstack/orbstack/issues/2064
if (st.stx_btime.tv_sec != 0)
return st.stx_btime.tv_sec;
LOG(LWARNING, ("statx returned zero birth time for", path,
", using the earliest time from access, modification or status change instead."));
return std::min(st.stx_atime.tv_sec, std::min(st.stx_mtime.tv_sec, st.stx_ctime.tv_sec));
}
LOG(LERROR, ("GetFileCreationTime statx failed for", path, "with error", strerror(errno)));
#else
struct stat st;
if (0 == stat(path.c_str(), &st))
return std::min(st.st_atim.tv_sec, st.st_mtim.tv_sec);
LOG(LERROR, ("GetFileCreationTime stat failed for", path, "with error", strerror(errno)));
#endif
// TODO(AB): Refactor to return std::optional<time_t>.
return 0;
}
@@ -279,5 +293,8 @@ time_t Platform::GetFileModificationTime(std::string const & path)
struct stat st;
if (0 == stat(path.c_str(), &st))
return st.st_mtim.tv_sec;
LOG(LERROR, ("GetFileModificationTime stat failed for", path, "with error", strerror(errno)));
// TODO(AB): Refactor to return std::optional<time_t>.
return 0;
}

View File

@@ -190,6 +190,9 @@ time_t Platform::GetFileCreationTime(std::string const & path)
struct stat st;
if (0 == stat(path.c_str(), &st))
return st.st_birthtimespec.tv_sec;
LOG(LERROR, ("GetFileCreationTime stat failed for", path, "with error", strerror(errno)));
// TODO(AB): Refactor to return std::optional<time_t>.
return 0;
}
@@ -199,5 +202,8 @@ time_t Platform::GetFileModificationTime(std::string const & path)
struct stat st;
if (0 == stat(path.c_str(), &st))
return st.st_mtimespec.tv_sec;
LOG(LERROR, ("GetFileModificationTime stat failed for", path, "with error", strerror(errno)));
// TODO(AB): Refactor to return std::optional<time_t>.
return 0;
}

View File

@@ -220,7 +220,10 @@ time_t GetFileTime(std::string const & path, FileTimeType fileTimeType)
{
HANDLE hFile = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
{
LOG(LERROR, ("GetFileTime CreateFileA failed for", path, "with error", strerror(errno)));
return 0; // TODO(AB): Refactor to return std::optional<time_t>.
}
SCOPE_GUARD(autoClose, bind(&CloseHandle, hFile));
@@ -234,8 +237,11 @@ time_t GetFileTime(std::string const & path, FileTimeType fileTimeType)
case FileTimeType::Modification: ftLastWrite = &ft; break;
}
if (!GetFileTime(hFile, ftCreate, nullptr, ftLastWrite))
return 0;
if (!::GetFileTime(hFile, ftCreate, nullptr, ftLastWrite))
{
LOG(LERROR, ("GetFileTime ::GetFileTime failed for", path, "with error", strerror(errno)));
return 0; // TODO(AB): Refactor to return std::optional<time_t>.
}
ULARGE_INTEGER ull;
ull.LowPart = ft.dwLowDateTime;
@@ -247,12 +253,14 @@ time_t GetFileTime(std::string const & path, FileTimeType fileTimeType)
// static
time_t Platform::GetFileCreationTime(std::string const & path)
{
// TODO(AB): Refactor to return std::optional<time_t>.
return GetFileTime(path, FileTimeType::Creation);
}
// static
time_t Platform::GetFileModificationTime(std::string const & path)
{
// TODO(AB): Refactor to return std::optional<time_t>.
return GetFileTime(path, FileTimeType::Modification);
}