Skip to content

Instantly share code, notes, and snippets.

@jackrusher
Created July 20, 2023 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jackrusher/5a427e28e43b7ed921028f138feaaf41 to your computer and use it in GitHub Desktop.
Save jackrusher/5a427e28e43b7ed921028f138feaaf41 to your computer and use it in GitHub Desktop.
Read the extended attributes from an AppleDouble file (those ._foo.txt files that Apple sprinkles on network drives, in Zip archives, &c).
(let [buf (java.nio.ByteBuffer/wrap byte-buffer-containing-appledouble-binary)
magic-number (.getInt buf)
version-number (.getInt buf)
_ (.position buf 24) ; after magic / version / filler bytes
num-entries (.getShort buf)
;; first entry header, for the data fork
_ (Integer/toUnsignedLong (.getInt buf)) ; entry-id
offset (Integer/toUnsignedLong (.getInt buf))]
;; some sanity checking
(when (not= magic-number 0x51607)
(throw (Exception. "Bad magic number in AppleDouble header, probably corrupt.")))
(when (not= version-number 0x20000)
(throw (Exception. "Bad version number in AppleDouble header, probably corrupt.")))
(when (not= num-entries 2) ; data fork + resource fork
(throw (Exception. "Wrong number of entries in AppleDouble file, probably corrupt.")))
(.position buf (+ offset 34)) ; skip the FinderInfo section
(when (not= (.getInt buf) 0x41545452) ; as bytes = "ATTR"
(throw (Exception. "Wrong magic number in attribute header, probably corrupt.")))
;; skip headers that are redundant for our purposes
(.position buf (+ (.position buf) 30))
;; parse the attributes themselves
(repeatedly (.getShort buf)
(fn []
(let [value-offset (Integer/toUnsignedLong (.getInt buf)) ; no getUnsignedInt :/
value-length (Integer/toUnsignedLong (.getInt buf))
_ (.getShort buf) ; some flags
value (let [value-ba (byte-array value-length)]
(.mark buf)
(.position buf value-offset)
(.get buf value-ba)
(.reset buf)
(String. value-ba))
name-len (.get buf) ; attribute name length, always <=128
ba (byte-array (dec name-len))] ; includes null terminator, so dec
(.get buf ba)
;; eat the null and pad out to 4-byte boundary for next record
(.position buf (+ (.position buf) (mod (+ 11 name-len) 4) 1))
[(String. ba "UTF-8") value]))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment