New Hacking Project
Back in March, I bought a used 12” PowerBook G4 from James. I absolutely love it, but I’ve had one annoying problem. I have both Mac OS X and Linux on it, and I have three partitions on the 60GB drive: one for Mac, one for Linux, and one for my home directory, which is shared between both OSes. Of course, I needed something that both of them could read and write to, so I chose the Mac HFS+ format. At the time I installed it, the ext2/3 driver for OS X didn’t have write support for 10.4. It does now, but it doesn’t have ext3 journaling support.
Unfortunately, the Linux HFS+ driver lacks journaling support as well, so I was forced to use a non-journaled volume, as Linux refuses to mount a journaled HFS+ volume read/write.
Now, I don’t know if it’s a deficiency in the Linux HFS+ driver, but 90% of the time after a hard lock (which unfortunately happens somewhat often, as the OSS Broadcom wireless drivers aren’t always so stable), the filesystem gets trashed, and even OS X’s Disk Utility can’t fix it; I have to bust out the big guns: Disk Warrior. And even sometimes that has trouble. So, needless to say, I don’t put anything irreplaceable on that partition.
So, I’ve decided to add journaling support to the Linux HFS+ driver. I don’t expect this to be remotely easy. The first step, which I have somewhat completed, is just to allow the driver to replay the journal if there’s data in it on mount (i.e., if a crash under OS X left the filesystem in an inconsistent state). I’ve been running into a bunch of annoyances, many of which just stem from the fact that I’ve never done any kernel hacking before. It took forever just to figure out how to write to the disk. Figuring out how to read wasn’t easy either. I thought that after I’d found the sb_bread() function, that would be it. But, of course, it’s all a matter of units. sb_bread() seems to read in blocks of 4096 bytes (which might be device-dependent; it just happens to be 4096 for me). In the HFS+ volume header, the journal location is specified in filesystem blocks (512 bytes), which took me forever to realise. Then most other offsets are in bytes. To make matters worse, some of Apple’s documentation doesn’t quite make sense.
Writing was another matter. sb_bread() returns a “buffer head” structure, which contains all the data in that block from the disk. (That’s another thing: you can’t read a certain amount of bytes from the disk, from what I can tell. Each time you read, you get a block, no more, no less.) For writing, you have to read out the correct block, copy the new data to the correct point in that block’s buffer, then call mark_buffer_dirty() on it. If you want to write it immediately (I think/hope), you then call sync_dirty_buffer() on it as well.
Anyway, I think I have most of that sorted out; I just need to figure out some things I’m doing wrong regarding interpreting the spec and where portions of the journal are, and it should be ok.
Then comes the hard part, which as far as I can tell isn’t documented anywhere: actually implement the journaling support in the driver so as you write to the disk during normal daily activities, metadata gets journaled, then data is written, and then the metadata is flushed to disk (or whatever). I really have no idea how this even works, let alone the details of implementing it. If anyone has any info on this, please feel free to drop me a note…