Why I hate HFS+

August 5, 2011

HFS+, the filesystem used on modern versions of Mac OS, is long past its prime. It’s actually a remarkable feat of engineering: the same filesystem has been extended and improved upon for over ten years despite technical limitations inherent in its original design. However, sometimes its limitations are a little too much.

I have kept a checkout of the linux 2.6 git repository on my Mac for some time now – it’s sometimes fun to go sourcediving through the kernel, even though I grok very little of it. A few months ago, however, I noticed that after a pull, git said I had unstaged changes in my working directory. This was curious since I hadn’t changed anything, but I didn’t think much of it, and after a few failed git reset --hards, I forgot about it.

Today, however, I got frustrated at git and decided to dig further into the problem. I tried everything – first I tried adding and committing the unstaged changes, but git add refused to add any of the supposedly untracked files. git reset --hard and git clean were similarly ineffective. git rm -f was the only command that I could actually get to function properly, but that put git in some odd state in which the files were both staged and not staged for deletion.

But then I looked closer at the filenames. Turns out they weren’t quite the same. The set of files that was staged for deletion were partially uppercase, while the unstaged ones were entirely lowercase:

# On branch master
# Changes to be committed:
# deleted:    include/linux/netfilter/xt_CONNMARK.h
# deleted:    include/linux/netfilter/xt_DSCP.h
# deleted:    include/linux/netfilter/xt_MARK.h
# deleted:    include/linux/netfilter/xt_RATEEST.h
# deleted:    include/linux/netfilter/xt_TCPMSS.h
# deleted:    include/linux/netfilter_ipv4/ipt_ECN.h
# deleted:    include/linux/netfilter_ipv4/ipt_TTL.h
# deleted:    include/linux/netfilter_ipv6/ip6t_HL.h
# deleted:    net/ipv4/netfilter/ipt_ECN.c
# deleted:    net/netfilter/xt_DSCP.c
# deleted:    net/netfilter/xt_HL.c
# deleted:    net/netfilter/xt_RATEEST.c
# deleted:    net/netfilter/xt_TCPMSS.c
# Changes not staged for commit:
# deleted:    include/linux/netfilter/xt_connmark.h
# deleted:    include/linux/netfilter/xt_dscp.h
# deleted:    include/linux/netfilter/xt_mark.h
# deleted:    include/linux/netfilter/xt_rateest.h
# deleted:    include/linux/netfilter/xt_tcpmss.h
# deleted:    include/linux/netfilter_ipv4/ipt_ecn.h
# deleted:    include/linux/netfilter_ipv4/ipt_ttl.h
# deleted:    include/linux/netfilter_ipv6/ip6t_hl.h
# deleted:    net/ipv4/netfilter/ipt_ecn.c
# deleted:    net/netfilter/xt_dscp.c
# deleted:    net/netfilter/xt_hl.c
# deleted:    net/netfilter/xt_rateest.c
# deleted:    net/netfilter/xt_tcpmss.c
# Untracked files:
# samples/hidraw/

Suddenly it clicked. HFS+, by default, is a case-insensitve filesystem. At some point in the last few months, the Linux repo added multiple files with different capitalizations, pretty much screwing over anyone still stuck with this miserable excuse for a filesystem.

Hey Apple. How’s that ZFS support coming along?