From 83a75c7fac68e6bd50e23c12e5e04c4fce8aec37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= <nabijaczleweli@nabijaczleweli.xyz>
Date: Sat, 9 Nov 2024 03:12:42 +0100
Subject: [PATCH] Encode and decode file times in UTC, not in/out of local time

For encoding, just use gmtime(3) instead of localtime(3).

For decoding, we use mktime(3), which has no UTC equivalents,
so just put ourselves in UTC
(with putenv("TZ=UTC0"),
 this is the most correct naming and implementation per POSIX,
 and it's what date -u does).

Test script:
  make -j fusefatfs
  mv fusefatfs fusefatfs.before
  # apply patch
  make -j fusefatfs
  mv fusefatfs fusefatfs.after

  for f in before after; do
  	truncate -s 1G $f
  	mkdir $f.d
  	mkfs.vfat $f
  	./fusefatfs.$f -o rw+ $f $f.d
  done
  touch before.d/now after.d/now
  ls -l --time-style=full *.d/now
  date -Is

  -rwxr-xr-x 1 root root 0 2024-11-09 03:11:36.000000000 +0100 before.d/now
  -rwxr-xr-x 1 root root 0 2024-11-09 03:11:36.000000000 +0100 after.d/now
  2024-11-09T03:11:37+01:00

  umount *.d
  for f in before after; do
  	sudo mount $f $f.d
  done
  ls -l --time-style=full *.d/now

  -rwxr-xr-x 1 root root 0 2024-11-09 04:11:36.000000000 +0100 before.d/now
  -rwxr-xr-x 1 root root 0 2024-11-09 03:11:36.000000000 +0100 after.d/now

Closes #6
---
 fusefatfs.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fusefatfs.c b/fusefatfs.c
index cebd996..02ea77e 100644
--- a/fusefatfs.c
+++ b/fusefatfs.c
@@ -360,7 +360,7 @@ static int fff_utimens(const char *path, const struct timespec tv[2]) {
 	FILINFO fno;
 	struct tm tm;
 	time_t newtime = tv[1].tv_sec;
-	if (localtime_r(&newtime, &tm) == NULL)
+	if (gmtime_r(&newtime, &tm) == NULL)
 		mutex_out_return(-EINVAL);
 	fno.fdate =
 		/* bit15:9: Year origin from the 1980 (0..127, e.g. 37 for 2017) */
@@ -552,6 +552,7 @@ int main(int argc, char *argv[])
 	struct fftab *ffentry;
 	int flags = 0;
 	struct stat sbuf;
+	putenv("TZ=UTC0");
 	if (fuse_opt_parse(&args, &options, fff_opts, fff_opt_proc) == -1) {
 		fuse_opt_free_args(&args);
 		return -1;
