+
+ return 1; // keep iterating
+}
+
+extern dev_t IOBSDGetMediaWithUUID(const char *uuid_cstring, char *bsd_name, int bsd_name_len, int timeout);
+extern void IOBSDIterateMediaWithContent(const char *uuid_cstring, int (*func)(const char *bsd_dev_name, const char *uuid_str, void *arg), void *arg);
+extern kern_return_t IOBSDGetPlatformUUID(__darwin_uuid_t uuid, mach_timespec_t timeoutp);
+kern_return_t IOBSDGetPlatformSerialNumber(char *serial_number_str, u_int32_t len);
+
+
+static vnode_t
+open_journal_dev(const char *vol_device,
+ int need_clean,
+ char *uuid_str,
+ char *machine_serial_num,
+ off_t jsize,
+ size_t blksize,
+ int *need_init)
+{
+ int retry_counter=0;
+ jopen_cb_info ji;
+
+ ji.jsize = jsize;
+ ji.desired_uuid = uuid_str;
+ ji.jvp = NULL;
+ ji.blksize = blksize;
+ ji.need_clean = need_clean;
+ ji.need_init = 0;
+
+// if (uuid_str[0] == '\0') {
+// printf("hfs: open journal dev: %s: locating any available non-dirty external journal partition\n", vol_device);
+// } else {
+// printf("hfs: open journal dev: %s: trying to find the external journal partition w/uuid %s\n", vol_device, uuid_str);
+// }
+ while (ji.jvp == NULL && retry_counter++ < 4) {
+ if (retry_counter > 1) {
+ if (uuid_str[0]) {
+ printf("hfs: open_journal_dev: uuid %s not found. waiting 10sec.\n", uuid_str);
+ } else {
+ printf("hfs: open_journal_dev: no available external journal partition found. waiting 10sec.\n");
+ }
+ delay_for_interval(10* 1000000, NSEC_PER_USEC); // wait for ten seconds and then try again
+ }
+
+ IOBSDIterateMediaWithContent(EXTJNL_CONTENT_TYPE_UUID, journal_open_cb, &ji);
+ }
+
+ if (ji.jvp == NULL) {
+ printf("hfs: volume: %s: did not find jnl device uuid: %s from machine serial number: %s\n",
+ vol_device, uuid_str, machine_serial_num);
+ }
+
+ *need_init = ji.need_init;
+
+ return ji.jvp;
+}
+
+
+__private_extern__
+int
+hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
+ void *_args, off_t embeddedOffset, daddr64_t mdb_offset,
+ HFSMasterDirectoryBlock *mdbp, kauth_cred_t cred)
+{
+ JournalInfoBlock *jibp;
+ struct buf *jinfo_bp, *bp;
+ int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+ int retval, write_jibp = 0;
+ uint32_t blksize = hfsmp->hfs_logical_block_size;
+ struct vnode *devvp;
+ struct hfs_mount_args *args = _args;
+ u_int32_t jib_flags;
+ u_int64_t jib_offset;
+ u_int64_t jib_size;
+ const char *dev_name;
+
+ devvp = hfsmp->hfs_devvp;
+ dev_name = vnode_name(devvp);
+ if (dev_name == NULL) {
+ dev_name = "unknown-dev";
+ }
+
+ if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+ arg_flags = args->journal_flags;
+ arg_tbufsz = args->journal_tbuffer_size;
+ }
+
+ sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
+
+ jinfo_bp = NULL;
+ retval = (int)buf_meta_bread(devvp,
+ (daddr64_t)((embeddedOffset/blksize) +
+ ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
+ hfsmp->hfs_physical_block_size, cred, &jinfo_bp);
+ if (retval) {
+ if (jinfo_bp) {
+ buf_brelse(jinfo_bp);
+ }
+ return retval;
+ }
+
+ jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
+ jib_flags = SWAP_BE32(jibp->flags);
+ jib_size = SWAP_BE64(jibp->size);
+
+ if (jib_flags & kJIJournalInFSMask) {
+ hfsmp->jvp = hfsmp->hfs_devvp;
+ jib_offset = SWAP_BE64(jibp->offset);
+ } else {
+ int need_init=0;
+
+ // if the volume was unmounted cleanly then we'll pick any
+ // available external journal partition
+ //
+ if (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) {
+ *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
+ }
+
+ hfsmp->jvp = open_journal_dev(dev_name,
+ !(jib_flags & kJIJournalNeedInitMask),
+ (char *)&jibp->ext_jnl_uuid[0],
+ (char *)&jibp->machine_serial_num[0],
+ jib_size,
+ hfsmp->hfs_logical_block_size,
+ &need_init);
+ if (hfsmp->jvp == NULL) {
+ buf_brelse(jinfo_bp);
+ return EROFS;
+ } else {
+ if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
+ strlcpy(&jibp->machine_serial_num[0], "unknown-machine-uuid", sizeof(jibp->machine_serial_num));
+ }
+ }
+
+ jib_offset = 0;
+ write_jibp = 1;
+ if (need_init) {
+ jib_flags |= kJIJournalNeedInitMask;
+ }
+ }
+
+ // save this off for the hack-y check in hfs_remove()
+ hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
+ hfsmp->jnl_size = jib_size;
+
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+ // if the file system is read-only, check if the journal is empty.
+ // if it is, then we can allow the mount. otherwise we have to
+ // return failure.
+ retval = journal_is_clean(hfsmp->jvp,
+ jib_offset + embeddedOffset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size);
+
+ hfsmp->jnl = NULL;
+
+ buf_brelse(jinfo_bp);
+
+ if (retval) {
+ const char *name = vnode_getname(devvp);
+ printf("hfs: early journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
+ name ? name : "");
+ if (name)
+ vnode_putname(name);
+ }
+
+ return retval;
+ }
+
+ if (jib_flags & kJIJournalNeedInitMask) {
+ printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+ jib_offset + embeddedOffset, jib_size);
+ hfsmp->jnl = journal_create(hfsmp->jvp,
+ jib_offset + embeddedOffset,
+ jib_size,
+ devvp,
+ blksize,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+
+ // no need to start a transaction here... if this were to fail
+ // we'd just re-init it on the next mount.
+ jib_flags &= ~kJIJournalNeedInitMask;
+ jibp->flags = SWAP_BE32(jib_flags);
+ buf_bwrite(jinfo_bp);
+ jinfo_bp = NULL;
+ jibp = NULL;
+ } else {
+ //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+ // jib_offset + embeddedOffset,
+ // jib_size, SWAP_BE32(vhp->blockSize));
+
+ hfsmp->jnl = journal_open(hfsmp->jvp,
+ jib_offset + embeddedOffset,
+ jib_size,
+ devvp,
+ blksize,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+
+ if (write_jibp) {
+ buf_bwrite(jinfo_bp);
+ } else {
+ buf_brelse(jinfo_bp);
+ }
+ jinfo_bp = NULL;
+ jibp = NULL;
+
+ if (hfsmp->jnl && mdbp) {
+ // reload the mdb because it could have changed
+ // if the journal had to be replayed.
+ if (mdb_offset == 0) {
+ mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
+ }
+ bp = NULL;
+ retval = (int)buf_meta_bread(devvp,
+ HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
+ hfsmp->hfs_physical_block_size, cred, &bp);
+ if (retval) {
+ if (bp) {
+ buf_brelse(bp);
+ }
+ printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
+ retval);
+ return retval;
+ }
+ bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size), mdbp, 512);
+ buf_brelse(bp);
+ bp = NULL;
+ }
+ }
+
+
+ //printf("journal @ 0x%x\n", hfsmp->jnl);
+
+ // if we expected the journal to be there and we couldn't
+ // create it or open it then we have to bail out.
+ if (hfsmp->jnl == NULL) {
+ printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+
+//
+// This function will go and re-locate the .journal_info_block and
+// the .journal files in case they moved (which can happen if you
+// run Norton SpeedDisk). If we fail to find either file we just
+// disable journaling for this volume and return. We turn off the
+// journaling bit in the vcb and assume it will get written to disk
+// later (if it doesn't on the next mount we'd do the same thing
+// again which is harmless). If we disable journaling we don't
+// return an error so that the volume is still mountable.
+//
+// If the info we find for the .journal_info_block and .journal files
+// isn't what we had stored, we re-set our cached info and proceed
+// with opening the journal normally.
+//
+static int
+hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
+{
+ JournalInfoBlock *jibp;
+ struct buf *jinfo_bp;
+ int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+ int retval, write_jibp = 0, recreate_journal = 0;
+ struct vnode *devvp;
+ struct cat_attr jib_attr, jattr;
+ struct cat_fork jib_fork, jfork;
+ ExtendedVCB *vcb;
+ u_int32_t fid;
+ struct hfs_mount_args *args = _args;
+ u_int32_t jib_flags;
+ u_int64_t jib_offset;
+ u_int64_t jib_size;
+
+ devvp = hfsmp->hfs_devvp;
+ vcb = HFSTOVCB(hfsmp);
+
+ if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+ if (args->journal_disable) {
+ return 0;
+ }
+
+ arg_flags = args->journal_flags;
+ arg_tbufsz = args->journal_tbuffer_size;
+ }
+
+ fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
+ if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
+ printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
+ jib_fork.cf_extents[0].startBlock);
+ vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+ return 0;
+ }
+ hfsmp->hfs_jnlinfoblkid = fid;
+
+ // make sure the journal_info_block begins where we think it should.
+ if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
+ printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n",
+ SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
+
+ vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock;
+ vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
+ recreate_journal = 1;
+ }
+
+
+ sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_logical_block_size;
+ jinfo_bp = NULL;
+ retval = (int)buf_meta_bread(devvp,
+ (vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size +
+ ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
+ hfsmp->hfs_physical_block_size, NOCRED, &jinfo_bp);
+ if (retval) {
+ if (jinfo_bp) {
+ buf_brelse(jinfo_bp);
+ }
+ printf("hfs: can't read journal info block. disabling journaling.\n");
+ vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+ return 0;
+ }
+
+ jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
+ jib_flags = SWAP_BE32(jibp->flags);
+ jib_offset = SWAP_BE64(jibp->offset);
+ jib_size = SWAP_BE64(jibp->size);
+
+ fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
+ if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
+ printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
+ jfork.cf_extents[0].startBlock);
+ buf_brelse(jinfo_bp);
+ vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+ return 0;
+ }
+ hfsmp->hfs_jnlfileid = fid;
+
+ // make sure the journal file begins where we think it should.
+ if ((jib_flags & kJIJournalInFSMask) && (jib_offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
+ printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n",
+ (jib_offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
+
+ jib_offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
+ write_jibp = 1;
+ recreate_journal = 1;
+ }
+
+ // check the size of the journal file.
+ if (jib_size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
+ printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n",
+ jib_size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
+
+ jib_size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
+ write_jibp = 1;
+ recreate_journal = 1;
+ }
+
+ if (jib_flags & kJIJournalInFSMask) {
+ hfsmp->jvp = hfsmp->hfs_devvp;
+ jib_offset += (off_t)vcb->hfsPlusIOPosOffset;
+ } else {
+ const char *dev_name;
+ int need_init = 0;
+
+ dev_name = vnode_name(devvp);
+ if (dev_name == NULL) {
+ dev_name = "unknown-dev";
+ }
+
+ // since the journal is empty, just use any available external journal
+ *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
+
+ // this fills in the uuid of the device we actually get
+ hfsmp->jvp = open_journal_dev(dev_name,
+ !(jib_flags & kJIJournalNeedInitMask),
+ (char *)&jibp->ext_jnl_uuid[0],
+ (char *)&jibp->machine_serial_num[0],
+ jib_size,
+ hfsmp->hfs_logical_block_size,
+ &need_init);
+ if (hfsmp->jvp == NULL) {
+ buf_brelse(jinfo_bp);
+ return EROFS;
+ } else {
+ if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
+ strlcpy(&jibp->machine_serial_num[0], "unknown-machine-serial-num", sizeof(jibp->machine_serial_num));
+ }
+ }
+ jib_offset = 0;
+ recreate_journal = 1;
+ write_jibp = 1;
+ if (need_init) {
+ jib_flags |= kJIJournalNeedInitMask;
+ }
+ }
+
+ // save this off for the hack-y check in hfs_remove()
+ hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
+ hfsmp->jnl_size = jib_size;
+
+ if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+ // if the file system is read-only, check if the journal is empty.
+ // if it is, then we can allow the mount. otherwise we have to
+ // return failure.
+ retval = journal_is_clean(hfsmp->jvp,
+ jib_offset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size);
+
+ hfsmp->jnl = NULL;
+
+ buf_brelse(jinfo_bp);
+
+ if (retval) {
+ const char *name = vnode_getname(devvp);
+ printf("hfs: late journal init: volume on %s is read-only and journal is dirty. Can not mount volume.\n",
+ name ? name : "");
+ if (name)
+ vnode_putname(name);
+ }
+
+ return retval;
+ }
+
+ if ((jib_flags & kJIJournalNeedInitMask) || recreate_journal) {
+ printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+ jib_offset, jib_size);
+ hfsmp->jnl = journal_create(hfsmp->jvp,
+ jib_offset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+
+ // no need to start a transaction here... if this were to fail
+ // we'd just re-init it on the next mount.
+ jib_flags &= ~kJIJournalNeedInitMask;
+ write_jibp = 1;
+
+ } else {
+ //
+ // if we weren't the last person to mount this volume
+ // then we need to throw away the journal because it
+ // is likely that someone else mucked with the disk.
+ // if the journal is empty this is no big deal. if the
+ // disk is dirty this prevents us from replaying the
+ // journal over top of changes that someone else made.
+ //
+ arg_flags |= JOURNAL_RESET;
+
+ //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+ // jib_offset,
+ // jib_size, SWAP_BE32(vhp->blockSize));
+
+ hfsmp->jnl = journal_open(hfsmp->jvp,
+ jib_offset,
+ jib_size,
+ devvp,
+ hfsmp->hfs_logical_block_size,
+ arg_flags,
+ arg_tbufsz,
+ hfs_sync_metadata, hfsmp->hfs_mp);
+ }
+
+
+ if (write_jibp) {
+ jibp->flags = SWAP_BE32(jib_flags);
+ jibp->offset = SWAP_BE64(jib_offset);
+ jibp->size = SWAP_BE64(jib_size);
+
+ buf_bwrite(jinfo_bp);
+ } else {
+ buf_brelse(jinfo_bp);
+ }
+ jinfo_bp = NULL;
+ jibp = NULL;
+
+ //printf("hfs: journal @ 0x%x\n", hfsmp->jnl);
+
+ // if we expected the journal to be there and we couldn't
+ // create it or open it then we have to bail out.
+ if (hfsmp->jnl == NULL) {
+ printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
+ return EINVAL;
+ }
+
+ return 0;