all: properly handle time_t on 32-bit platforms

Do not force 64-bit `time_t` on 32-bit platforms, since libfuse
does not do so.  Linking shared objects with incompatible types
breaks ABI, resulting in undefined behavior.

Instead, add run-time checks to make sure that timestamps do not
overflow.  If they do, set to `INT32_MAX`.

Also tidy up build scripts, tests, and the installation guide.
This commit is contained in:
CismonX 2025-06-14 11:43:03 +08:00
parent f1451d206e
commit 6fc165ff65
No known key found for this signature in database
GPG key ID: 3094873E29A482FB
5 changed files with 36 additions and 47 deletions

View file

@ -166,19 +166,6 @@ Notes
In this case, the utility library will not be built from source, In this case, the utility library will not be built from source,
and other components will link to the specified library instead. and other components will link to the specified library instead.
### Targeting 32-bit Platforms
BookmarkFS requires 64-bit `off_t` and `time_t`, which may not be supported
on 32-bit platforms (e.g., i386 FreeBSD does not support 64-bit `time_t`).
If using Autoconf 2.72 or later, the configuration script automatically
performs checks and defines necessary macros, and fails if unsupported.
With legacy Autoconf, only `off_t` is checked.
To manually configure 64-bit `time_t`, add preprocessor flags
`-D_TIME_BITS=64` (or something equivalent, depending on the toolchain).
If unsupported, `make` will fail.
### FreeBSD and GNU libiconv ### FreeBSD and GNU libiconv
NOTE: You may skip this section if _not_ building the Chromium backend. NOTE: You may skip this section if _not_ building the Chromium backend.

View file

@ -196,18 +196,7 @@ AS_VAR_IF([ac_have_largefile], [no], [
])) ]))
]) ])
m4_version_prereq([2.72], [ AX_COMPILE_CHECK_SIZEOF([time_t])
AC_SYS_YEAR2038
AS_VAR_IF([ac_have_year2038], [no], [
AC_MSG_ERROR(m4_normalize([
64-bit time_t is unsupported on this platform.
]))
])
], [
dnl fallback to compile-time check...
AX_COMPILE_CHECK_SIZEOF([time_t])
])
AX_COMPILE_CHECK_SIZEOF([uintptr_t], [#include <stdint.h>]) AX_COMPILE_CHECK_SIZEOF([uintptr_t], [#include <stdint.h>])
# -- Output -- # -- Output --

View file

@ -58,17 +58,14 @@
#include "watcher.h" #include "watcher.h"
#include "xstd.h" #include "xstd.h"
#if defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T != 8)
# error "64-bit time_t is required"
#endif
#define BACKEND_FILENAME_GUID ( 1u << 16 ) #define BACKEND_FILENAME_GUID ( 1u << 16 )
// Chromium uses Windows FILETIME epoch instead of Unix epoch. // Chromium uses Windows FILETIME epoch, which is
// `((1970 - 1601) * 365 + 89) * 24 * 3600` seconds before the Unix epoch.
// //
// See Chromium source code: /base/time/time.h // See Chromium source code: /base/time/time.h
// (`base::Time::kTimeTToMicrosecondsOffset`) // (`base::Time::kTimeTToMicrosecondsOffset`)
#define EPOCH_DIFF ( (time_t)((1970 - 1601) * 365 + 89) * 24 * 3600 ) #define EPOCH_DIFF INT64_C(11644473600)
#define BOOKMARKS_ROOT_ID 0 #define BOOKMARKS_ROOT_ID 0
@ -387,7 +384,7 @@ build_tsnode (
ts = &now; ts = &now;
} }
time_t secs = ts->tv_sec + EPOCH_DIFF; int64_t secs = ts->tv_sec + EPOCH_DIFF;
int64_t microsecs = secs * 1000000 + ts->tv_nsec / 1000; int64_t microsecs = secs * 1000000 + ts->tv_nsec / 1000;
char buf[32]; char buf[32];
@ -1461,12 +1458,17 @@ parse_ts (
} }
if (buf != NULL) { if (buf != NULL) {
time_t secs = microsecs / 1000000; int64_t secs = microsecs / 1000000 - EPOCH_DIFF;
if (unlikely(secs < EPOCH_DIFF)) { if (unlikely(secs < 0)) {
// Stay away from negative tv_sec // Stay away from negative tv_sec
secs = EPOCH_DIFF; secs = 0;
} }
buf->tv_sec = secs - EPOCH_DIFF; #if defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T < 8)
else if (secs > INT32_MAX) {
secs = INT32_MAX;
}
#endif
buf->tv_sec = secs;
buf->tv_nsec = (microsecs % 1000000) * 1000; buf->tv_nsec = (microsecs % 1000000) * 1000;
} }
return 0; return 0;

View file

@ -2135,7 +2135,7 @@ static int64_t
timespec_to_usecs ( timespec_to_usecs (
struct timespec const *ts struct timespec const *ts
) { ) {
return ts->tv_sec * 1000000 + ts->tv_nsec / 1000; return (int64_t)ts->tv_sec * 1000000 + ts->tv_nsec / 1000;
} }
static int static int
@ -2778,11 +2778,16 @@ usecs_to_timespec (
struct timespec *ts_buf, struct timespec *ts_buf,
int64_t microsecs int64_t microsecs
) { ) {
if (unlikely(microsecs < 0)) { int64_t secs = microsecs / 1000000;
microsecs = 0; if (unlikely(secs < 0)) {
secs = 0;
} }
#if defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T < 8)
ts_buf->tv_sec = microsecs / 1000000; else if (secs > INT32_MAX) {
secs = INT32_MAX;
}
#endif
ts_buf->tv_sec = secs;
ts_buf->tv_nsec = (microsecs % 1000000) * 1000; ts_buf->tv_nsec = (microsecs % 1000000) * 1000;
} }

View file

@ -31,13 +31,14 @@
#include <unistd.h> #include <unistd.h>
#include "check_util.h" #include "check_util.h"
#include "backend_util.h"
#include "frontend_util.h" #include "frontend_util.h"
#include "prng.h" #include "prng.h"
// Forward declaration start // Forward declaration start
static int compare_timespec (struct timespec, struct timespec); static int compare_timespec (struct timespec, struct timespec);
static int do_check_fs_times (int, int); static int do_check_fs_times (int, int);
static void usecs_to_timespec (struct timespec *, uint32_t); static void usecs_to_timespec (struct timespec *, uint64_t);
// Forward declaration end // Forward declaration end
static int static int
@ -113,9 +114,8 @@ do_check_fs_times (
ASSERT_NE(-1, compare_timespec(stat_buf.st_atim, now)); ASSERT_NE(-1, compare_timespec(stat_buf.st_atim, now));
for (int i = 0; i < rounds; ++i) { for (int i = 0; i < rounds; ++i) {
uint64_t bits = prng_rand(); usecs_to_timespec(&times[0], prng_rand());
usecs_to_timespec(&times[0], bits & 0xffffffff); usecs_to_timespec(&times[1], prng_rand());
usecs_to_timespec(&times[1], bits >> 32);
ASSERT_EQ(0, futimens(fd, times)); ASSERT_EQ(0, futimens(fd, times));
ASSERT_EQ(0, fstat(fd, &stat_buf)); ASSERT_EQ(0, fstat(fd, &stat_buf));
@ -142,9 +142,15 @@ do_check_fs_times (
static void static void
usecs_to_timespec ( usecs_to_timespec (
struct timespec *ts_buf, struct timespec *ts_buf,
uint32_t usecs uint64_t usecs
) { ) {
ts_buf->tv_sec = usecs / 1000000; int64_t secs = usecs / 1000000;
#if defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T < 8)
secs %= (int64_t)INT32_MAX + 1;
#else
secs %= TIMESPEC_SEC_MAX + 1;
#endif
ts_buf->tv_sec = secs;
ts_buf->tv_nsec = (usecs % 1000000) * 1000; ts_buf->tv_nsec = (usecs % 1000000) * 1000;
} }