From ead41bcb4ef413f32c733846d2c1a3db0ffcd581 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 3 Aug 2021 17:20:12 -0400 Subject: [PATCH 1/4] Add Test to Verify mtime is Copied to Destination --- tests/playTests.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index ce585f226..d108d5d8e 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -124,6 +124,13 @@ case "$UNAME" in Darwin | FreeBSD | OpenBSD | NetBSD) MTIME="stat -f %m" ;; esac +assertSameMTime() { + MT1=$($MTIME "$1") + MT2=$($MTIME "$2") + echo MTIME $MT1 $MT2 + [ "$MT1" = "$MT2" ] || die "mtime on $1 doesn't match mtime on $2 ($MT1 != $MT2)" +} + GET_PERMS="stat -c %a" case "$UNAME" in Darwin | FreeBSD | OpenBSD | NetBSD) GET_PERMS="stat -f %Lp" ;; @@ -588,6 +595,18 @@ if [ -n "$READFROMBLOCKDEVICE" ] ; then rm -f tmp.img tmp.img.zst tmp.img.copy fi +println "\n===> zstd created file timestamp tests" +datagen > tmp +sleep 1 # could also use touch -t but I don't know how portable that is. +println "test : copy mtime in file -> file compression " +zstd -f tmp -o tmp.zst +assertSameMTime tmp tmp.zst +sleep 1 # could also use touch -t but I don't know how portable that is. +println "test : copy mtime in file -> file decompression " +zstd -f -d tmp.zst -o tmp.out +assertSameMTime tmp.zst tmp.out +rm -f tmp + println "\n===> compress multiple files into an output directory, --output-dir-flat" println henlo > tmp1 mkdir tmpInputTestDir From a719edbbc3f8f978ed41f1152b29cb51150cfd07 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 4 Aug 2021 14:49:00 -0400 Subject: [PATCH 2/4] Pull `utime()` Call into Helper --- programs/util.c | 43 ++++++++++++++++++++++++------------------- programs/util.h | 8 ++++++++ 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/programs/util.c b/programs/util.c index 81c10d0ca..b29177818 100644 --- a/programs/util.c +++ b/programs/util.c @@ -159,6 +159,29 @@ int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions) return chmod(filename, permissions); } +/* set access and modification times */ +int UTIL_utime(const char* filename, const stat_t *statbuf) +{ + int ret; + /* We check that st_mtime is a macro here in order to give us confidence + * that struct stat has a struct timespec st_mtim member. We need this + * check because there are some platforms that claim to be POSIX 2008 + * compliant but which do not have st_mtim... */ +#if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime) + /* (atime, mtime) */ + struct timespec timebuf[2] = { {0, UTIME_NOW} }; + timebuf[1] = statbuf->st_mtim; + ret = utimensat(AT_FDCWD, filename, timebuf, 0); +#else + struct utimbuf timebuf; + timebuf.actime = time(NULL); + timebuf.modtime = statbuf->st_mtime; + ret = utime(filename, &timebuf); +#endif + errno = 0; + return ret; +} + int UTIL_setFileStat(const char *filename, const stat_t *statbuf) { int res = 0; @@ -168,25 +191,7 @@ int UTIL_setFileStat(const char *filename, const stat_t *statbuf) return -1; /* set access and modification times */ - /* We check that st_mtime is a macro here in order to give us confidence - * that struct stat has a struct timespec st_mtim member. We need this - * check because there are some platforms that claim to be POSIX 2008 - * compliant but which do not have st_mtim... */ -#if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime) - { - /* (atime, mtime) */ - struct timespec timebuf[2] = { {0, UTIME_NOW} }; - timebuf[1] = statbuf->st_mtim; - res += utimensat(AT_FDCWD, filename, timebuf, 0); - } -#else - { - struct utimbuf timebuf; - timebuf.actime = time(NULL); - timebuf.modtime = statbuf->st_mtime; - res += utime(filename, &timebuf); - } -#endif + res += UTIL_utime(filename, statbuf); #if !defined(_WIN32) res += chown(filename, statbuf->st_uid, statbuf->st_gid); /* Copy ownership */ diff --git a/programs/util.h b/programs/util.h index c1c963434..7632b9a0a 100644 --- a/programs/util.h +++ b/programs/util.h @@ -136,6 +136,14 @@ int UTIL_stat(const char* filename, stat_t* statbuf); */ int UTIL_setFileStat(const char* filename, const stat_t* statbuf); +/** + * Set atime to now and mtime to the st_mtim in statbuf. + * + * Directly wraps utime() or utimensat(). Returns -1 on error. + * Does not validate filename is valid. + */ +int UTIL_utime(const char* filename, const stat_t *statbuf); + /* * These helpers operate on a pre-populated stat_t, i.e., the result of * calling one of the above functions. From 9cd6c1ff4d56fc74a2cbdfd9bcc82a64e0fe4bb7 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 4 Aug 2021 14:49:56 -0400 Subject: [PATCH 3/4] Update mtime and atime for Written Files --- programs/fileio.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/programs/fileio.c b/programs/fileio.c index da5412f27..b786b7e4d 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1647,6 +1647,7 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx, int closeDstFile = 0; int result; stat_t statbuf; + int transferMTime = 0; assert(ress.srcFile != NULL); if (ress.dstFile == NULL) { int dstFilePermissions = DEFAULT_FILE_PERMISSIONS; @@ -1654,6 +1655,7 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx, && UTIL_stat(srcFileName, &statbuf) && UTIL_isRegularFileStat(&statbuf) ) { dstFilePermissions = statbuf.st_mode; + transferMTime = 1; } closeDstFile = 1; @@ -1680,6 +1682,9 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx, DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno)); result=1; } + if (transferMTime) { + UTIL_utime(dstFileName, &statbuf); + } if ( (result != 0) /* operation failure */ && strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */ ) { @@ -2553,6 +2558,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx, int result; stat_t statbuf; int releaseDstFile = 0; + int transferMTime = 0; if ((ress.dstFile == NULL) && (prefs->testMode==0)) { int dstFilePermissions = DEFAULT_FILE_PERMISSIONS; @@ -2560,6 +2566,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx, && UTIL_stat(srcFileName, &statbuf) && UTIL_isRegularFileStat(&statbuf) ) { dstFilePermissions = statbuf.st_mode; + transferMTime = 1; } releaseDstFile = 1; @@ -2585,6 +2592,10 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx, result = 1; } + if (transferMTime) { + UTIL_utime(dstFileName, &statbuf); + } + if ( (result != 0) /* operation failure */ && strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */ ) { From a8f4612eab1a61da5cbadccacee43a440e8f34c0 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 4 Aug 2021 15:53:37 -0400 Subject: [PATCH 4/4] Remove sleep()s in Test; Replace with Artificial mtime This behavior of `touch` is standardized in POSIX, so it should be available. --- tests/playTests.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index d108d5d8e..d83001fee 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -597,11 +597,10 @@ fi println "\n===> zstd created file timestamp tests" datagen > tmp -sleep 1 # could also use touch -t but I don't know how portable that is. +touch -m -t 200001010000.00 tmp println "test : copy mtime in file -> file compression " zstd -f tmp -o tmp.zst assertSameMTime tmp tmp.zst -sleep 1 # could also use touch -t but I don't know how portable that is. println "test : copy mtime in file -> file decompression " zstd -f -d tmp.zst -o tmp.out assertSameMTime tmp.zst tmp.out