$ fsdb -r ./baddisk.img > cd a > ls slot 2 off 24 ino 4 reclen 488: regular, `c.pdf' > inode 4 current inode: regular file I=4 MODE=100770 SIZE=89518 BTIME=Apr 22 04:55:41 2020 [0 nsec] MTIME=Apr 22 04:55:41 2020 [0 nsec] CTIME=Apr 22 10:11:52 2020 [904270000 nsec] ATIME=Apr 22 10:16:09 2020 [826906000 nsec] OWNER=root GRP=stress2user LINKCNT=1 FLAGS=0 BLKCNT=b8 GEN=1c8ba184 & $ bg $ gdb $(which fsdb) $(pgrep fsdb) ... (gdb) p curinode.dp2.di_extb $1 = {89, 0} (gdb) p curinode.dp2.di_extsize $2 = 192 $ dumpfs ./baddisk.img ... cg 0: magic 90255 tell 20000 time Wed Apr 22 10:11:52 2020 cgx 0 ndblk 1032 niblk 640 initiblk 256 unrefs 0 nbfree 114 ndir 2 nifree 635 nffree 14 rotor 0 irotor 4 frotor 88 frsum 1 0 0 0 0 1 1 sum of frsum: 14 clusters 1-3: 0 1 0 clusters size 4 and over: 1 clusters free: 13-14, 17-128 inodes used: 0-4 blks free: 87, 90-95, 97-119, 136-1031 ... ^ 89 allocated 0x59 = 89 (di_extb[0]), 0x1000 = 4kB $ hd < baddisk.img | less ... * 00059000 30 00 00 00 01 00 29 44 6f 73 53 74 72 65 61 6d |0.....)DosStream| 00059010 2e 63 6f 6d 2e 61 70 70 6c 65 2e 6c 61 73 74 75 |.com.apple.lastu| 00059020 73 65 64 64 61 74 65 23 50 53 3a 24 44 41 54 41 |seddate#PS:$DATA| 00059030 48 00 00 00 01 00 09 44 4f 53 41 54 54 52 49 42 |H......DOSATTRIB| 00059040 30 78 32 30 00 00 03 00 03 00 00 00 11 00 00 00 |0x20............| 00059050 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............| 00059060 00 00 00 00 00 00 00 00 80 9c 1d f2 9c 18 d6 01 |................| 00059070 00 00 00 00 00 00 00 00 00 00 00 00 01 04 09 44 |...............D| 00059080 4f 53 41 54 54 52 49 42 30 78 30 00 03 00 03 00 |OSATTRIB0x0.....| 00059090 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000590a0 00 00 00 00 00 00 00 00 00 00 00 00 ca f8 8f f2 |................| 000590b0 9c 18 d6 01 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000590c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00060000 03 00 00 00 0c 00 04 01 2e 00 00 00 02 00 00 00 |................| // i_ea_area, struct extattr header /* * This structure defines the required fields of an extended-attribute header. */ struct extattr { uint32_t ea_length; /* length of this attribute */ uint8_t ea_namespace; /* name space of this attribute */ uint8_t ea_contentpadlen; /* bytes of padding at end of attribute */ uint8_t ea_namelength; /* length of attribute name */ char ea_name[1]; /* attribute name (NOT nul-terminated) */ /* padding, if any, to align attribute content to 8 byte boundary */ /* extended attribute content follows */ }; /* * These macros are used to access and manipulate an extended attribute: * * EXTATTR_NEXT(eap) returns a pointer to the next extended attribute * following eap. * EXTATTR_CONTENT(eap) returns a pointer to the extended attribute * content referenced by eap. * EXTATTR_CONTENT_SIZE(eap) returns the size of the extended attribute * content referenced by eap. */ #define EXTATTR_NEXT(eap) \ ((struct extattr *)(((u_char *)(eap)) + (eap)->ea_length)) #define EXTATTR_CONTENT(eap) \ (void *)(((u_char *)(eap)) + EXTATTR_BASE_LENGTH(eap)) #define EXTATTR_CONTENT_SIZE(eap) \ ((eap)->ea_length - EXTATTR_BASE_LENGTH(eap) - (eap)->ea_contentpadlen) /* -1 below compensates for ea_name[1] */ #define EXTATTR_BASE_LENGTH(eap) \ roundup2((sizeof(struct extattr) - 1 + (eap)->ea_namelength), 8) So: di_extsize == 192 == 0xc0 1st extattr: 00059000 30 00 00 00 01 00 29 44 6f 73 53 74 72 65 61 6d |0.....)DosStream| |ea_length| ns cp nl | ea_name ... 00059010 2e 63 6f 6d 2e 61 70 70 6c 65 2e 6c 61 73 74 75 |.com.apple.lastu| 00059020 73 65 64 64 61 74 65 23 50 53 3a 24 44 41 54 41 |seddate#PS:$DATA| ... ea_name | So it's empty and has the name "DosStream.com.apple.lastuseddate#PS:$DATA". So far so good. 2nd extattr: 00059030 48 00 00 00 01 00 09 44 4f 53 41 54 54 52 49 42 |H......DOSATTRIB| |ea_length| ns cp nl | ea_name ... | 00059040 30 78 32 30 00 00 03 00 03 00 00 00 11 00 00 00 |0x20............| |^EXTATTR_CONTENT ... 00059050 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............| 00059060 00 00 00 00 00 00 00 00 80 9c 1d f2 9c 18 d6 01 |................| 00059070 00 00 00 00 00 00 00 00 |........| ... EXTATTR_CONTENT | It's named "DOSATTRIB" and has 0x28 bytes of content, including the prefix ASCII string "0x20". Seems fine. 3rd extattr: 00059070 00 00 00 00 01 04 09 44 |.......D| |ea_length| ns cp nl | ea_name ... 00059080 4f 53 41 54 54 52 49 42 30 78 30 00 03 00 03 00 |OSATTRIB0x0.....| ... ea_name | | ... 00059090 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000590a0 00 00 00 00 00 00 00 00 00 00 00 00 ca f8 8f f2 |................| 000590b0 9c 18 d6 01 00 00 00 00 00 00 00 00 00 00 00 00 |................| ... ??? | The 3rd extattr has a bogus ea_length of zero, which probably causes the infinite loop during listextattr or findextattr type operations. These loops are generally of the form: for (p = 0; p < end; p += p->ea_length) Obviously that doesn't work well if ea_length is zero. So: we have a workaround for a corrupt FS image: check for zero ea_length. But the root problem here seems to be the zero ea_length in the first place.