From: Konstantin Komarov almaz.alexandrovich@paragon-software.com
[ Upstream commit 5180138604323895b5c291eca6aa7c20be494ade ]
Before it used an unsigned 64-bit type, which prevented proper handling of timestamps earlier than 1970-01-01. Switch to a signed 64-bit type to support pre-epoch timestamps. The issue was caught by xfstests.
Signed-off-by: Konstantin Komarov almaz.alexandrovich@paragon-software.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
Now I have a complete understanding. Let me write the detailed analysis:
## Detailed Analysis
### 1. COMMIT MESSAGE ANALYSIS
**Subject:** `fs/ntfs3: Support timestamps prior to epoch`
**Commit Body:** - The commit explains that before this fix, the code used an **unsigned 64-bit type** (`u64`), which prevented proper handling of timestamps earlier than 1970-01-01 (the Unix epoch). - The fix switches to a **signed 64-bit type** (`s64`) to support pre- epoch timestamps. - The issue was caught by **xfstests test 258**, which specifically tests for timestamps prior to the epoch.
**Tags:** - **No `Cc: stable@vger.kernel.org` tag** is present. - **No `Fixes:` tag** is present.
### 2. CODE CHANGE ANALYSIS
#### The Bug:
In the current code (before the patch):
```980:987:fs/ntfs3/ntfs_fs.h static inline void nt2kernel(const __le64 tm, struct timespec64 *ts) { u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
// WARNING: do_div changes its first argument(!) ts->tv_nsec = do_div(t, _100ns2seconds) * 100; ts->tv_sec = t; } ```
The variable `t` is declared as `u64` (unsigned 64-bit integer). NTFS timestamps are stored as the number of 100-nanosecond intervals since January 1, 1601. After subtracting `SecondsToStartOf1970` (0x00000002B6109100), which represents the number of seconds between 1601 and 1970, `t` represents a Unix timestamp.
**Problem:** When the NTFS timestamp represents a date before January 1, 1970, the subtraction produces a **negative** result. But since `t` is unsigned (`u64`), the negative value wraps around to a very large positive value, resulting in a wrong (future) timestamp.
**The `do_div` macro** operates on unsigned integers and uses unsigned division, which doesn't handle negative values correctly.
#### The Fix:
The patch changes: 1. `u64 t` → `s64 t` (signed 64-bit integer) 2. `do_div(t, _100ns2seconds)` → `div_s64_rem(t, _100ns2seconds, &t32)` (signed division) 3. Introduces `s32 t32` to hold the remainder
The new code: ```c static inline void nt2kernel(const __le64 tm, struct timespec64 *ts) { s32 t32; /* use signed 64 bit to support timestamps prior to epoch. xfstest 258. */ s64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
ts->tv_sec = div_s64_rem(t, _100ns2seconds, &t32); ts->tv_nsec = t32 * 100; } ```
`div_s64_rem()` is designed for signed 64-bit division with a 32-bit divisor, properly handling negative values and returning the remainder through a pointer.
**Root Cause:** Using unsigned arithmetic for a value that can be negative (pre-epoch timestamps).
### 3. CLASSIFICATION
- **Bug fix:** YES - This is fixing incorrect behavior with timestamps before 1970. - **New feature:** NO - It doesn't add new functionality, only fixes existing functionality. - **Security fix:** NO - No security implications. - **Exception category:** NO - Not a device ID, quirk, DT update, build fix, or documentation fix.
### 4. SCOPE AND RISK ASSESSMENT
**Lines Changed:** Very minimal - changes ~8 lines of code within a single inline function.
**Files Touched:** 1 file (`fs/ntfs3/ntfs_fs.h`)
**Subsystem Complexity:** - NTFS3 is a relatively new filesystem (added in 5.15) - The change is in a core time conversion function `nt2kernel()` that is called whenever reading timestamps from disk - All file timestamps (creation, modification, access times) use this function
**Risk Assessment:** - **LOW RISK** - The change is small, surgical, and uses a well- established kernel API (`div_s64_rem`) - The new code is mathematically equivalent to the old code for timestamps after 1970 - For timestamps before 1970, the new code produces correct results (negative `tv_sec` values) - The `timespec64` structure's `tv_sec` field is `time64_t` which is signed (`__s64`), so it can properly represent negative timestamps
### 5. USER IMPACT
**Who is affected?** - Users of NTFS3 filesystem - Users with NTFS volumes containing files with pre-1970 timestamps
**How severe?** - The bug causes **data corruption** (incorrect timestamp display/storage) for pre-epoch files - Without this fix, timestamps like January 1, 1960 would appear as some date far in the future - This breaks file management tools and any software relying on correct timestamps
**Test Coverage:** - The fix was identified by **xfstests generic/258**, a standard filesystem test that specifically validates pre-epoch timestamp handling - This is a well-known and commonly run test suite
### 6. STABILITY INDICATORS
- **No `Tested-by:` tag** - However, xfstests is mentioned, implying the fix was tested against this test suite - **No `Reviewed-by:` tag** - The commit is from the ntfs3 maintainer (Konstantin Komarov from Paragon Software)
### 7. DEPENDENCY CHECK
- **No dependencies** - This is a standalone fix - Uses existing kernel APIs (`div_s64_rem`, `s64`, `s32`) that have been available for a long time - The affected code (`nt2kernel` function) has existed since ntfs3 was introduced in kernel 5.15
### Summary
**YES signals:** 1. ✅ Fixes a **real bug** (incorrect timestamps for pre-epoch dates) 2. ✅ **Small and surgical** change - only 8 lines changed in one function 3. ✅ **Obviously correct** - uses proper signed arithmetic for values that can be negative 4. ✅ No new features, no new APIs 5. ✅ Caught by **xfstests**, a standard fs test suite (test 258) 6. ✅ Applies to all stable kernels that have ntfs3 (5.15+) 7. ✅ Zero risk of regression for post-epoch timestamps (mathematically equivalent) 8. ✅ Fixes **data integrity issue** (timestamp corruption)
**NO signals:** 1. ⚠️ No explicit `Cc: stable@vger.kernel.org` tag 2. ⚠️ No `Fixes:` tag 3. ⚠️ No `Tested-by:` or `Reviewed-by:` tags
**Verdict:**
This is a clear bug fix that addresses a real issue (incorrect pre-epoch timestamp handling) that affects NTFS3 users. The fix is small, surgical, uses well-tested kernel APIs, and is mathematically correct. While the commit lacks stable tags, the nature of the fix - correcting data integrity issues with timestamps - makes it appropriate for backporting. The bug has existed since ntfs3 was introduced (5.15), so all stable trees with ntfs3 would benefit from this fix.
The lack of `Cc: stable` tag is likely an oversight since the fix clearly meets stable kernel criteria: it's a bug fix, small in scope, obviously correct, and fixes a real user-visible issue (incorrect file timestamps).
**YES**
fs/ntfs3/ntfs_fs.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 630128716ea73..2649fbe16669d 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -979,11 +979,12 @@ static inline __le64 kernel2nt(const struct timespec64 *ts) */ static inline void nt2kernel(const __le64 tm, struct timespec64 *ts) { - u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970; + s32 t32; + /* use signed 64 bit to support timestamps prior to epoch. xfstest 258. */ + s64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
- // WARNING: do_div changes its first argument(!) - ts->tv_nsec = do_div(t, _100ns2seconds) * 100; - ts->tv_sec = t; + ts->tv_sec = div_s64_rem(t, _100ns2seconds, &t32); + ts->tv_nsec = t32 * 100; }
static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb)