+
+
+//
+// NOTE: this function takes care of starting a transaction and
+// acquiring the systemfile lock so that it can call
+// cat_update().
+//
+// NOTE: do NOT hold and cnode locks while calling this function
+// to avoid deadlocks (because we take a lock on the root
+// cnode)
+//
+int
+hfs_generate_document_id(struct hfsmount *hfsmp, uint32_t *docid)
+{
+ struct vnode *rvp;
+ struct cnode *cp;
+ int error;
+
+ error = VFS_ROOT(HFSTOVFS(hfsmp), &rvp, vfs_context_kernel());
+ if (error) {
+ return error;
+ }
+
+ cp = VTOC(rvp);
+ if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)) != 0) {
+ return error;
+ }
+ struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((void *)((char *)&cp->c_attr.ca_finderinfo + 16));
+
+ int lockflags;
+ if ((error = hfs_start_transaction(hfsmp)) != 0) {
+ return error;
+ }
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+
+ if (extinfo->document_id == 0) {
+ // initialize this to start at 3 (one greater than the root-dir id)
+ extinfo->document_id = 3;
+ }
+
+ *docid = extinfo->document_id++;
+
+ // mark the root cnode dirty
+ cp->c_flag |= C_MODIFIED;
+ hfs_update(cp->c_vp, 0);
+
+ hfs_systemfile_unlock (hfsmp, lockflags);
+ (void) hfs_end_transaction(hfsmp);
+
+ (void) hfs_unlock(cp);
+
+ vnode_put(rvp);
+ rvp = NULL;
+
+ return 0;
+}
+
+
+/*
+ * Return information about number of file system allocation blocks
+ * taken by metadata on a volume.
+ *
+ * This function populates struct hfsinfo_metadata with allocation blocks
+ * used by extents overflow btree, catalog btree, bitmap, attribute btree,
+ * journal file, and sum of all of the above.
+ */
+int
+hfs_getinfo_metadata_blocks(struct hfsmount *hfsmp, struct hfsinfo_metadata *hinfo)
+{
+ int lockflags = 0;
+ int ret_lockflags = 0;
+
+ /* Zero out the output buffer */
+ bzero(hinfo, sizeof(struct hfsinfo_metadata));
+
+ /*
+ * Getting number of allocation blocks for all btrees
+ * should be a quick operation, so we grab locks for
+ * all of them at the same time
+ */
+ lockflags = SFL_CATALOG | SFL_EXTENTS | SFL_BITMAP | SFL_ATTRIBUTE;
+ ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
+ /*
+ * Make sure that we were able to acquire all locks requested
+ * to protect us against conditions like unmount in progress.
+ */
+ if ((lockflags & ret_lockflags) != lockflags) {
+ /* Release any locks that were acquired */
+ hfs_systemfile_unlock(hfsmp, ret_lockflags);
+ return EPERM;
+ }
+
+ /* Get information about all the btrees */
+ hinfo->extents = hfsmp->hfs_extents_cp->c_datafork->ff_blocks;
+ hinfo->catalog = hfsmp->hfs_catalog_cp->c_datafork->ff_blocks;
+ hinfo->allocation = hfsmp->hfs_allocation_cp->c_datafork->ff_blocks;
+ hinfo->attribute = hfsmp->hfs_attribute_cp->c_datafork->ff_blocks;
+
+ /* Done with btrees, give up the locks */
+ hfs_systemfile_unlock(hfsmp, ret_lockflags);
+
+ /* Get information about journal file */
+ hinfo->journal = howmany(hfsmp->jnl_size, hfsmp->blockSize);
+
+ /* Calculate total number of metadata blocks */
+ hinfo->total = hinfo->extents + hinfo->catalog +
+ hinfo->allocation + hinfo->attribute +
+ hinfo->journal;
+
+ return 0;
+}
+
+static int
+hfs_freezewrite_callback(struct vnode *vp, __unused void *cargs)
+{
+ vnode_waitforwrites(vp, 0, 0, 0, "hfs freeze 8");
+
+ return 0;
+}
+
+__private_extern__
+int hfs_freeze(struct hfsmount *hfsmp)
+{
+ // First make sure some other process isn't freezing
+ hfs_lock_mount(hfsmp);
+ while (hfsmp->hfs_freeze_state != HFS_THAWED) {
+ if (msleep(&hfsmp->hfs_freeze_state, &hfsmp->hfs_mutex,
+ PWAIT | PCATCH, "hfs freeze 1", NULL) == EINTR) {
+ hfs_unlock_mount(hfsmp);
+ return EINTR;
+ }
+ }
+
+ // Stop new syncers from starting
+ hfsmp->hfs_freeze_state = HFS_WANT_TO_FREEZE;
+
+ // Now wait for all syncers to finish
+ while (hfsmp->hfs_syncers) {
+ if (msleep(&hfsmp->hfs_freeze_state, &hfsmp->hfs_mutex,
+ PWAIT | PCATCH, "hfs freeze 2", NULL) == EINTR) {
+ hfs_thaw_locked(hfsmp);
+ hfs_unlock_mount(hfsmp);
+ return EINTR;
+ }
+ }
+ hfs_unlock_mount(hfsmp);
+
+ // flush things before we get started to try and prevent
+ // dirty data from being paged out while we're frozen.
+ // note: we can't do this once we're in the freezing state because
+ // other threads will need to take the global lock
+ vnode_iterate(hfsmp->hfs_mp, 0, hfs_freezewrite_callback, NULL);
+
+ // Block everything in hfs_lock_global now
+ hfs_lock_mount(hfsmp);
+ hfsmp->hfs_freeze_state = HFS_FREEZING;
+ hfsmp->hfs_freezing_thread = current_thread();
+ hfs_unlock_mount(hfsmp);
+
+ /* Take the exclusive lock to flush out anything else that
+ might have the global lock at the moment and also so we
+ can flush the journal. */
+ hfs_lock_global(hfsmp, HFS_EXCLUSIVE_LOCK);
+ journal_flush(hfsmp->jnl, JOURNAL_WAIT_FOR_IO);
+ hfs_unlock_global(hfsmp);
+
+ // don't need to iterate on all vnodes, we just need to
+ // wait for writes to the system files and the device vnode
+ //
+ // Now that journal flush waits for all metadata blocks to
+ // be written out, waiting for btree writes is probably no
+ // longer required.
+ if (HFSTOVCB(hfsmp)->extentsRefNum)
+ vnode_waitforwrites(HFSTOVCB(hfsmp)->extentsRefNum, 0, 0, 0, "hfs freeze 3");
+ if (HFSTOVCB(hfsmp)->catalogRefNum)
+ vnode_waitforwrites(HFSTOVCB(hfsmp)->catalogRefNum, 0, 0, 0, "hfs freeze 4");
+ if (HFSTOVCB(hfsmp)->allocationsRefNum)
+ vnode_waitforwrites(HFSTOVCB(hfsmp)->allocationsRefNum, 0, 0, 0, "hfs freeze 5");
+ if (hfsmp->hfs_attribute_vp)
+ vnode_waitforwrites(hfsmp->hfs_attribute_vp, 0, 0, 0, "hfs freeze 6");
+ vnode_waitforwrites(hfsmp->hfs_devvp, 0, 0, 0, "hfs freeze 7");
+
+ // We're done, mark frozen
+ hfs_lock_mount(hfsmp);
+ hfsmp->hfs_freeze_state = HFS_FROZEN;
+ hfsmp->hfs_freezing_proc = current_proc();
+ hfs_unlock_mount(hfsmp);
+
+ return 0;
+}
+
+__private_extern__
+int hfs_thaw(struct hfsmount *hfsmp, const struct proc *process)
+{
+ hfs_lock_mount(hfsmp);
+
+ if (hfsmp->hfs_freeze_state != HFS_FROZEN) {
+ hfs_unlock_mount(hfsmp);
+ return EINVAL;
+ }
+ if (process && hfsmp->hfs_freezing_proc != process) {
+ hfs_unlock_mount(hfsmp);
+ return EPERM;
+ }
+
+ hfs_thaw_locked(hfsmp);
+
+ hfs_unlock_mount(hfsmp);
+
+ return 0;
+}
+
+static void hfs_thaw_locked(struct hfsmount *hfsmp)
+{
+ hfsmp->hfs_freezing_proc = NULL;
+ hfsmp->hfs_freeze_state = HFS_THAWED;
+
+ wakeup(&hfsmp->hfs_freeze_state);
+}