From eecf3977a5d9d2788c0b7d47e2d268708551d4d7 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Wed, 26 Jan 2022 13:25:11 -0500 Subject: [PATCH] libroot_build: Implement FD writing STAT_ACCESS_TIME and STAT_MODIFICATION_TIME. We have used and had fallback futimens() implementations in libroot_build (and in this file even) for over a decade, but nobody seems to have noticed this bit of missing functionality which can be implemented with them. In fact, this is rather important bit of functionality, as "copyattr" relies on it to preserve the mtimes of copied files; and we use recursive copyattr many places in Haiku builds. Thus, the lack of an implementation here was the cause of all files in built Haiku images having timestamps of whenever the build was done, and not whenever the file was actually modified. This should make development on Haiku nightlies much more pleasant, as the system headers from the haiku_devel package should no longer have always-current timestamps with every upgrade. --- src/build/libroot/fs.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/build/libroot/fs.cpp b/src/build/libroot/fs.cpp index 149c8c054f..8b0c88c4a3 100644 --- a/src/build/libroot/fs.cpp +++ b/src/build/libroot/fs.cpp @@ -891,7 +891,6 @@ _kern_write_stat(int fd, const char *path, bool traverseLink, return errno; isSymlink = S_ISLNK(tmpStat.st_mode); - } else { Descriptor *descriptor = get_descriptor(fd); if (!descriptor) @@ -900,17 +899,14 @@ _kern_write_stat(int fd, const char *path, bool traverseLink, if (FileDescriptor *fileFD = dynamic_cast(descriptor)) { realFD = fileFD->fd; - } else if (dynamic_cast(descriptor)) { error = get_path(fd, NULL, realPath); if (error != B_OK) return error; - } else if (SymlinkDescriptor *linkFD = dynamic_cast(descriptor)) { realPath = linkFD->path; isSymlink = true; - } else return B_FILE_ERROR; } @@ -941,15 +937,27 @@ _kern_write_stat(int fd, const char *path, bool traverseLink, return errno; } - // The timestamps can only be set via utime(), but that requires a - // path we don't have. - if (statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME - | B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME)) { - return B_ERROR; + if (statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) { + // Grab the previous mod and access times so we only overwrite + // the specified time and not both + struct stat oldStat; + if (~statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) { + if (fstat(realFD, &oldStat) < 0) + return errno; + } + + struct timespec times[2]; + times[0] = (statMask & B_STAT_ACCESS_TIME) ? st->st_atim : oldStat.st_atim; + times[1] = (statMask & B_STAT_MODIFICATION_TIME) ? st->st_mtim : oldStat.st_mtim; + if (futimens(realFD, times) < 0) + return errno; } - return 0; + // not supported + if (statMask & (B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME)) + return B_ERROR; + return 0; } else { if (statMask & B_STAT_MODE) { if (chmod(realPath.c_str(), st->st_mode) < 0)