3 # AppleSingle/AppleDouble dump
5 # (c) 2009-2010 by HAT <hat@fa2.so-net.ne.jp>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
21 # Applesingle and AppleDouble format internals (version 1)
22 # http://users.phg-online.de/tk/netatalk/doc/Apple/v1/
24 # AppleSingle/AppleDouble Formats for Foreign Files Developer's Note (version2)
25 # http://users.phg-online.de/tk/netatalk/doc/Apple/v2/AppleSingle_AppleDouble.pdf
27 # Inside Macintosh: Macintosh Toolbox Essentials /
28 # Chapter 7 - Finder Interface / Finder Interface Reference
29 # http://developer.apple.com/legacy/mac/library/documentation/mac/toolbox/Toolbox-463.html
31 # Finder Interface Reference
32 # http://developer.apple.com/legacy/mac/library/documentation/Carbon/Reference/Finder_Interface/Reference/reference.html
34 # Technical Note TN1150 HFS Plus Volume Format
35 # http://developer.apple.com/mac/library/technotes/tn/tn1150.html#FinderInfo
37 # CarbonHeaders source
38 # http://www.opensource.apple.com/source/CarbonHeaders/CarbonHeaders-8A428/Finder.h
39 # http://www.opensource.apple.com/source/CarbonHeaders/CarbonHeaders-9A581/Finder.h
43 # /usr/bin/GetFileInfo
45 # Mac OS X 10.6.2 kernel source
46 # http://www.opensource.apple.com/source/xnu/xnu-1486.2.11/bsd/vfs/vfs_xattr.c
51 use bigint; # require perl >= 5.8
53 # parse command line -----------------------------------------------
55 $finderinfo = 0; # 0: unknown 1: file 2: directory
56 while ($arg = shift @ARGV)
58 if ($arg =~ /^(-h|-help|--help)$/ ) {
59 printf ("usage: %s [-a] FILE|DIR\n" ,basename($0));
60 printf (" or: %s -f FILE\n" ,basename($0));
61 printf (" or: %s -d FILE\n" ,basename($0));
62 printf (" or: %s -h|-help|--help\n" ,basename($0));
63 printf (" or: %s -v|-version|--version\n" ,basename($0));
64 printf ("Dump AppleSingle/AppleDouble format file.\n");
66 printf (" -a (default) Dump a AppleSingle/AppleDouble file for FILE or DIR\n");
67 printf (" automatically.\n");
68 printf (" Extrapolate FinderInfo type from absolute path.\n");
69 printf (" If FILE is not AppleSingle/AppleDouble format,\n");
70 printf (" look for '.AppleDouble/FILE' and '._FILE'.\n");
71 printf (" If DIR, look for 'DIR/.AppleDouble/.Parent' and '._DIR'.\n");
72 printf (" -f Dump FILE. Assume FinderInfo to be FileInfo.\n");
73 printf (" -d Dump FILE. Assume FinderInfo to be DirInfo.\n");
74 printf (" -h,-help,--help Display this help and exit\n");
75 printf (" -v,-version,--version Show version and exit\n");
77 printf ("There is no way to detect whether FinderInfo is FileInfo or DirInfo.\n");
78 printf ("By default, %s examins whether file or directory,\n" ,basename($0));
79 printf ("a parent directory is .AppleDouble, filename is ._*, filename is .Parent,\n");
80 printf ("and so on.\n");
81 printf ("If setting option -f or -d, %s assume FinderInfo and doesn't look for\n");
82 printf ("another file.\n");
84 } elsif ($arg =~ /^(-v|-version|--version)$/ ) {
85 printf ("%s \(Netatalk @NETATALK_VERSION@\)\n", basename($0));
87 } elsif ($arg eq "-a") {
89 } elsif ($arg eq "-f") {
91 } elsif ($arg eq "-d") {
93 } elsif ($arg =~ /^-/) {
94 printf (STDERR "%s: invalid option %s\n", basename($0), $arg);
95 printf (STDERR "Try \`%s\ -h' for more information.\n", basename($0));
103 printf (STDERR "missing file operand.\n");
107 printf (STDERR "\"%s\" not found.\n", $afile);
111 # detect FinderInfo, and search AppleSingle/AppleDouble file --------------
113 $abspath = File::Spec->rel2abs($afile);
114 ($basename, $path, $ext) = fileparse($abspath);
116 if ( $finderinfo != 0 ) {
118 } elsif ( -f $afile ) {
119 if ( $basename eq ".Parent") {
121 } elsif ( $path =~ /\/.AppleDouble\/$/ ) {
123 } elsif ( $basename =~ /^._/ ) {
124 if ( -f $path.substr($basename, 2) ) {
126 } elsif ( -d $path.substr($basename, 2) ) {
130 if (!open(INFILE, "<$afile")) {
131 printf (STDERR "cannot open %s\n", $afile);
135 $val = unpack("N", $buf );
137 if ($val == 0x00051600 || $val == 0x00051607) {
140 printf ("\"%s\" is not AppleSingle/AppleDouble format.\n", $afile);
142 $netatalkfile = $path.".AppleDouble/".$basename;
143 $osxfile = $path."._".$basename;
145 if (( -e $netatalkfile ) && !( -e $osxfile )) {
146 $afile = $netatalkfile;
147 } elsif (!( -e $netatalkfile ) && ( -e $osxfile )) {
149 } elsif (( -e $netatalkfile ) && ( -e $osxfile )) {
150 printf ("\"%s\" found.\n", $netatalkfile);
151 printf ("\"%s\" found.\n", $osxfile);
152 printf ("Specify which of file.\n");
155 printf ("\"%s\" not found.\n", $netatalkfile);
156 printf ("\"%s\" not found.\n", $osxfile);
160 } elsif ( -d $afile) {
161 printf ("\"%s\" is a directory.\n", $afile);
163 $netatalkfile = $path.$basename."/.AppleDouble/.Parent";
164 $osxfile = $path."._".$basename;
166 if (( -e $netatalkfile ) && !( -e $osxfile )) {
167 $afile = $netatalkfile;
168 } elsif (!( -e $netatalkfile ) && ( -e $osxfile )) {
170 } elsif (( -e $netatalkfile ) && ( -e $osxfile )) {
171 printf ("\"%s\" found.\n", $netatalkfile);
172 printf ("\"%s\" found.\n", $osxfile);
173 printf ("Specify which of file.\n");
176 printf ("\"%s\" not found.\n", $netatalkfile);
177 printf ("\"%s\" not found.\n", $osxfile);
181 printf (STDERR "unknown error: %s\n", $afile);
185 if (!open(INFILE, "<$afile")) {
186 printf (STDERR "cannot open %s\n", $afile);
189 printf ("%s:\n\n", $afile);
191 # Magic Number -----------------------------------------------
193 print "-------------------------------------------------------------------------------\n";
196 $val = unpack("N", $buf );
197 printf("MagicNumber: %08X", $val);
198 if ( $val == 0x00051600 ) {
199 printf(" : AppleSingle");
201 elsif ( $val == 0x00051607 ) {
202 printf(" : AppleDouble");
205 printf(" : Unknown" );
209 # Version Number ---------------------------------------------
212 $version = unpack("N", $buf );
213 printf("Version : %08X", $version);
214 if ( $version == 0x00010000 ) {
215 printf(" : Version 1");
216 } elsif ( $version == 0x00020000 ) {
217 printf(" : Version 2");
219 printf(" : Unknown" );
223 # v1:Home file system / v2:Filler ----------------------------
225 read(INFILE,$buf,16);
226 if ( $version == 0x00010000 ) {
227 print "HomeFileSys:";
231 hexdump($buf, 16, 16, " ");
233 # Number of entities -----------------------------------------
236 $entnum = unpack("n", $buf );
237 printf("Num. of ent: %04X ", $entnum);
238 printf(" : %d", $entnum);
241 # data -------------------------------------------------------
243 for ( $num = 0 ; $num < $entnum ; $num++) {
245 seek(INFILE, ($num * 12 + 26), 0);
247 # Entry ---------------------------------------------------
250 $entid = unpack("N", $buf );
251 print "\n-------------------------------------------------------------------------------\n";
252 printf("Entry ID : %08X", $entid);
253 if ( $entid == 1 ) { printf(" : Data Fork"); }
254 elsif ( $entid == 2 ) { printf(" : Resource Fork"); }
255 elsif ( $entid == 3 ) { printf(" : Real Name"); }
256 elsif ( $entid == 4 ) { printf(" : Comment"); }
257 elsif ( $entid == 5 ) { printf(" : Icon, B&W"); }
258 elsif ( $entid == 6 ) { printf(" : Icon Color"); }
259 elsif ( $entid == 7 ) { printf(" : File Info"); }
260 elsif ( $entid == 8 ) { printf(" : File Dates Info"); }
261 elsif ( $entid == 9 ) { printf(" : Finder Info"); }
262 elsif ( $entid == 10 ) { printf(" : Macintosh File Info"); }
263 elsif ( $entid == 11 ) { printf(" : ProDOS File Info"); }
264 elsif ( $entid == 12 ) { printf(" : MS-DOS File Info"); }
265 elsif ( $entid == 13 ) { printf(" : Short Name"); }
266 elsif ( $entid == 14 ) { printf(" : AFP File Info"); }
267 elsif ( $entid == 15 ) { printf(" : Directory ID"); }
268 elsif ( $entid == 0x8053567E ) { printf(" : CNID (Netatalk Extended)"); }
269 elsif ( $entid == 0x8053594E ) { printf(" : DB stamp (Netatalk Extended)"); }
270 elsif ( $entid == 0x80444556 ) { printf(" : dev (Netatalk Extended)"); }
271 elsif ( $entid == 0x80494E4F ) { printf(" : inode (Netatalk Extended)"); }
272 else { printf(" : Unknown"); }
275 # Offset -------------------------------------------------
278 $ofst = unpack("N", $buf );
279 printf("Offset : %08X", $ofst);
280 printf(" : %d ", $ofst);
282 # Length -------------------------------------------------
285 $len = unpack("N", $buf );
286 printf("\nLength : %08X", $len);
287 printf(" : %d", $len);
292 # Dump for each Entry ID --------------------------------
294 # if ( $entid == 1 ) { ; } # Data Fork
295 # if ( $entid == 2 ) { ; } # Resource Fork
296 # if ( $entid == 3 ) { ; } # Real Name
297 # if ( $entid == 4 ) { ; } # Comment
298 # if ( $entid == 5 ) { ; } # Icon, B&W
299 # if ( $entid == 6 ) { ; } # Icon Color
300 # if ( $entid == 7 ) { ; } # File Info
301 if ( $entid == 8 ) { filedatesdump($ofst,$len); }
302 elsif ( $entid == 9 ) { finderinfodump($ofst,$len); }
303 # if ( $entid == 10 ) { ; } # Macintosh File Info
304 # if ( $entid == 11 ) { ; } # ProDOS File Info
305 # if ( $entid == 12 ) { ; } # MS-DOS File Info
306 # if ( $entid == 13 ) { ; } # Short Name
307 # if ( $entid == 14 ) { ; } # AFP File Info
308 elsif ( $entid == 15 ) { print "\n"; bedump($ofst,$len); } # Directory ID
309 elsif ( $entid == 0x8053567E ) { print "\n"; bedump($ofst,$len); } # CNID (Netatalk Extended)
310 elsif ( $entid == 0x8053594E ) { print "\n"; bedump($ofst,$len); ledump($ofst,$len); } # DB stamp (Netatalk Extended)
311 elsif ( $entid == 0x80444556 ) { print "\n"; bedump($ofst,$len); ledump($ofst,$len); } # dev (Netatalk Extended)
312 elsif ( $entid == 0x80494E4F ) { print "\n"; bedump($ofst,$len); ledump($ofst,$len); } # inode (Netatalk Extended)
314 # RAW Dump ---------------------------------------------------
316 if ( ($quo > 0) || ($rem > 0)) {
318 print "-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)\n";
321 seek(INFILE, $ofst, 0);
329 #sub -----------------------------------------------------------
332 my ($ofst, $len) = @_;
337 @datetype =('create ', 'modify ', 'backup ', 'access ');
339 seek(INFILE, $ofst, 0);
342 printf ("-DATE------: : (GMT) : (Local)\n");
344 for ( $i = 0 ; $i < 4 ; $i++) {
346 $datedata = unpack("N", $buf );
347 if ($datedata < 0x80000000) {
348 $datestr = gmtime( $datedata + 946684800)
350 .localtime( $datedata + 946684800);
351 } elsif ($datedata == 0x80000000) {
352 $datestr = "Unknown or Initial";
354 $datestr = gmtime( $datedata - 3348282496)
356 .localtime( $datedata - 3348282496);
358 printf ("%s : %08X : %s\n",$datetype[$i], $datedata, $datestr);
363 my ($ofst, $len) = @_;
365 seek(INFILE, $ofst, 0);
367 if ($finderinfo == 0) {
369 print "-NOTE------: cannot detect whether FInfo or DInfo. assume FInfo.\n";
372 if ($finderinfo == 0 || $finderinfo == 1) {
373 filefinderinfodump();
374 } elsif ($finderinfo == 2) {
377 print STDERR "unknown FinderInfo type\n"
380 if ($len > 32) { eadump(); }
383 sub filefinderinfodump {
386 print "-FInfo-----:\n";
390 hexdump($buf, 4, 4, "");
394 hexdump($buf, 4, 4, "");
399 $val = unpack("n", $buf );
400 printf("Location v : %04X", $val);
401 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
404 $val = unpack("n", $buf );
405 printf("Location h : %04X", $val);
406 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
410 hexdump($buf, 2, 4, "");
413 print "-FXInfo----:\n";
416 $val = unpack("n", $buf );
417 printf("Rsvd|IconID: %04X", $val);
418 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
422 hexdump($buf, 2, 4, "");
425 hexdump($buf, 2, 4, "");
428 hexdump($buf, 2, 4, "");
433 $val = unpack("n", $buf );
434 printf("Rsvd|commnt: %04X", $val);
435 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
438 $val = unpack("N", $buf );
439 printf("PutAway : %08X", $val);
440 printf(" : %d\n", $val>0x7FFFFFFF?$val-0x100000000:$val); # Why SInt32?
444 sub dirfinderinfodump {
447 print "-DInfo-----:\n";
450 $val = unpack("n", $buf );
451 printf("Rect top : %04X", $val);
452 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
455 $val = unpack("n", $buf );
456 printf("Rect left : %04X", $val);
457 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
460 $val = unpack("n", $buf );
461 printf("Rect bottom: %04X", $val);
462 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
465 $val = unpack("n", $buf );
466 printf("Rect right : %04X", $val);
467 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
472 $val = unpack("n", $buf );
473 printf("Location v : %04X", $val);
474 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
477 $val = unpack("n", $buf );
478 printf("Location h : %04X", $val);
479 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
483 hexdump($buf, 2, 4, "");
486 print "-DXInfo----:\n";
489 $val = unpack("n", $buf );
490 printf("Scroll v : %04X", $val);
491 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
494 $val = unpack("n", $buf );
495 printf("Scroll h : %04X", $val);
496 printf(" : %d\n", $val>0x7FFF?$val-0x10000:$val);
499 $val = unpack("N", $buf );
500 printf("Rsvd|OpnChn: %08X", $val);
501 printf(" : %d\n", $val>0x7FFFFFFF?$val-0x100000000:$val); # Why SInt32?
507 hexdump($buf, 2, 4, "");
510 $val = unpack("N", $buf );
511 printf("PutAway : %08X", $val);
512 printf(" : %d\n", $val>0x7FFFFFFF?$val-0x100000000:$val); # Why SInt32?
517 @colortype =('none', 'gray', 'green', 'purple', 'blue', 'yellow', 'red', 'orange');
520 $flags = unpack("n", $buf );
521 printf ("isAlias : %d\n", ($flags >> 15) & 1);
522 printf ("Invisible : %d\n", ($flags >> 14) & 1);
523 printf ("hasBundle : %d\n", ($flags >> 13) & 1);
524 printf ("nameLocked : %d\n", ($flags >> 12) & 1);
525 printf ("Stationery : %d\n", ($flags >> 11) & 1);
526 printf ("CustomIcon : %d\n", ($flags >> 10) & 1);
527 printf ("Reserved : %d\n", ($flags >> 9) & 1);
528 printf ("Inited : %d\n", ($flags >> 8) & 1);
529 printf ("NoINITS : %d\n", ($flags >> 7) & 1);
530 printf ("Shared : %d\n", ($flags >> 6) & 1);
531 printf ("SwitchLaunc: %d\n", ($flags >> 5) & 1);
532 printf ("Hidden Ext : %d\n", ($flags >> 4) & 1);
533 printf ("color : %d%d%d : %s\n", ($flags >> 3) & 1,
536 @colortype[($flags & 0xE)>>1]);
537 printf ("isOnDesk : %d\n", ($flags >> 0) & 1);
544 $flags = unpack("n", $buf );
546 if (($flags >> 15) == 1) {
548 hexdump($buf, 1, 4, "");
550 printf ("AreInvalid : %d\n", ($flags >> 15) & 1);
551 printf ("unknown bit: %d\n", ($flags >> 14) & 1);
552 printf ("unknown bit: %d\n", ($flags >> 13) & 1);
553 printf ("unknown bit: %d\n", ($flags >> 12) & 1);
554 printf ("unknown bit: %d\n", ($flags >> 11) & 1);
555 printf ("unknown bit: %d\n", ($flags >> 10) & 1);
556 printf ("unknown bit: %d\n", ($flags >> 9) & 1);
559 printf ("CustomBadge: %d\n", ($flags >> 8) & 1);
560 printf ("ObjctIsBusy: %d\n", ($flags >> 7) & 1);
561 printf ("unknown bit: %d\n", ($flags >> 6) & 1);
562 printf ("unknown bit: %d\n", ($flags >> 5) & 1);
563 printf ("unknown bit: %d\n", ($flags >> 4) & 1);
564 printf ("unknown bit: %d\n", ($flags >> 3) & 1);
565 printf ("RoutingInfo: %d\n", ($flags >> 2) & 1);
566 printf ("unknown bit: %d\n", ($flags >> 1) & 1);
567 printf ("unknown bit: %d\n", ($flags >> 0) & 1);
574 print "-EA--------:\n";
578 hexdump($buf, 2, 4, "");
582 hexdump($buf, 4, 4, "");
585 $ea_debug_tag = unpack("N", $buf );
586 printf("debug_tag : %08X", $ea_debug_tag);
587 printf(" : %d\n", $ea_debug_tag);
590 $ea_total_size = unpack("N", $buf );
591 printf("total_size : %08X", $ea_total_size);
592 printf(" : %d\n", $ea_total_size);
595 $ea_data_start = unpack("N", $buf );
596 printf("data_start : %08X", $ea_data_start);
597 printf(" : %d\n", $ea_data_start);
600 $ea_data_length = unpack("N", $buf );
601 printf("data_length: %08X", $ea_data_length);
602 printf(" : %d\n", $ea_data_length);
605 print "reserved[0]: ";
606 hexdump($buf, 4, 4, "");
609 print "reserved[1]: ";
610 hexdump($buf, 4, 4, "");
613 print "reserved[2]: ";
614 hexdump($buf, 4, 4, "");
618 hexdump($buf, 2, 4, "");
621 $ea_num_attrs = unpack("n", $buf );
622 printf("num_attrs : %04X", $ea_num_attrs);
623 printf(" : %d\n", $ea_num_attrs);
627 for ($i = 0 ; $i < $ea_num_attrs ; $i++) {
629 $pos = (($pos & 0x3) == 0) ? ($pos) : ((($pos >> 2) + 1) << 2);
630 seek(INFILE, $pos, 0);
632 print "-EA ENTRY--:\n";
635 $ea_offset = unpack("N", $buf );
636 printf("offset : %08X", $ea_offset);
637 printf(" : %d\n", $ea_offset);
640 $ea_length = unpack("N", $buf );
641 printf("length : %08X", $ea_length);
642 printf(" : %d\n", $ea_length);
646 hexdump($buf, 2, 4, "");
649 $ea_namelen = unpack("C", $buf );
650 printf("namelen : %02X", $ea_namelen);
651 printf(" : %d\n", $ea_namelen);
653 $ea_namequo = $ea_namelen >> 4;
654 $ea_namerem = $ea_namelen & 0xF;
655 print "-EA NAME---: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)\n";
656 rawdump($ea_namequo, $ea_namerem);
660 seek(INFILE, $ea_offset, 0);
661 $ea_quo = $ea_length >> 4;
662 $ea_rem = $ea_length & 0xF;
663 print "-EA VALUE--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)\n";
664 rawdump($ea_quo, $ea_rem);
669 my ($ofst, $len) = @_;
673 seek(INFILE, $ofst, 0);
675 printf("%2dbit-BE : ", $len * 8 );
678 for ( $i=0 ; $i < $len ; $i++ ) {
680 $bytedata[$i] = unpack("C", $buf );
681 $value += $bytedata[$i] << (($len - $i -1) * 8) ;
684 for ( $i=0 ; $i < $len ; $i++ ) {
685 printf("%02X", $bytedata[$i]);
688 printf(" : %s", $value);
694 my ($ofst, $len) = @_;
698 seek(INFILE, $ofst, 0);
700 printf("%2dbit-LE : ", $len * 8 );
703 for ( $i=0 ; $i < $len ; $i++ ) {
705 $bytedata[$len - $i - 1] = unpack("C", $buf );
706 $value += $bytedata[$len - $i -1] << ($i * 8) ;
709 for ( $i=0 ; $i < $len ; $i++ ) {
710 printf("%02X", $bytedata[$i]);
713 printf(" : %s", $value);
719 my ($quo, $rem) = @_;
720 my ($addrs, $line, $buf);
723 for ( $line = 0 ; $line < $quo ; $line++) {
724 read(INFILE, $buf, 16);
725 printf ( "%08X :", $addrs);
726 hexdump($buf, 16, 16, " ");
727 $addrs = $addrs + 0x10;
730 read(INFILE, $buf, $rem);
731 printf ( "%08X :", $addrs);
732 hexdump($buf, $rem, 16, " ");
738 my ($buf, $len, $col, $delimit) = @_;
744 for ( $i=0 ; $i < $len ; $i++ ) {
745 $val = substr($buf, $i, 1);
747 $hexstr .= sprintf("%s%02X", $delimit, $ascval);
749 if (($ascval < 32) || ( $ascval > 126 )) {
754 for ( ; $i < $col ; $i++) {
755 $hexstr .= " ".$delimit;
759 printf("%s : %s", $hexstr,$ascstr);