Thursday, September 28, 2017

APFS timestamps

From APFS documentation it is revealed that the new timestamp has a nano second resolution. From my data I can see several timestamps in what appears to be the root directory for a test APFS volume I created. Armed with this information, it was pretty easy to guess the epoch - it is the Unix epoch.

ApfsTimeStamp = number of nano-seconds since 1-1-1970

If you like to use the 010 editor for analysis, put the following code in your Inspector.bt or InspectorDates.bt file:

//----------------------------------------------------------------
// ApfsTime
//  64-bit integer, number of nanoseconds since 01/01/1970 00:00:00

typedef int64 ApfsTime <read=ApfsTimeRead, write=ApfsTimeWrite>;
FSeek( pos ); ApfsTime _aft <name="ApfsTimes">;
string ApfsTimeRead( ApfsTime t )
{   
    // Convert to FILETIME
    return FileTimeToString( t/100L + 116444736000000000L );
}
int ApfsTimeWrite( ApfsTime &t, string value )
{
    // Convert from FILETIME
    FILETIME ft;
    int result = StringToFileTime( value, ft );
    t = (((int64)ft - 116444736000000000L)*100L);
    return result;
}

Now 010 can interpret APFS timestamps :)

010 interprets ApfsTimes

If you need to read this timestamp in python (and convert to python datetime), the following function will do this:


import datetime


def ConvertApfsTime(ts):
    try:
        return datetime.datetime(1970,1,1) + datetime.timedelta(microseconds=ts / 1000. )
    except:
        pass
  return None

Update: As noted by Joachim, the timestamp is an int64, and has been corrected in the above code.

3 comments:

  1. To me it looks more likely the the timestamp is a signed value.

    ```
    touch -t 194001010000 /Volumes/SingleVolume/myfile.txt

    -rw-r--r-- 1 user staff 0 Jan 1 1940 myfile.tzt
    ```

    ```
    printf "0x%x\n" $(( `date -u -d"1940-01-01 00:00:00" +"%s"` * 1000000000 ))
    0xf2dc649c1c6e0000
    ```

    ```
    00000000: 02 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 ........ ........
    00000010: 3a c7 32 1b a1 90 54 15 00 60 b5 eb 55 61 dc f2 :.2...T. .`..Ua..
    00000020: 01 68 33 1b a1 90 54 15 00 60 b5 eb 55 61 dc f2 .h3...T. .`..Ua..
    00000030: 00 80 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ........ ........
    00000040: 01 00 00 00 00 00 00 00 63 00 00 00 63 00 00 00 ........ c...c...
    00000050: a4 81 00 00 00 00 00 00 00 00 00 00 01 00 10 00 ........ ........
    00000060: 04 02 0b 00 6d 79 66 69 6c 65 2e 74 78 74 00 00 ....myfi le.txt..
    00000070: 00 00 00 00 ....
    ```

    `00 60 b5 eb 55 61 dc f2` matches the calculated value 0xf2dc649c1c6e0000

    ReplyDelete
  2. You are correct Joachim. This should be int64, not uint64. Thanks for catching that bug (untested use case). I will change all my APFS library code to reflect this.

    ReplyDelete
  3. Thx, for the reader, I've seen this error in several publications about APFS by now.

    ReplyDelete