+
+/*
+ * Calculate the allocation zone for metadata.
+ *
+ * This zone includes the following:
+ * Allocation Bitmap file
+ * Overflow Extents file
+ * Journal file
+ * Quota files
+ * Clustered Hot files
+ * Catalog file
+ *
+ * METADATA ALLOCATION ZONE
+ * ____________________________________________________________________________
+ * | | | | | | |
+ * | BM | JF | OEF | CATALOG |---> | HOT FILES |
+ * |____|____|_____|_______________|______________________________|___________|
+ *
+ * <------------------------------- N * 128 MB ------------------------------->
+ *
+ */
+#define GIGABYTE (u_int64_t)(1024*1024*1024)
+
+#define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
+#define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
+#define JOURNAL_DEFAULT_SIZE (8*1024*1024)
+#define JOURNAL_MAXIMUM_SIZE (512*1024*1024)
+#define HOTBAND_MINIMUM_SIZE (10*1024*1024)
+#define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
+
+static void
+hfs_metadatazone_init(struct hfsmount *hfsmp)
+{
+ ExtendedVCB *vcb;
+ struct BTreeInfoRec btinfo;
+ u_int64_t fs_size;
+ u_int64_t zonesize;
+ u_int64_t temp;
+ u_int64_t filesize;
+ u_int32_t blk;
+ int items;
+
+ vcb = HFSTOVCB(hfsmp);
+ fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->totalBlocks;
+
+ /*
+ * For volumes less than 10 GB, don't bother.
+ */
+ if (fs_size < ((u_int64_t)10 * GIGABYTE))
+ return;
+ /*
+ * Skip non-journaled volumes as well.
+ */
+ if (hfsmp->jnl == NULL)
+ return;
+
+ /*
+ * Start with allocation bitmap (a fixed size).
+ */
+ zonesize = roundup(vcb->totalBlocks / 8, vcb->vcbVBMIOSize);
+
+ /*
+ * Overflow Extents file gets 4 MB per 100 GB.
+ */
+ items = fs_size / ((u_int64_t)100 * GIGABYTE);
+ filesize = (u_int64_t)(items + 1) * OVERFLOW_DEFAULT_SIZE;
+ if (filesize > OVERFLOW_MAXIMUM_SIZE)
+ filesize = OVERFLOW_MAXIMUM_SIZE;
+ zonesize += filesize;
+ hfsmp->hfs_overflow_maxblks = filesize / vcb->blockSize;
+
+ /*
+ * Plan for at least 8 MB of journal for each
+ * 100 GB of disk space (up to a 512 MB).
+ */
+ items = fs_size / ((u_int64_t)100 * GIGABYTE);
+ filesize = (u_int64_t)(items + 1) * JOURNAL_DEFAULT_SIZE;
+ if (filesize > JOURNAL_MAXIMUM_SIZE)
+ filesize = JOURNAL_MAXIMUM_SIZE;
+ zonesize += filesize;
+
+ /*
+ * Catalog file gets 10 MB per 1 GB.
+ *
+ * How about considering the current catalog size (used nodes * node size)
+ * and the current file data size to help estimate the required
+ * catalog size.
+ */
+ filesize = MIN((fs_size / 1024) * 10, GIGABYTE);
+ hfsmp->hfs_catalog_maxblks = filesize / vcb->blockSize;
+ zonesize += filesize;
+
+ /*
+ * Add space for hot file region.
+ *
+ * ...for now, use 5 MB per 1 GB (0.5 %)
+ */
+ filesize = (fs_size / 1024) * 5;
+ if (filesize > HOTBAND_MAXIMUM_SIZE)
+ filesize = HOTBAND_MAXIMUM_SIZE;
+ else if (filesize < HOTBAND_MINIMUM_SIZE)
+ filesize = HOTBAND_MINIMUM_SIZE;
+ /*
+ * Calculate user quota file requirements.
+ */
+ items = QF_USERS_PER_GB * (fs_size / GIGABYTE);
+ if (items < QF_MIN_USERS)
+ items = QF_MIN_USERS;
+ else if (items > QF_MAX_USERS)
+ items = QF_MAX_USERS;
+ if (!powerof2(items)) {
+ int x = items;
+ items = 4;
+ while (x>>1 != 1) {
+ x = x >> 1;
+ items = items << 1;
+ }
+ }
+ filesize += (items + 1) * sizeof(struct dqblk);
+ /*
+ * Calculate group quota file requirements.
+ *
+ */
+ items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE);
+ if (items < QF_MIN_GROUPS)
+ items = QF_MIN_GROUPS;
+ else if (items > QF_MAX_GROUPS)
+ items = QF_MAX_GROUPS;
+ if (!powerof2(items)) {
+ int x = items;
+ items = 4;
+ while (x>>1 != 1) {
+ x = x >> 1;
+ items = items << 1;
+ }
+ }
+ filesize += (items + 1) * sizeof(struct dqblk);
+ hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
+ zonesize += filesize;
+
+ /*
+ * Round up entire zone to a bitmap block's worth.
+ * The extra space goes to the catalog file and hot file area.
+ */
+ temp = zonesize;
+ zonesize = roundup(zonesize, vcb->vcbVBMIOSize * 8 * vcb->blockSize);
+ temp = zonesize - temp; /* temp has extra space */
+ filesize += temp / 3;
+ hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize;
+
+ /* Convert to allocation blocks. */
+ blk = zonesize / vcb->blockSize;
+
+ /* The default metadata zone location is at the start of volume. */
+ hfsmp->hfs_metazone_start = 1;
+ hfsmp->hfs_metazone_end = blk - 1;
+
+ /* The default hotfile area is at the end of the zone. */
+ hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize);
+ hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end;
+ hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp);
+#if 0
+ printf("HFS: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end);
+ printf("HFS: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end);
+ printf("HFS: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks);
+#endif
+ hfsmp->hfs_flags |= HFS_METADATA_ZONE;
+}
+
+
+static u_int32_t
+hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
+{
+ ExtendedVCB *vcb = HFSTOVCB(hfsmp);
+ int freeblocks;
+
+ freeblocks = MetaZoneFreeBlocks(vcb);
+ /* Minus Extents overflow file reserve. */
+ freeblocks -=
+ hfsmp->hfs_overflow_maxblks - VTOF(vcb->extentsRefNum)->ff_blocks;
+ /* Minus catalog file reserve. */
+ freeblocks -=
+ hfsmp->hfs_catalog_maxblks - VTOF(vcb->catalogRefNum)->ff_blocks;
+ if (freeblocks < 0)
+ freeblocks = 0;
+
+ return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks);
+}
+
+/*
+ * Determine if a file is a "virtual" metadata file.
+ * This includes journal and quota files.
+ */
+__private_extern__
+int
+hfs_virtualmetafile(struct cnode *cp)
+{
+ char * filename;
+
+
+ if (cp->c_parentcnid != kHFSRootFolderID)
+ return (0);
+
+ filename = cp->c_desc.cd_nameptr;
+ if (filename == NULL)
+ return (0);
+
+ if ((strcmp(filename, ".journal") == 0) ||
+ (strcmp(filename, ".journal_info_block") == 0) ||
+ (strcmp(filename, ".quota.user") == 0) ||
+ (strcmp(filename, ".quota.group") == 0) ||
+ (strcmp(filename, ".hotfiles.btree") == 0))
+ return (1);
+
+ return (0);
+}
+