APFS Snapshots and Timestamps: A Forensic Guide for macOS
How APFS volumes, firmlinks, local snapshots and nanosecond timestamps work on macOS, and how to use them to build timelines and spot timestomping.
The Apple File System (APFS) is where almost every macOS timeline begins. It answers the questions an investigator asks first: when was this file created, when was its content last changed, when were its permissions or name last touched, and when did it land in this folder. APFS also adds something older file systems lacked on the Mac, local snapshots, which can hold a read-only copy of the Data volume from hours earlier. This article covers the volume layout you need to understand before collecting anything, how to find and mount snapshots, the timestamps APFS stores and their epochs, how copy and move operations affect them, and practical hints for detecting timestomping.
APFS containers, volumes and the Catalina split
An APFS container is a single partition that holds one or more volumes. Volumes in the same container share free space, so there is no fixed size per volume. On a typical internal disk you will see something like this:
diskutil apfs list
diskutil list internal
Since macOS 10.15 Catalina, the operating system lives on a read-only System volume while user data, applications installed by the user and most configuration live on a separate Data volume. The two are tied together in a volume group and presented as a single tree at /. On macOS 11 Big Sur and later, the System volume is additionally protected as a sealed system volume: the Mac boots from a cryptographically sealed snapshot of it, so its content is essentially the Apple-shipped build and rarely holds case evidence.
For forensics this means:
- The Data volume is the one that matters. It is mounted at
/System/Volumes/Dataon a running system. - Paths such as
/Users,/Applications,/Libraryand/privateappear at the root but physically reside on the Data volume. - When you examine an image offline, the tool must understand the volume group, or you must analyse the Data volume directly and mentally map paths back to
/.
Firmlinks
The glue between the two volumes is the firmlink, a bidirectional link type that exists only for this purpose. The list of firmlinked directories is stored in /usr/share/firmlinks on the System volume:
cat /usr/share/firmlinks
Each line maps a root path (for example /Users) to its relative location on the Data volume (Users). The exact list changes between releases, so read it from the evidence system instead of relying on memory. Firmlinks explain why the same file can appear under two paths in a live collection (/Users/analyst/... and /System/Volumes/Data/Users/analyst/...), which is a common source of duplicate rows in triage output.
APFS snapshots
An APFS snapshot is a read-only, point-in-time view of a volume. Because APFS is copy-on-write, a snapshot costs almost nothing when created and only grows as blocks it references are overwritten in the live volume. Snapshots are relevant for three reasons:
- Time Machine local snapshots. When Time Machine is configured, macOS creates local snapshots of the Data volume, typically hourly, and removes them automatically, usually after about 24 hours or sooner under disk pressure.
- Update snapshots. macOS creates snapshots around software updates.
- Third-party backup software may create its own snapshots.
Listing snapshots
# Time Machine local snapshots for the Data volume
tmutil listlocalsnapshots /
# All snapshots on a given APFS volume, with XIDs
diskutil apfs listSnapshots /System/Volumes/Data
Local Time Machine snapshot names follow the pattern com.apple.TimeMachine.YYYY-MM-DD-HHMMSS.local, which already gives you an approximate creation time. diskutil apfs listSnapshots also shows the transaction ID (XID), useful when ordering snapshots.
Mounting a snapshot read-only
sudo mkdir /tmp/snap
sudo mount_apfs -o rdonly -s com.apple.TimeMachine.2026-09-27-093012.local \
/System/Volumes/Data /tmp/snap
You can now browse /tmp/snap as the Data volume looked at that moment, diff it against the live volume, and copy files out with your normal hashing workflow. Unmount with sudo umount /tmp/snap when done. Reading from a mounted snapshot requires root and, for protected locations, a terminal with Full Disk Access.
Creating a snapshot during live response
tmutil localsnapshot asks macOS to create a new local snapshot. Some responders use this to freeze the Data volume before running a long logical collection, then collect from the mounted snapshot so every file reflects the same instant. It is a change to the system (it adds a snapshot and consumes space over time), so document it and weigh it against your acquisition policy. See macOS forensic acquisition for where this fits in an order-of-volatility plan.
The APFS timestamps
Each APFS inode record stores four timestamps as unsigned 64-bit integers counting nanoseconds since the Unix epoch (1970-01-01 00:00:00 UTC):
| Timestamp | APFS field | POSIX / stat name | Updated when |
|---|---|---|---|
| Birth (created) | create_time | st_birthtime, %B | The inode is created |
| Modified | mod_time | st_mtime, %m | File content changes |
| Changed | change_time | st_ctime, %c | Metadata changes (permissions, owner, name, xattrs, other timestamps) |
| Accessed | access_time | st_atime, %a | Content is read, subject to the system's atime policy |
Nanosecond resolution is a real improvement over HFS+, which stored whole seconds. It also gives you a cheap anomaly check, discussed below.
Reading timestamps on a live system
# Birth, modify, change, access in a readable format
stat -f "B:%SB M:%Sm C:%Sc A:%Sa" -t "%Y-%m-%d %H:%M:%S %z" report.pdf
# Full nanosecond values (requires python3 from the Command Line Tools)
python3 -c 'import os,sys; s=os.stat(sys.argv[1]); print(s.st_birthtime, s.st_mtime_ns, s.st_ctime_ns, s.st_atime_ns)' report.pdf
# Spotlight view of the same file
mdls -name kMDItemFSCreationDate -name kMDItemContentModificationDate \
-name kMDItemDateAdded -name kMDItemLastUsedDate report.pdf
Treat access time with caution. It may be updated lazily, may not be updated at all for some reads, and can be touched by indexing, backup or security tools. It is weak evidence of user activity on its own.
Date Added and Last Used
Two more values appear in the Finder and in Spotlight:
- Date Added (
kMDItemDateAdded) records when an item was placed into its current parent folder. It is maintained separately from the four POSIX timestamps and changes when a file is moved into a different folder, even though the move preserves birth and modification times. This makes it one of the best indicators of when a download or a copied file arrived in~/Downloadsor on the Desktop. - Last Used (
kMDItemLastUsedDate) is set by Launch Services when a user opens the item through the GUI. It is not updated by direct reads such ascator scripted file access, although opening a file with theopencommand goes through Launch Services. Related attributes such askMDItemUseCountandkMDItemUsedDatesare covered in Spotlight forensics.
Timestamp epochs you will meet on a Mac
A single macOS case routinely mixes several epochs. Getting one wrong shifts events by decades or by 31 years, a common mistake with Cocoa dates.
| Epoch | Origin | Unit | Where you see it | Convert to Unix seconds |
|---|---|---|---|---|
| Unix | 1970-01-01 UTC | seconds or nanoseconds | APFS inodes, many logs | as is (divide ns by 1,000,000,000) |
| Mac Absolute Time (Cocoa / Core Data) | 2001-01-01 UTC | seconds, often fractional | knowledgeC, Safari History.db, many plists | add 978307200 |
| HFS+ | 1904-01-01 | seconds | legacy HFS+ volumes, some resource structures | subtract 2082844800 |
| WebKit / Chrome | 1601-01-01 UTC | microseconds | Chrome and Chromium history | divide by 1,000,000, then subtract 11644473600 |
For SQLite work on Cocoa dates:
SELECT datetime(ZSTARTDATE + 978307200, 'unixepoch') AS start_utc
FROM ZOBJECT LIMIT 5;
How copy and move operations affect timestamps
Behaviour depends on the tool and on whether the operation stays within one volume. The table below summarises typical behaviour; confirm on the macOS version you are examining before relying on it in a report.
| Operation | Birth | Modified | Changed | Date Added |
|---|---|---|---|---|
Move/rename within the same volume (mv, Finder drag) | preserved | preserved | updated | updated if parent folder changes |
| Finder copy | typically preserved from source | preserved | new | new |
cp without flags | new | new | new | new |
cp -p or ditto | depends on tool and flags | preserved | new | new |
| Move to another volume | behaves like copy then delete | usually preserved | new | new |
APFS clone (cp -c, Finder duplicate on APFS) | new inode, check carefully | usually preserved | new | new |
The practical lesson is that a birth time earlier than the Date Added value is completely normal for downloaded or copied content. It tells you the file existed elsewhere before it arrived in its current location.
Timestomping detection hints
Timestomping is the deliberate alteration of timestamps to blend a file into its surroundings. No single check proves it, but several indicators together are persuasive.
Compare ctime with the other timestamps
User-space tools like touch can set modification and access times, and on macOS moving the modification time earlier than the birth time generally pulls the birth time back as well. They cannot directly set the change time, because rewriting timestamps is itself a metadata change. A file whose birth and modification times sit in 2021 while its ctime is last Tuesday deserves a closer look, especially if nothing else explains the metadata change.
Look at the nanoseconds
Genuine timestamps written by the file system almost always have non-zero sub-second components. Tools that set times from a human-readable value often write whole seconds, leaving the nanosecond portion as zero. A cluster of files with .000000000 modification times in a directory where neighbours have ordinary nanosecond values is a strong hint.
Cross-check against independent sources
- Date Added and Spotlight metadata are not modified by
touch. - FSEvents records creation, modification and inode metadata changes by path. An
InodeMetaModevent on a file whose modification time claims to be years old is suspicious. - Quarantine data gives an independent download time for files fetched by quarantine-aware apps.
- Unified Logs may record the process that wrote or executed the file.
- Snapshots let you compare the file's timestamps as they stood hours earlier.
Caveats
- Live collection changes access times. Copying a file reads it. Prefer tools that record metadata before reading content, or collect from a snapshot.
- Firmlink duplicates. Deduplicate by inode number and volume, not just by path.
- Time zone. APFS stores UTC. Convert for display only, and state the zone in reports.
- Snapshots are ephemeral. Time Machine local snapshots disappear on their own schedule and under disk pressure. Record
tmutil listlocalsnapshots /output early. - Encryption. On FileVault-protected or Apple Silicon systems, offline access to the Data volume requires unlocking it. See Apple Silicon forensics.
- Version drift. Firmlink lists, snapshot retention and timestamp update policy have changed across releases. Verify on your target version.
Frequently asked questions
Can an APFS local snapshot recover a file that has since been deleted?
Yes, if a snapshot was taken while the file existed and the snapshot has not been purged. Mount the snapshot read-only with mount_apfs -s and copy the file out. Time Machine local snapshots are short-lived and are removed automatically, so check for them early.
Which APFS timestamp is hardest for a user to change?
The inode change time (ctime). Common user-level tools such as touch can set modification and access times, and birth time can be moved backwards, but ctime is updated by the file system whenever metadata changes, including when someone rewrites the other timestamps.
Where does the Finder 'Date Added' value come from?
It is a separate value maintained by the file system for items placed into a folder and surfaced by Spotlight as kMDItemDateAdded. It is not one of the four POSIX timestamps and it changes when an item is moved into a different folder.