From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-yx1-f45.google.com (mail-yx1-f45.google.com [74.125.224.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 26A5630DECB for ; Wed, 14 Oct 2026 00:37:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.45 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791938272; cv=none; b=ujoDmvA0cOXjCW8PbXn3Yn7xJwAmawN5akfJ/O2AOdNeWh5hUKFOKbgvj1TVAAmQBwZbhy63ynhQOpriVfoipO5GS677vU90V2y+7ZA83lB2L9B+EHvbFzxYwUV+7S1Z3ewGJmK7vrMdrG/HQDO5lZy+NnFFz0ongKkVSxeR0P0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791938272; c=relaxed/simple; bh=IF6xi800bQg70p/YOaEzX7lIbgZssVLNkfMtxS1VX2w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RAInM7hP7kRTRRkzgMd+J696SJLZ8El8lMy6Scap6OnZEIB34zSXXVfgejEpnd4wdp2jPVEI47Aq721QaXI8SupKp/ZvPyeBHVc0cp1COQEEyv7AQc+0EPv3k6ooxwF/NkLKqCD9uNQWsd/YuBemkHj+nUSzuFaXtVk+UEPC68Y= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=dubeyko.com; spf=pass smtp.mailfrom=dubeyko.com; dkim=pass (2048-bit key) header.d=dubeyko-com.20251104.gappssmtp.com header.i=@dubeyko-com.20251104.gappssmtp.com header.b=eqTOqz3v; arc=none smtp.client-ip=74.125.224.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=dubeyko.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=dubeyko.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=dubeyko-com.20251104.gappssmtp.com header.i=@dubeyko-com.20251104.gappssmtp.com header.b="eqTOqz3v" Received: by mail-yx1-f45.google.com with SMTP id 956f58d0204a3-677dbe051d4so3354432d50.0 for ; Tue, 13 Oct 2026 17:37:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dubeyko-com.20251104.gappssmtp.com; s=20251104; t=1791938269; x=1792543069; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=fonBvjZO/D/GM9L+MsZkX1d2b3mKIY2/WZMtCJsJfEU=; b=eqTOqz3v3wmsRc2AM4HP/6q6yoBRp5WHvDut1mkdciOHuAtpLhC+k74m6YAewP3K4A +GPpSRZ4XNR+8mzN0boZApGgCzsiHWX0L2qa7hjVOBIv10AsXgmWPVojiT0SnLbU9qwQ 808ZUjO9sT2XLAnCxlGEqoZP2V2qDzc87H68IxbTdXiZxT7VOiQkKvdPrffR0nG99tYl b5dWEhXkEmgz1K3NPDU1obzuhAQmKrSfiTCtiSIboF6TGIj47w+Vybv4zCri3qiL5xg5 /2+ewk6VFCHmILIfPtLgFXAQYI2MAjFlso2TjihxF3z2ek3y/zBDCa+Nlo6XiE5gTsJY QWJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1791938269; x=1792543069; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=fonBvjZO/D/GM9L+MsZkX1d2b3mKIY2/WZMtCJsJfEU=; b=C9oHEMvvHkjR5XGjpmSyVYCdjd2yvxh/WFN5+ln1vUCoPH7xLr9QGH8FoM6j2lcK/C xmux0GgeWnkguKl8tf3u3FyBaUqBbHErTn/XhBLki0hyfZuYswbpWNUl22Rtb7HPxvKR Yrihw/j5nDRW6V12ZmsnGTAzlTOlfw/1HLumKze+L35HBcjGrXGjImKM/xLwnWLGtRiC L0U30iAle26nT6QAVKiaF9S59493Qvm1lCT1Gb7sQkTFXjEco6nmZL9I/Ssf2WjtdIFI yHShGYgaBPtCst50mSzQ2KdsGsh6C9jK2X6525lCg+S9KRg90XRiYMQJuRriOJbS1QPb Febw== X-Forwarded-Encrypted: i=1; AKwUvBwGNtQXXNUTUChf+N1dv0k7BARh1QSSSyJzK8BBYYHgf8gTGKe8cMed7ZGT5TdATHSL+sFsCIZBsUV/uw8=@vger.kernel.org X-Gm-Message-State: AFq9FYImDGy8oglNT5rMgd5cDgOaWeOToBO0Ei/APY5U9TyXafxp5FrI X2/UYQh9rUayZ2kHiCGnbMDmdmtj564NidVAZF25r0yJKgmpWhJ1oatwXwtpJJLGUt4= X-Gm-Gg: AYBFou0RiP3kNTql840XGoQc4ID+NCoFLQn6SQpv0mU3v+YSGRla0qw8EPP4fYnvRei hn//mjZcQjI6tJ7jeWD3AUHgJVYdL5YHcC30xXn4w67JFXOlPYGFngLpTI+VJNbKiMNgz1MX9rQ XXrjR8EPrclyWFNgku8y9/1MkyD5Km7/IaP26D7NtXhYZJeiFboQBC5aFt2awjLv28BZEtKvOhy 8a8mR5AQHa/GhvgKtnnuv/zVquIUmGliFaK5yKOdwStDwAfshCDx/6exAeASfjyvNz11Nz7bu6U 111XSC0Unj82qHqVkCVlkUv21EKleQ6jdyuDa7afTDFwMNdhjzr2PDrWeD43836/A2xcY2VW5YY k7aQJY1r7f1WT6cuztKuKkM4xtdeuosP6qNLN3LEApXXr2r4z/5qGdnBO1yqooDY0a+zSk4UxHq RXBQptq0QaUlLxSkPmsi2Qv/7JYG8XxZ2d9hLD0qqAnMtXdBxGuTO6NnuOWYpk5zLu9CR40QAnv dfTqHkO3vOdoS/6E09p+ylwoPjlWgEH8mzfDDYHSqhdMdo0yzarRwwMhrAJSaltyHFFY5NLrDUC aeq+PbE5c4K3QnH7iKGY5QdaUVxj X-Received: by 2002:a05:690e:4146:b0:677:d21f:b63f with SMTP id 956f58d0204a3-6793603f7e1mr3922393d50.49.1791938268820; Tue, 13 Oct 2026 17:37:48 -0700 (PDT) Received: from pop-os.attlocal.net ([2600:1700:6476:1430:98d3:ec2f:c938:3bc1]) by smtp.gmail.com with ESMTPSA id 956f58d0204a3-67986a15c13sm463194d50.7.2026.10.13.17.37.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 13 Oct 2026 17:37:48 -0700 (PDT) From: Viacheslav Dubeyko To: glaubitz@physik.fu-berlin.de, frank.li@vivo.com Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, vdubeyko@coreweave.com, Viacheslav Dubeyko Subject: [PATCH v3 1/3] hfsplus: fix timestamp wrapped issue Date: Tue, 13 Oct 2026 17:37:29 -0700 Message-ID: <20261014003731.369728-2-slava@dubeyko.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261014003731.369728-1-slava@dubeyko.com> References: <20261014003731.369728-1-slava@dubeyko.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The xfstests' test-case generic/258 fails to execute correctly: FSTYP -- hfsplus PLATFORM -- Linux/x86_64 hfsplus-testing-0001 6.15.0-rc4+ #8 SMP PREEMPT_DYNAMIC Thu May 1 16:43:22 PDT 2025 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch generic/258 [failed, exit status 1]- output mismatch (see xfstests-dev/results//generic/258.out.bad) The main reason of the issue is the logic: cpu_to_be32(lower_32_bits(ut) + HFSPLUS_UTC_OFFSET) At first, we take the lower 32 bits of the value and, then, we add the time offset. However, if we have negative value (timestamp before January 1, 1970), then we make completely wrong calculation. The reverse conversion has the same issue because it treats any on-disk timestamp under HFSPLUS_UTC_OFFSET (January 1, 1970) as a time between 2040 and 2106. This patch corrects the logic of __hfsp_mt2ut() and __hfsp_ut2mt(). Now, the on-disk timestamp is interpreted as unsigned number of seconds since January 1, 1904. As a result, the supported time range is from January 1, 1904 till February 6, 2040. The HFS_UTC_OFFSET constant has been moved into include/linux/hfs_common.h and HFSPLUS_UTC_OFFSET has been removed. The HFS_MIN_TIMESTAMP_SECS and HFS_MAX_TIMESTAMP_SECS constants have been introduced in include/linux/hfs_common.h. The hfsplus_fill_super() logic defines sb->s_time_min, sb->s_time_max, and sb->s_time_gran. It means that VFS clamps the timestamps that are out of the supported range. [1] https://github.com/hfs-linux-kernel/hfs-linux-kernel/issues/133 Signed-off-by: Viacheslav Dubeyko cc: John Paul Adrian Glaubitz cc: Yangtao Li cc: linux-fsdevel@vger.kernel.org --- fs/hfs/hfs_fs.h | 13 ------------- fs/hfsplus/hfsplus_fs.h | 17 +++++------------ fs/hfsplus/super.c | 4 ++++ include/linux/hfs_common.h | 13 +++++++++++++ 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index e250f87a5e33..659b0ba733d4 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -255,19 +255,6 @@ extern int hfs_mac2asc(struct super_block *sb, /* super.c */ extern void hfs_mark_mdb_dirty(struct super_block *sb); -/* - * There are two time systems. Both are based on seconds since - * a particular time/date. - * Unix: signed little-endian since 00:00 GMT, Jan. 1, 1970 - * mac: unsigned big-endian since 00:00 GMT, Jan. 1, 1904 - * - * HFS implementations are highly inconsistent, this one matches the - * traditional behavior of 64-bit Linux, giving the most useful - * time range between 1970 and 2106, by treating any on-disk timestamp - * under HFS_UTC_OFFSET (Jan 1 1970) as a time between 2040 and 2106. - */ -#define HFS_UTC_OFFSET 2082844800U - static inline time64_t __hfs_m_to_utime(__be32 mt) { time64_t ut = (u32)(be32_to_cpu(mt) - HFS_UTC_OFFSET); diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 916e6552e3f0..cd51afa22ece 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -525,26 +525,19 @@ bool is_hfs_thread_record_type(u16 type) int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry); -/* - * time helpers: convert between 1904-base and 1970-base timestamps - * - * HFS+ implementations are highly inconsistent, this one matches the - * traditional behavior of 64-bit Linux, giving the most useful - * time range between 1970 and 2106, by treating any on-disk timestamp - * under HFSPLUS_UTC_OFFSET (Jan 1 1970) as a time between 2040 and 2106. - */ -#define HFSPLUS_UTC_OFFSET 2082844800U - static inline time64_t __hfsp_mt2ut(__be32 mt) { - time64_t ut = (u32)(be32_to_cpu(mt) - HFSPLUS_UTC_OFFSET); + time64_t ut = (time64_t)be32_to_cpu(mt) - HFS_UTC_OFFSET; return ut; } static inline __be32 __hfsp_ut2mt(time64_t ut) { - return cpu_to_be32(lower_32_bits(ut) + HFSPLUS_UTC_OFFSET); + ut += HFS_UTC_OFFSET; + + return cpu_to_be32(lower_32_bits(ut)); + } static inline enum hfsplus_btree_mutex_classes diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index ff7d6b3336a6..657dfa40bdf3 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -511,6 +511,10 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) if (!sbi->rsrc_clump_blocks) sbi->rsrc_clump_blocks = 1; + sb->s_time_gran = NSEC_PER_SEC; + sb->s_time_min = HFS_MIN_TIMESTAMP_SECS; + sb->s_time_max = HFS_MAX_TIMESTAMP_SECS; + err = -EFBIG; last_fs_block = sbi->total_blocks - 1; last_fs_page = (last_fs_block << sbi->alloc_blksz_shift) >> diff --git a/include/linux/hfs_common.h b/include/linux/hfs_common.h index d6a615e74b26..d4e91dea2362 100644 --- a/include/linux/hfs_common.h +++ b/include/linux/hfs_common.h @@ -667,4 +667,17 @@ typedef union { struct hfsplus_attr_key attr; } __packed hfsplus_btree_key; +/* + * There are two time systems. Both are based on seconds since + * a particular time/date. + * Unix: signed little-endian since 00:00 GMT, Jan. 1, 1970 + * mac: unsigned big-endian since 00:00 GMT, Jan. 1, 1904 + */ +#define HFS_UTC_OFFSET 2082844800U + +/* January 1, 1904, 00:00:00 UTC */ +#define HFS_MIN_TIMESTAMP_SECS -2082844800LL +/* February 6, 2040, 06:28:15 UTC */ +#define HFS_MAX_TIMESTAMP_SECS 2212122495LL + #endif /* _HFS_COMMON_H_ */ -- 2.43.0