mirror of
https://github.com/aaru-dps/docs.git
synced 2025-12-16 19:24:38 +00:00
* FD-Soft.html: Documents Atari FAT variations. * File_Manager.pdf: Documents HFS and Apple Partition Map. * hm2def.h: Documentation for ODS. * td0notes.txt: TeleDisk format information. * tn1150.html: Documents HFS+. * README.md: Information about this folder.
6804 lines
297 KiB
HTML
6804 lines
297 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
|
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
|
|
<HTML>
|
|
<!-- Template 03-24-01 -->
|
|
<!--Includes revisions to code listings-->
|
|
<head>
|
|
<meta name="ROBOTS" content="NOINDEX" />
|
|
<meta id="book-resource-type" name="book-resource-type" content="Technical Note">
|
|
<meta id="book-title" name="book-title" content="HFS Plus Volume Format">
|
|
<meta id="book-name" name="book-name" content="DTS_TN1150">
|
|
<meta id="book-root" name="book-root" content="./">
|
|
<meta id="devcenter" name="devcenter" content="Legacy">
|
|
<meta id="devcenter-url" name="devcenter-url" content="http://developer.apple.com/legacy">
|
|
<meta id="reflib" name="reflib" content="Retired Documents Library">
|
|
<meta id="book-assignments" name="book-assignments" content="{Type/Technical Note}">
|
|
<meta scheme="apple_ref" id="identifier" name="identifier" content="//apple_ref/doc/uid/DTS10002989">
|
|
<meta id="document-version" name="document-version" content="2.0.0">
|
|
<meta id="build" name="build" content="d46365bf75b5960db508d19a06816597">
|
|
<meta id="description" name="description" content="TN1150: Describes the physical layout of an HFS Plus volume.">
|
|
<meta id="resources-uri" name="resources-uri" content="../../Resources/893/">
|
|
<meta id="Generator" name="Generator" content="Gutenberg Static 003a3034">
|
|
<meta id="Copyright" name="Copyright" content="Copyright 2014 Apple Inc. All Rights Reserved.">
|
|
<link rel="stylesheet" type="text/css" href="../../Resources/893/CSS/screen.css" />
|
|
<link rel="stylesheet" type="text/css" href="../../Resources/893/CSS/feedback.css" />
|
|
|
|
|
|
<LINK REL="stylesheet" HREF="../../adcstyle.css" TYPE="text/css">
|
|
<LINK REL="stylesheet" HREF="../../style.css" TYPE="text/css">
|
|
|
|
<title>Technical Note TN1150: HFS Plus Volume Format</title>
|
|
|
|
<meta name="keywords" content="Mac OS 8 HFS Plus Volume format header B-trees catalog journal">
|
|
<meta name="Description" content="Technical Note TN1150: This Technical Note describes the
|
|
on-disk format for an HFS Plus volume. It does not describe
|
|
any programming interfaces for HFS Plus volumes. Topics include:
|
|
HFS Plus Basics, Volume Header (structure and types), B-Trees,
|
|
Catalog File, Extents Overflow File, Allocation File, Attributes
|
|
File, Startup File, Hard Links, Symbolic Links, Journal,
|
|
Unicode Subtleties, HFS Wrapper, and Volume Consistency Checks.">
|
|
|
|
|
|
<meta name="categories" content="Files and System Releases">
|
|
|
|
|
|
<meta name="week-posted" content="Dec 27, 1999 - Jan 7, 2000">
|
|
|
|
<LINK REL="stylesheet" HREF="../../css/adcstyle.css" TYPE="text/css"><script language="JavaScript" type="text/javascript" src="../../Resources/893/JavaScript/adc.js"></script>
|
|
</head>
|
|
|
|
|
|
<BODY BGCOLOR="#FFFFFF" id="StaticPage"><a name="//apple_ref/doc/uid/DTS10002989" title="HFS Plus Volume Format"></a>
|
|
|
|
<div id="adcHeader" class="hideOnPrint hideInXcodeSC">
|
|
<div id='ssi_Header' class="hideInXcodeSC default">
|
|
<a id="ssi_LibraryTitle" href='../../navigation/'>Retired Documents Library</a>
|
|
<a id="ssi_AppleDeveloperConnection" href='https://developer.apple.com/'>Developer</a>
|
|
<div id='ssi_SearchButton' role="button" title="Search">Search</div>
|
|
</div>
|
|
<form id='ssi_SearchMenu' method='get' action='../../search/' accept-charset='utf-8'>
|
|
<label for='adcsearch'>Search Retired Documents Library</label>
|
|
<input type='search' id='ssi_SearchField' name='q' accesskey='s' results='5' />
|
|
</form>
|
|
</div>
|
|
<header id="header">
|
|
<div id="title" role="banner">
|
|
<h1>HFS Plus Volume Format</h1>
|
|
<span id="file_links">
|
|
<a id="PDF_link" role="button" tabindex='4' rel="alternate" title="Download PDF"><span id="pdf_icon"></span>PDF</a>
|
|
<a id="Companion_link" role="button" tabindex='3' title="Download Companion File"><span id="companion_icon"></span>Companion File</a>
|
|
</span>
|
|
</div>
|
|
<ul id="headerButtons" class="hideOnPrint" role="toolbar">
|
|
<li id="toc_button" style="display:none">
|
|
<button tabindex="5" id="table_of_contents" class="open" role="checkbox" aria-label="Show Table of Contents">
|
|
<span class="disclosure"></span>Table of Contents</button>
|
|
</li>
|
|
<li id="jumpto_button" style="display:none" role="navigation">
|
|
<select tabindex="6" id="jumpTo">
|
|
<option value="top"><<Jump To…>></option>
|
|
</select>
|
|
</li>
|
|
<li id="downloadSample_button" style="display:none">
|
|
<a id="Sample_link"><button id="Sample_button">Download Sample Code</button></a>
|
|
</li>
|
|
</ul>
|
|
</header>
|
|
<nav id="tocContainer" class="" tabindex="7">
|
|
<ul id="toc" role="tree"></ul>
|
|
</nav>
|
|
<article id="contents">
|
|
<div id="technical">
|
|
<a NAME="top"></a>
|
|
<!-- begin_header_information --><p><a href="http://developer.apple.com/">ADC Home</a> > <a href="../../referencelibrary/index.html">Reference Library</a> > <a href="../../technicalnotes/index.html">Technical Notes</a> > <a href="../../technicalnotes/Carbon/index.html">Carbon</a> > <a href="../../technicalnotes/Carbon/idxFileManagement-date.html">File Management</a> > </p><!-- end_header_information -->
|
|
<!-- bottom_of_header_marker_comment -->
|
|
<!-- top_of_titles_marker_comment --><CENTER><table width="600" cellpadding="0" cellspacing="0" border="0">
|
|
<tr><td align="left" scope="row">
|
|
<h1>
|
|
<div id="pagehead">Technical Note TN1150</div>
|
|
<div id="pageheadsub">HFS Plus Volume Format</div>
|
|
</h1>
|
|
</td></tr></table></CENTER><!-- bottom_of_titles_marker_comment -->
|
|
|
|
<CENTER><table BORDER=0 CELLSPACING=1 WIDTH=600><TR><TD>
|
|
|
|
<!-- begin_header_box -->
|
|
<table width="600" cellpadding="0" cellspacing="0" border="0">
|
|
<tr>
|
|
<td width="300" valign="top" scope="row">
|
|
<table border="0" width="300" cellpadding="0" cellspacing="0">
|
|
<tr>
|
|
<td width="300"> <img src="images/tnmenutop.gif" alt="" align="bottom" width=300 height=7></td>
|
|
</tr>
|
|
<tr bgcolor="#e6e6e6">
|
|
<td background="images/tnmenubody.gif" width="300">
|
|
<span id="menutitle">
|
|
CONTENTS
|
|
<br>
|
|
<br>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr bgcolor="#e6e6e6">
|
|
<td background="images/tnmenubody.gif" width="300">
|
|
<!-- begin_toc -->
|
|
<P id = "menutext"><a HREF = "#HFSPlusBasics">HFS Plus Basics</a><BR><BR>
|
|
<a HREF = "#CoreConcepts">Core Concepts</a><BR><BR>
|
|
|
|
<a HREF = "#VolumeHeader">Volume Header</a><BR><BR>
|
|
|
|
<a HREF = "#BTrees">B-Trees</a><BR><BR>
|
|
|
|
<a HREF = "#CatalogFile">Catalog File</a><BR><BR>
|
|
|
|
<a HREF = "#ExtentsOverflowFile">Extents Overflow File</a><BR><BR>
|
|
|
|
<a HREF = "#AllocationFile">Allocation File</a><BR><BR>
|
|
|
|
<a HREF = "#AttributesFile">Attributes File</a><BR><BR>
|
|
|
|
<a HREF = "#StartupFile">Startup File</a><BR><BR>
|
|
|
|
<a HREF = "#HardLinks">Hard Links</a><BR><BR>
|
|
|
|
<a HREF = "#Symlinks">Symbolic Links</a><BR><BR>
|
|
|
|
<a HREF = "#Journal">Journal</a><BR><BR>
|
|
|
|
<a HREF = "#HFSX">HFSX</a><BR><BR>
|
|
|
|
<a HREF = "#MetadataZone">Metadata Zone</a><BR><BR>
|
|
|
|
<a HREF = "#HotFile">Hot Files</a><BR><BR>
|
|
|
|
<a HREF = "#UnicodeSubtleties">Unicode Subtleties</a><BR><BR>
|
|
|
|
<a HREF = "#HFSWrapper">HFS Wrapper</a><BR><BR>
|
|
|
|
<a HREF = "#VolumeConsistencyChecks">Volume Consistency Checks</a><BR><BR>
|
|
|
|
<a HREF = "#Summary">Summary</a><BR><BR>
|
|
|
|
<a href="#Downloads">Downloadables</a></p>
|
|
<!-- end_toc -->
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td width="300" scope="row">
|
|
<img src="images/tnmenubottom.gif" alt="" width=300 height=16>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
<td width="300" valign="top">
|
|
|
|
<!-- begin_intro_text -->
|
|
<P id = "introtext">This Technote
|
|
describes the on-disk format for an HFS Plus volume. It does
|
|
<B>not</B> describe any programming interfaces for HFS Plus
|
|
volumes.</P>
|
|
|
|
<P id = "introtext">This technote is directed at developers who need to work
|
|
with HFS Plus at a very low level, below the abstraction
|
|
provided by the File Manager programming interface. This
|
|
includes developers of disk recovery utilities and
|
|
programmers implementing HFS Plus support on other
|
|
platforms.</P>
|
|
|
|
<P id = "introtext">This technote assumes that you have a conceptual
|
|
understanding of the HFS volume format, as described in
|
|
<a href="http://developer.apple.com/techpubs/mac/Files/Files-99.html#HEADING99-0">Inside
|
|
Macintosh: Files</a>.</p>
|
|
|
|
<!-- end_intro_text -->
|
|
<!-- begin_date --><H4 ALIGN=center>[Mar 05, 2004]</H4><!-- end_date -->
|
|
</TD>
|
|
</TR>
|
|
</table>
|
|
<!-- end_header_box -->
|
|
<BR><BR>
|
|
<hr width=500 align=center>
|
|
<BR><BR>
|
|
<!-- begin_content -->
|
|
<H2><a NAME="HFSPlusBasics"></a>HFS Plus Basics</H2>
|
|
|
|
<P>HFS Plus is a volume format for Mac OS. HFS
|
|
Plus was introduced with <a HREF =
|
|
"http://developer.apple.com/technotes/tn/tn1121.html#HFS%20Plus">Mac
|
|
OS 8.1</a>. HFS Plus is architecturally very similar to
|
|
HFS, although there have been a number of changes. The
|
|
following table summarizes the important differences.</P>
|
|
|
|
<P ALIGN=CENTER><B>Table 1</B> HFS and HFS Plus Compared
|
|
</P>
|
|
|
|
<CENTER><table BORDER=1>
|
|
<TR>
|
|
<TD>
|
|
<P><B>Feature</B></p>
|
|
</TD><TD>
|
|
<P><B>HFS</B></p>
|
|
</TD><TD>
|
|
<P><B>HFS Plus</B></p>
|
|
</TD><TD>
|
|
<P><B>Benefit/Comment</B></p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>User visible name</p>
|
|
</TD><TD>
|
|
<P>Mac OS Standard</p>
|
|
</TD><TD>
|
|
<P>Mac OS Extended</p>
|
|
</TD><TD>
|
|
<P></p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>Number of allocation blocks</p>
|
|
</TD><TD>
|
|
<P>16 bits worth</p>
|
|
</TD><TD>
|
|
<P>32 bits worth</p>
|
|
</TD><TD>
|
|
<P>Radical decrease in disk space used on large
|
|
volumes, and a larger number of files per volume.</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>Long file names</p>
|
|
</TD><TD>
|
|
<P>31 characters</p>
|
|
</TD><TD>
|
|
<P>255 characters</p>
|
|
</TD><TD>
|
|
<P>Obvious user benefit; also improves
|
|
cross-platform compatibility</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>File name encoding</p>
|
|
</TD><TD>
|
|
<P>MacRoman</p>
|
|
</TD><TD>
|
|
<P>Unicode</p>
|
|
</TD><TD>
|
|
<P>Allows for international-friendly file names,
|
|
including mixed script names</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>File/folder attributes</p>
|
|
</TD><TD>
|
|
<P>Support for fixed size attributes (FileInfo and
|
|
ExtendedFileInfo)</p>
|
|
</TD><TD>
|
|
<P>Allows for future meta-data extensions</p>
|
|
</TD><TD>
|
|
<P>Future systems may use metadata for a richer
|
|
Finder experience</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>OS startup support</p>
|
|
</TD><TD>
|
|
<P>System Folder ID</p>
|
|
</TD><TD>
|
|
<P>Also supports a dedicated startup file</p>
|
|
</TD><TD>
|
|
<P>May help non-Mac OS systems to boot from HFS
|
|
Plus volumes</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>catalog node size</p>
|
|
</TD><TD>
|
|
<P>512 bytes</p>
|
|
</TD><TD>
|
|
<P>4 KB</p>
|
|
</TD><TD>
|
|
<P>Maintains efficiency in the face of the other
|
|
changes. (This larger catalog node size is due to
|
|
the much longer file names [512 bytes as opposed to
|
|
32 bytes], and larger catalog records (because of
|
|
more/larger fields)).</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD>
|
|
<P>Maximum file size</p>
|
|
</TD><TD>
|
|
<P>2<SUP>31</SUP> bytes</p>
|
|
</TD><TD>
|
|
<P>2<SUP>63</SUP> bytes</p>
|
|
</TD><TD>
|
|
<P>Obvious user benefit, especially for multimedia
|
|
content creators.</p></td>
|
|
</tr></table></CENTER>
|
|
|
|
<P>The extent to which these HFS Plus features are available
|
|
through a programming interface is OS dependent. Mac OS versions
|
|
less than 9.0 do not provide programming interfaces for any
|
|
HFS Plus-specific features.</P>
|
|
|
|
<P>To summarize, the key goals that guided the design of the
|
|
HFS Plus volume format were:</P>
|
|
|
|
<UL>
|
|
<li>efficient use of disk space,</li>
|
|
<li>international-friendly file names,</li>
|
|
<li>future support for named forks, and</li>
|
|
<li>ease booting on non-Mac OS operating systems.</li>
|
|
</UL>
|
|
|
|
<P>The following sections describes these goals, and the
|
|
differences between HFS and HFS Plus required to meet these
|
|
goals.</P>
|
|
|
|
<H3>Efficient Use of Disk Space</H3>
|
|
|
|
<P>HFS divides the total space on a volume into equal-sized
|
|
pieces called allocation blocks. It uses 16-bit fields to
|
|
identify a particular allocation block, so there must be
|
|
less than 2<SUP>16</SUP> (65,536) allocation blocks on an
|
|
HFS volume. The size of an allocation block is typically the
|
|
smallest multiple of 512 such that there are less than
|
|
65,536 allocation blocks on the volume (i.e., the volume
|
|
size divided by 65,535, rounded up to a multiple of 512).
|
|
Any non-empty fork must occupy an integral number of
|
|
allocation blocks. This means that the amount of space
|
|
occupied by a fork is rounded up to a multiple of the
|
|
allocation block size. As volumes (and therefore allocation
|
|
blocks) get bigger, the amount of allocated but unused space
|
|
increases.</P>
|
|
|
|
<P>HFS Plus uses 32-bit values to identify allocation
|
|
blocks. This allows up to 2 <SUP>32</SUP> (4,294,967,296)
|
|
allocation blocks on a volume. More allocation blocks means
|
|
a smaller allocation block size, especially on volumes of 1
|
|
GB or larger, which in turn means less average wasted space
|
|
(the fraction of an allocation block at the end of a fork,
|
|
where the entire allocation block is not actually used). It
|
|
also means you can have more files, since the available
|
|
space can be more finely distributed among a larger number
|
|
of files. This change is especially beneficial if the volume
|
|
contains a large number of small files.</P>
|
|
|
|
<H3>International-Friendly File Names</H3>
|
|
|
|
<P>HFS uses 31-byte strings to store file names. HFS does
|
|
not store any kind of script information with the file name
|
|
to indicate how it should be interpreted. File names are
|
|
compared and sorted using a routine that assumes a Roman
|
|
script, wreaking havoc for names that use some other script
|
|
(such as Japanese). Worse, this algorithm is buggy, even for
|
|
Roman scripts. The Finder and other applications interpret
|
|
the file name based on the script system in use at runtime.
|
|
</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
|
|
The problem with using non-Roman scripts in an HFS
|
|
file name is that HFS compares file names in a case-
|
|
insensitive fashion. The case-insensitive
|
|
comparison algorithm assume a MacRoman encoding.
|
|
When presented with non-Roman text, this algorithm
|
|
fails in strange ways. The upshot is that HFS
|
|
decides that certain non-Roman file names are
|
|
duplicates of other file names, even though they
|
|
are not duplicates in the source encoding.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>HFS Plus uses up to 255 Unicode characters to store file
|
|
names. Allowing up to 255 characters makes it easier to have
|
|
very descriptive names. Long names are especially useful
|
|
when the name is computer-generated (such as Java class
|
|
names).</P>
|
|
|
|
<P>The HFS catalog B-tree uses 512-byte nodes. An HFS Plus
|
|
file name can occupy up to 512 bytes (including the length
|
|
field). Since a B-tree index node must store at least two
|
|
keys (plus pointers and node descriptor), the HFS Plus
|
|
catalog must use a larger node size. The typical node size
|
|
for an HFS Plus catalog B-tree is 4 KB.</P>
|
|
|
|
<P>In the HFS catalog B-tree, the keys stored in an index
|
|
node always occupy a fixed amount of space, the maximum key
|
|
size. In HFS Plus, the keys in an index node may occupy a
|
|
variable amount of space determined by the actual size of
|
|
the key. This allows for less wasted space in index nodes
|
|
and creates, on typical disks, a substantially larger
|
|
branching factor in the tree (requiring fewer node accesses
|
|
to find any given record).</P>
|
|
|
|
<H3>Future Support for Named Forks</H3>
|
|
|
|
<P>Files on an HFS volume have two forks: a data fork and a
|
|
resource fork, either of which may be empty (zero length).
|
|
Files and directories also contain a small amount of
|
|
additional information (known as catalog information or
|
|
metadata) such as the modification date or Finder info.</P>
|
|
|
|
<P>Apple software teams and third-party developers often
|
|
need to store information associated with particular files
|
|
and directories. In some cases (for example, custom icons for
|
|
files), the data or resource fork is appropriate. But in
|
|
other cases (for example, custom icons for directories, or File
|
|
Sharing access privileges), using the data or resource fork
|
|
is not appropriate or not possible.</P>
|
|
|
|
<P>A number of products have implemented special-purpose
|
|
solutions for storing their file- and directory-related
|
|
data. But because these are not managed by the file system,
|
|
they can become inconsistent with the file and directory
|
|
structure.</P>
|
|
|
|
<P>HFS Plus has an attribute file, another B-tree, that can
|
|
be used to store additional information for a file or
|
|
directory. Since it is part of the volume format, this
|
|
information can be kept with the file or directory as is it
|
|
moved or renamed, and can be deleted when the file or
|
|
directory is deleted. The contents of the attribute file's
|
|
records have not been fully defined yet, but the goal is to
|
|
provide an arbitrary number of forks, identified by Unicode
|
|
names, for any file or directory.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
|
|
Because the attributes file has not been fully
|
|
defined yet, current implementations are unable to
|
|
delete named forks when a file or directory is
|
|
deleted. Future implementations that properly
|
|
delete named forks will need to check for these
|
|
orphaned named forks and delete them when the
|
|
volume is mounted. The
|
|
<CODE>lastMountedVersion</CODE> field of the volume
|
|
header can be used to detect when such a check
|
|
needs to take place.</P>
|
|
|
|
<P>Whenever possible, an application should delete
|
|
named forks rather than orphan them.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3>Easy Startup of Alternative Operating Systems</H3>
|
|
|
|
<P>HFS Plus defines a special <STRONG>startup file</STRONG>,
|
|
an unstructured fork that can be found easily during system
|
|
startup. The location and size of the startup file is
|
|
described in the <a HREF = "#VolumeHeader">volume header</a>.
|
|
The startup file is especially useful on systems that don't
|
|
have HFS or HFS Plus support in ROM. In many respects, the
|
|
startup file is a generalization of the HFS boot blocks, one
|
|
that provides a much larger, variable-sized amount of
|
|
storage.</P>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="CoreConcepts"></a>Core Concepts</H2>
|
|
|
|
<P>HFS Plus uses a number of interrelated structures to
|
|
manage the organization of data on the volume. These
|
|
structures include:</P>
|
|
|
|
<UL>
|
|
<li>the <a HREF = "#VolumeHeader">volume header</a></li>
|
|
|
|
<li>the <a HREF = "#CatalogFile">catalog file</a></li>
|
|
|
|
<li>the <a HREF = "#ExtentsOverflowFile">extents overflow
|
|
file</a></li>
|
|
|
|
<li>the <a HREF = "#AttributesFile">attributes file</a></li>
|
|
|
|
<li>the <a HREF = "#AllocationFile">allocation file</a>
|
|
(bitmap)</li>
|
|
|
|
<li>the <a HREF = "#StartupFile">startup file</a></li>
|
|
</UL>
|
|
|
|
<P>Each of these complex structures is described in its own
|
|
section. The purpose of this section is to give an overview
|
|
of the volume format, describe how the structures fit
|
|
together, and define the primitive data types used by HFS
|
|
Plus.</P>
|
|
|
|
<H3>Terminology</H3>
|
|
|
|
<P>HFS Plus is a specification of how a <B>volume</B> (files
|
|
that contain user data, along with the structure to retrieve
|
|
that data) exists on a <B>disk</B> (the medium on which user
|
|
data is stored). The storage space on a disk is divided into
|
|
units called <B>sectors</B>. A sector is the smallest
|
|
part of a disk that the disk's driver software will read or write
|
|
in a single operation (without having to read or write additional
|
|
data before or after the requested data). The size of a sector is
|
|
usually based on the way the data is physically laid out on the disk.
|
|
For hard disks, sectors are typically 512 bytes. For optical media,
|
|
sectors are typically 2048 bytes.</P>
|
|
|
|
<P>Most of the data structures on an HFS Plus volume do not
|
|
depend on the size of a sector, with the exception of the
|
|
<a href="#Journal">journal</a>. Because the journal does rely
|
|
on accessing individual sectors, the sector size is stored
|
|
in the <code>jhdr_size</code> field of the
|
|
<a href="#JournalHeader">journal header</a> (if the
|
|
volume has a journal).</P>
|
|
|
|
<P>HFS Plus allocates space in units called <B>allocation
|
|
blocks</B>; an allocation block is simply a group of
|
|
consecutive bytes. The size (in bytes) of an allocation
|
|
block is a power of two, greater than or equal to 512, which
|
|
is set when the volume is initialized. This value cannot be
|
|
easily changed without reinitializing the volume. Allocation
|
|
blocks are identified by a 32-bit <B>allocation block
|
|
number</B>, so there can be at most 2<SUP>32</SUP>
|
|
allocation blocks on a volume. Current implementations of the
|
|
file system are optimized for 4K allocation blocks.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
For the best performance, the allocation block size should
|
|
be a multiple of the sector size. If the
|
|
volume has an <a HREF = "#HFSWrapper">HFS wrapper</a>, the
|
|
wrapper's allocation block size and allocation block start
|
|
should also be multiples of the sector size to
|
|
allow the best performance.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>All of the volume's structures, including the volume
|
|
header, are part of one or more allocation blocks (with the possible
|
|
exception of the alternate volume header, discussed
|
|
<a HREF = "#OddSizeVolumes">below</a>). This differs from HFS,
|
|
which has several structures (including the boot blocks, master
|
|
directory block, and bitmap) which are not part of any
|
|
allocation block.</P>
|
|
|
|
<P><a NAME="Clump"></a>To promote file contiguity and avoid
|
|
fragmentation, disk space is typically allocated to files in
|
|
groups of allocation blocks, or <B>clumps</B>. The clump size
|
|
is always a multiple of the allocation block size. The default
|
|
clump size is specified in the volume header.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The actual algorithm used to extend files is not part
|
|
of this specification. The implementation is not
|
|
required to act on the clump values in the volume
|
|
header; it merely provides space to store those
|
|
values.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The current non-contiguous algorithm in Mac OS will
|
|
begin allocating at the next free block it finds.
|
|
It will extend its allocation up to a multiple of
|
|
the clump size if there is sufficient free space
|
|
contiguous with the end of the requested
|
|
allocation. Space is not allocated in contiguous
|
|
clump-sized pieces.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Every HFS Plus volume must have a <B>volume header</B>.
|
|
The volume header contains sundry information about the
|
|
volume, such as the date and time of the volume's creation
|
|
and the number of files on the volume, as well as the
|
|
location of the other key structures on the volume. The
|
|
volume header is always located at 1024 bytes from the
|
|
start of the volume.</P>
|
|
|
|
<P>A copy of the volume header, known as the <B>alternate
|
|
volume header</B>, is stored starting at 1024 bytes before
|
|
the end of the volume. The first 1024 bytes of volume
|
|
(before the volume header), and the last 512 bytes of the
|
|
volume (after the alternate volume header) are
|
|
<a href="#ReservedAndPadFields">reserved</a>.
|
|
All of the allocation blocks containing the volume header,
|
|
alternate volume header, or the reserved areas before the
|
|
volume header or after the alternate volume header, are marked
|
|
as used in the <a href="#AllocationFile">allocation file</a>.
|
|
The actual number of allocation blocks marked this way depends
|
|
on the allocation block size.</P>
|
|
|
|
<P>An HFS Plus volume contains five <B>special files</B>,
|
|
which store the file system structures required to access
|
|
the file system payload: folders, <B>user files</B>, and
|
|
attributes. The special files are the catalog file, the
|
|
extents overflow file, the allocation file, the attributes
|
|
file and the startup file. Special files only have a single
|
|
fork (the data fork) and the extents of that fork are
|
|
described in the volume header.</P>
|
|
|
|
<P>The <B>catalog file</B> is a special file that describes
|
|
the folder and file hierarchy on a volume. The catalog file
|
|
contains vital information about all the files and folders
|
|
on a volume, as well as the <B>catalog information</B>, for
|
|
the files and folders that are stored in the catalog file.
|
|
The catalog file is organized as a B-tree (or "balanced
|
|
tree") to allow quick and efficient searches through a large
|
|
folder hierarchy.</P>
|
|
|
|
<P>The catalog file stores the file and folder names, which
|
|
consist of up to 255 Unicode characters, as described
|
|
<a HREF = "#HFSPlusNames">below</a>.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The <a HREF = "#BTrees">B-Trees</a> section contains
|
|
an in-depth description of the B-trees used by HFS
|
|
Plus.</p></TD></TR></table></CENTER>
|
|
|
|
<P>The <B>attributes file</B> is another special file which
|
|
contains additional data for a file or folder. Like the
|
|
catalog file, the attributes file is organized as a B-tree.
|
|
In the future, it will be used to store information about
|
|
additional forks. (This is similar to the way the catalog
|
|
file stores information about the data and resource forks of
|
|
a file.)</P>
|
|
|
|
<P>HFS Plus tracks which allocation blocks belong to a fork
|
|
by maintaining a list of the fork's extents. An
|
|
<B>extent</B> is a contiguous range of allocation blocks
|
|
allocated to some fork, represented by a pair of numbers:
|
|
the first allocation block number and the number of
|
|
allocation blocks. For a user file, the first eight extents
|
|
of each fork are stored in the volume's catalog file. Any
|
|
additional extents are stored in the <B>extents overflow
|
|
file</B>, which is also organized as a B-tree.</P>
|
|
|
|
<P>The extents overflow file also stores additional extents
|
|
for the special files except for the extents overflow file
|
|
itself. However, if the startup file requires more than the
|
|
eight extents in the Volume Header (and thus requires
|
|
additional extents in the extents overflow file), it would
|
|
be much harder to access, and defeat the purpose of the
|
|
startup file. So, in practice, a startup file should be
|
|
allocated such that it doesn't need additional extents in
|
|
the extents overflow file.</P>
|
|
|
|
<P>The <B>allocation file</B> is a special file which
|
|
specifies whether an allocation block is used or free. This
|
|
performs the same role as the HFS volume bitmap, although
|
|
making it a file adds flexibility to the volume format.</P>
|
|
|
|
<P>The <B>startup file</B> is another special file which
|
|
facilitates booting of non-Mac OS computers from HFS Plus
|
|
volumes.</P>
|
|
|
|
<P>Finally, the <B>bad block file</B> prevents the volume
|
|
from using certain allocation blocks because the portion of
|
|
the media that stores those blocks is defective. The bad
|
|
block file is neither a special file nor a user file; this
|
|
is merely convention used in the extents overflow file. See
|
|
<a HREF = "#BadBlockFile">Bad Block File</a> for more details.
|
|
</P>
|
|
|
|
<H3>Broad Structure</H3>
|
|
|
|
<P>The bulk of an HFS Plus volume consists of seven types of
|
|
information or areas:</P>
|
|
|
|
<OL>
|
|
<li>user file forks,</li>
|
|
|
|
<li>the allocation file (bitmap),</li>
|
|
|
|
<li>the catalog file,</li>
|
|
|
|
<li>the extents overflow file,</li>
|
|
|
|
<li>the attributes file,</li>
|
|
|
|
<li>the startup file, and</li>
|
|
|
|
<li>unused space.</li>
|
|
</OL>
|
|
|
|
<P>The general structure of an HFS Plus volume is
|
|
illustrated in Figure 1.</P>
|
|
|
|
|
|
<CENTER><img src="images/tn1150_001.png" alt="Organization of an HFS Plus Volume" width=297 height=450 align=bottom>
|
|
<P><B>Figure 1</B>. Organization of an HFS Plus
|
|
Volumes.</P>
|
|
</center>
|
|
|
|
<P>The volume header is always at a fixed
|
|
location (1024 bytes from the start of the volume).
|
|
However, the special files can appear anywhere between
|
|
the volume header block and the alternate volume header
|
|
block. These files can appear in any order and are not
|
|
necessarily contiguous.</P>
|
|
|
|
<P>The information on HFS Plus volumes (with the possible
|
|
exception of the alternate volume header, as discussed
|
|
<a HREF = "#OddSizeVolumes">below</a>) is organized solely
|
|
in allocation blocks. Allocation blocks are simply a means
|
|
of grouping space on the media into convenient parcels.
|
|
The size of an allocation block is a power of two,
|
|
and at least 512. The allocation block size is a volume
|
|
header parameter whose value is set when the volume is
|
|
initialized; it cannot be changed easily without
|
|
reinitializing the volume.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The allocation block size is a classic
|
|
speed-versus- space tradeoff. Increasing the
|
|
allocation block size decreases the size of the
|
|
allocation file, and often reduces the number of
|
|
separate extents that must be manipulated for every
|
|
file. It also tends to increase the average size of
|
|
a disk I/O, which decreases overhead. Decreasing
|
|
the allocation block size reduces the average
|
|
number of wasted bytes per file, making more
|
|
efficient use of the volume's space.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<a NAME="SmallAllocationBlockWarning"></a>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>WARNING:</B><BR>
|
|
|
|
While HFS Plus disks with an allocation block size
|
|
smaller than 4 KB are legal, DTS recommends that
|
|
you always use a minimum 4 KB allocation block
|
|
size. Disks with a smaller allocation block size
|
|
will be markedly slower when used on systems that
|
|
do 4 KB clustered I/O, such as Mac OS X Server.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3>Primitive Data Types</H3>
|
|
|
|
<P>This section describes the primitive data types used on
|
|
an HFS Plus volume. All data structures in this volume are
|
|
defined in the C language. The specification assumes that
|
|
the compiler will not insert any padding fields. Any
|
|
necessary padding fields are explicitly declared.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The HFS Plus volume format is largely derived from
|
|
the HFS volume format. When defining the new
|
|
format, it was decided to remove unused fields
|
|
(primarily legacy MFS fields) and arrange all the
|
|
remaining fields so that similar fields were
|
|
grouped together and that all fields had proper
|
|
alignment (using PowerPC alignment rules).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H4><a NAME="ReservedAndPadFields"></a>Reserved and Pad
|
|
Fields</H4>
|
|
|
|
<P>In many places this specification describes a field, or
|
|
bit within a field, as reserved. This has a definite
|
|
meaning, namely:</P>
|
|
|
|
<UL>
|
|
<li>When creating a structure with a reserved field, an
|
|
implementation must set the field to zero.</li>
|
|
|
|
<li>When reading existing structures, an implementation
|
|
must ignore any value in the field.</li>
|
|
|
|
<li>When modifying a structure with a reserved field, an
|
|
implementation must preserve the value of the reserved
|
|
field.</li>
|
|
</UL>
|
|
|
|
<P>This definition allows for backward-compatible
|
|
enhancements to the volume format.</P>
|
|
|
|
<P>Pad fields have exactly the same semantics as a reserved
|
|
field. The different name merely reflects the designer's
|
|
goals when including the field, not the behavior of the
|
|
implementation.</P>
|
|
|
|
<H4>Integer Types</H4>
|
|
|
|
<P>All integer values are defined by one of the following
|
|
primitive types: <CODE>UInt8</CODE>, <CODE>SInt8</CODE>,
|
|
<CODE>UInt16</CODE>, <CODE>SInt16</CODE>,
|
|
<CODE>UInt32</CODE>, <CODE>SInt32</CODE>,
|
|
<CODE>UInt64</CODE>, and <CODE>SInt64</CODE>. These
|
|
represent unsigned and signed (2's complement) 8-bit,
|
|
16-bit, 32-bit, and 64-bit numbers.</P>
|
|
|
|
<P>All multi-byte integer values are stored in big-endian
|
|
format. That is, the bytes are stored in order from most
|
|
significant byte through least significant byte, in
|
|
consecutive bytes, at increasing offset from the start of a
|
|
block. Bits are numbered from 0 to <i>n</i>-1 (for types
|
|
<code>UInt</code><i>n</i> and <code>SInt</code><i>n</i>),
|
|
with bit 0 being the least significant bit.</P>
|
|
|
|
<H4><a NAME="HFSPlusNames"></a>HFS Plus Names</H4>
|
|
|
|
<P>File and folder names on HFS Plus consist of up to 255
|
|
Unicode characters with a preceding 16-bit length, defined
|
|
by the type <CODE>HFSUniStr255</CODE>.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSUniStr255 {
|
|
UInt16 length;
|
|
UniChar unicode[255];
|
|
};
|
|
typedef struct HFSUniStr255 HFSUniStr255;
|
|
typedef const HFSUniStr255 *ConstHFSUniStr255Param;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P><CODE>UniChar</CODE> is a <CODE>UInt16</CODE> that
|
|
represents a character as defined in the Unicode character
|
|
set defined by <I> The Unicode Standard, Version 2.0</I>
|
|
[Unicode, Inc. ISBN 0-201-48345-9].</P>
|
|
|
|
<P>HFS Plus stores strings fully decomposed and in canonical
|
|
order. HFS Plus compares strings in a case-insensitive
|
|
fashion. Strings may contain Unicode characters that must
|
|
be ignored by this comparison. For more details on these
|
|
subtleties, see <a HREF = "#UnicodeSubtleties">Unicode
|
|
Subtleties</a>.</P>
|
|
|
|
<P>A variant of HFS Plus, called <a href="#HFSX">HFSX</a>,
|
|
allows volumes whose names are compared in a case-sensitive
|
|
fashion. The names are fully decomposed and in canonical order,
|
|
but no Unicode characters are ignored during the comparison.</P>
|
|
|
|
<H4><a NAME="TextEncodings"></a>Text Encodings</H4>
|
|
|
|
<P>Traditional Mac OS programming interfaces pass filenames as
|
|
Pascal strings (either as a <CODE>StringPtr</CODE> or as a
|
|
<CODE>Str63</CODE> embedded in an <CODE>FSSpec</CODE>). The
|
|
characters in those strings are not Unicode; the encoding
|
|
varies depending on how the system software was localized
|
|
and what language kits are installed. Identical sequences of
|
|
bytes can represent vastly different Unicode character
|
|
sequences. Similarly, many Unicode characters belong to more
|
|
than one Mac OS text encoding.</P>
|
|
|
|
<P>HFS Plus includes two features specifically designed to
|
|
help Mac OS handle the conversion between Mac OS-encoded
|
|
Pascal strings and Unicode. The first feature is the
|
|
<CODE>textEncoding</CODE> field of the file and folder
|
|
catalog records. This field is defined as a hint to be used
|
|
when converting the record's Unicode name back to a Mac OS-
|
|
encoded Pascal string.</P>
|
|
|
|
<P>The valid values for the <CODE>textEncoding</CODE> field
|
|
are defined in Table 2.</P>
|
|
|
|
<P ALIGN=CENTER><B>Table 2</B> Text Encodings</p>
|
|
|
|
<CENTER><table BORDER=1>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P><B>Encoding Name</B></p>
|
|
</TD><TD WIDTH=60>
|
|
<P><B>Value</B></p>
|
|
</TD><TD WIDTH=140>
|
|
<P><B>Encoding Name</B></p>
|
|
</TD><TD WIDTH=60>
|
|
<P><B>Value</B></p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacRoman</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>0</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacThai</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>21</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacJapanese</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>1</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacLaotian</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>22</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacChineseTrad</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>2</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacGeorgian</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>23</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacKorean</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>3</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacArmenian</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>24</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacArabic</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>4</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacChineseSimp</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>25</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacHebrew</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>5</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacTibetan</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>26</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacGreek</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>6</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacMongolian</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>27</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacCyrillic</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>7</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacEthiopic</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>28</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacDevanagari</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>9</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacCentralEurRoman</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>29</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacGurmukhi</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>10</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacVietnamese</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>30</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacGujarati</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>11</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacExtArabic</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>31</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacOriya</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>12</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacSymbol</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>33</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacBengali</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>13</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacDingbats</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>34</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacTamil</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>14</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacTurkish</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>35</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacTelugu</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>15</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacCroatian</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>36</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacKannada</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>16</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacIcelandic</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>37</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacMalayalam</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>17</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacRomanian</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>38</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacSinhalese</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>18</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacFarsi</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>140 (49)</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacBurmese</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>19</p>
|
|
</TD><TD WIDTH=140>
|
|
<P>MacUkrainian</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>152 (48)</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=140>
|
|
<P>MacKhmer</p>
|
|
</TD><TD WIDTH=60>
|
|
<P>20</p>
|
|
</TD><TD WIDTH=140>
|
|
<P></p>
|
|
</TD><TD WIDTH=60>
|
|
<P></p>
|
|
</TD></TR>
|
|
</table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
Non-Mac OS implementations of HFS Plus may choose
|
|
to simply ignore the <CODE>textEncoding</CODE>
|
|
field. In this case, the field must be treated as
|
|
a <a HREF = "#ReservedAndPadFields">reserved</a>
|
|
field.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Mac OS uses the <CODE>textEncoding</CODE> field in
|
|
the following way. When a file or folder is created
|
|
or renamed, Mac OS converts the supplied Pascal
|
|
string to a <CODE>HFSUniStr255</CODE>. It stores
|
|
the source text encoding in the
|
|
<CODE>textEncoding</CODE> field of the catalog
|
|
record. When Mac OS needs to create a Pascal string
|
|
for that record, it uses the
|
|
<CODE>textEncoding</CODE> as a hint to the text
|
|
conversion process. This hint ensures a high-degree
|
|
of round-trip conversion fidelity, which in turn
|
|
improves compatibility.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The second use of text encodings in HFS Plus is the
|
|
<CODE>encodingsBitmap</CODE> field of the volume header. For
|
|
each encoding used by a catalog node on the volume, the
|
|
corresponding bit in the <CODE>encodingsBitmap</CODE> field
|
|
must be set.</P>
|
|
|
|
<P>It is acceptable for a bit in this bitmap to be set even
|
|
though no names on the volume use that encoding. This means
|
|
that when an implementation deletes or renames an object, it
|
|
does not have to clear the encoding bit if that was the last
|
|
name to use the given encoding.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The text encoding value is used as the number of
|
|
the bit to set in <CODE>encodingsBitmap</CODE> to
|
|
indicate that the encoding is used on the volume.
|
|
However, <CODE>encodingsBitmap</CODE> is only 64
|
|
bits long, and thus the text encoding values for
|
|
MacFarsi and MacUkrainian cannot be used as bit
|
|
numbers. Instead, another bit number (shown in
|
|
parenthesis) is used.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Mac OS uses the <CODE>encodingsBitmap</CODE> field
|
|
to determine which text encoding conversion tables
|
|
to load when the volume is mounted. Text encoding
|
|
conversion tables are large, and loading them
|
|
unnecessarily is a waste of memory. Most systems
|
|
only use one text encoding, so there is a
|
|
substantial benefit to recording which encodings
|
|
are required on a volume-by-volume basis.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>WARNING:</B><BR>
|
|
|
|
Non-Mac OS implementations of HFS Plus must
|
|
correctly maintain the <CODE>encodingsBitmap</CODE>
|
|
field. Specifically, if the implementation sets the
|
|
<CODE>textEncoding</CODE> field a catalog record to
|
|
a text-encoding value, it must ensure that the
|
|
corresponding bit is set in
|
|
<CODE>encodingsBitmap</CODE> to ensure correct
|
|
operation when that disk is mounted on a system
|
|
running Mac OS.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H4><a NAME="HFSPlusDates"></a>HFS Plus Dates</H4>
|
|
|
|
<P>HFS Plus stores dates in several data structures,
|
|
including the volume header and catalog records. These dates
|
|
are stored in unsigned 32-bit integers (<CODE>UInt32</CODE>)
|
|
containing the number of seconds since midnight, January 1,
|
|
1904, GMT. This is slightly different from HFS, where the
|
|
value represents local time.</P>
|
|
|
|
<P>The maximum representable date is February 6, 2040 at
|
|
06:28:15 GMT.</P>
|
|
|
|
<P>The date values do not account for leap seconds. They do
|
|
include a leap day in every year that is evenly divisible by
|
|
four. This is sufficient given that the range of
|
|
representable dates does not contain 1900 or 2100, neither
|
|
of which have leap days.</P>
|
|
|
|
<P>The implementation is responsible for converting these
|
|
times to the format expected by client software. For
|
|
example, the Mac OS File Manager passes dates in local time;
|
|
the Mac OS HFS Plus implementation converts dates between
|
|
local time and GMT as appropriate.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The creation date stored in
|
|
the Volume Header is NOT stored in GMT; it is
|
|
stored in local time. The reason for this is that
|
|
many applications (including backup utilities) use
|
|
the volume's creation date as a relatively unique
|
|
identifier. If the date was stored in GMT, and
|
|
automatically converted to local time by an
|
|
implementation (like Mac OS), the value would
|
|
appear to change when the local time zone or
|
|
daylight savings time settings change (and thus
|
|
cause some applications to improperly identify the
|
|
volume). The use of the volume's creation date as a
|
|
unique identifier outweighs its use as a date. This
|
|
change was introduced late in the Mac OS 8.1
|
|
project.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H4><a NAME="HFSPlusPermissions"></a>HFS Plus Permissions
|
|
</H4>
|
|
|
|
<P>For each file and folder, HFS Plus maintains a record
|
|
containing access permissions, defined by the
|
|
<CODE>HFSPlusBSDInfo</CODE> structure.</P>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusBSDInfo {
|
|
UInt32 ownerID;
|
|
UInt32 groupID;
|
|
UInt8 adminFlags;
|
|
UInt8 ownerFlags;
|
|
UInt16 fileMode;
|
|
union {
|
|
UInt32 iNodeNum;
|
|
UInt32 linkCount;
|
|
UInt32 rawDevice;
|
|
} special;
|
|
};
|
|
typedef struct HFSPlusBSDInfo HFSPlusBSDInfo;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>ownerID</CODE></dt>
|
|
|
|
<DD>The Mac OS X user ID of the owner of the file or folder.
|
|
Mac OS X versions prior to 10.3 treats user ID 99 as if it was the user ID of the
|
|
user currently logged in to the console. If no user is logged in to the
|
|
console, user ID 99 is treated as user ID 0 (root). Mac OS X version 10.3
|
|
treats user ID 99 as if it was the user ID of the process making the call
|
|
(in effect, making it owned by everyone simultaneously). These substitutions
|
|
happen at run time. The actual user ID on disk is not changed.</dd>
|
|
|
|
<DT><CODE>groupID</CODE></dt>
|
|
|
|
<DD>The Mac OS X group ID of the group associated with the
|
|
file or folder. Mac OS X typically maps group ID 99 to the group
|
|
named "unknown." There is no run time substitution of group IDs in Mac OS X.</dd>
|
|
|
|
<DT><CODE>adminFlags</CODE></dt>
|
|
|
|
<DD>BSD flags settable by the super-user only. This field
|
|
corresponds to bits 16 through 23 of the <code>st_flags</code> field of <a
|
|
href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/html/stat.2.html">
|
|
<code>struct stat</code></a> in Mac OS X. See the <a
|
|
href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/html/chflags.2.html">
|
|
manual page for <code>chflags(2)</code></a> for more information. The following table
|
|
gives the bit position in the <code>adminFlags</code> field and the name of the
|
|
corresponding mask used in the <code>st_flags</code> field.
|
|
<table>
|
|
<col width="35"> <col width="100"> <col width="200">
|
|
<tr><th style="text-align: left">Bit <th style="text-align: left"><code>st_flags</code> mask <th style="text-align: left">Meaning</tr>
|
|
<tr><td scope="row">0 <td>SF_ARCHIVED <td>File has been archived</tr>
|
|
<tr><td scope="row">1 <td>SF_IMMUTABLE <td>File may not be changed</tr>
|
|
<tr><td scope="row">2 <td>SF_APPEND <td>Writes to file may only append</tr>
|
|
</table>
|
|
</DD>
|
|
|
|
<DT><CODE>ownerFlags</CODE></dt>
|
|
|
|
<DD>BSD flags settable by the owner of the file or directory,
|
|
or by the super-user. This field corresponds to bits 0 through 7 of the
|
|
<code>st_flags</code> field of <a
|
|
href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/html/stat.2.html">
|
|
<code>struct stat</code></a> in Mac OS X. See the <a
|
|
href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/html/chflags.2.html">
|
|
manual page for <code>chflags(2)</code></a> for more information. The following table
|
|
gives the bit position in the <code>ownerFlags</code> field and the name of the
|
|
corresponding mask used in the <code>st_flags</code> field.
|
|
<table>
|
|
<col width="35"> <col width="100"> <col width="200">
|
|
<tr><th style="text-align: left">Bit <th style="text-align: left"><code>st_flags</code> mask <th style="text-align: left">Meaning</tr>
|
|
<tr><td scope="row">0 <td>UF_NODUMP <td>Do not dump (back up or archive) this file</tr>
|
|
<tr><td scope="row">1 <td>UF_IMMUTABLE <td>File may not be changed</tr>
|
|
<tr><td scope="row">2 <td>UF_APPEND <td>Writes to file may only append</tr>
|
|
<tr><td scope="row">3 <td>UF_OPAQUE <td>Directory is opaque (see <a href="#Union">below</a>)</tr>
|
|
</table>
|
|
</DD>
|
|
|
|
<DT><CODE>fileMode</CODE></dt>
|
|
|
|
<DD>BSD file type and mode bits. Note that the constants from the header
|
|
shown below are in octal (base eight), not hexadecimal.
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>#define S_ISUID 0004000 /* set user id on execution */
|
|
#define S_ISGID 0002000 /* set group id on execution */
|
|
#define S_ISTXT 0001000 /* sticky bit */
|
|
|
|
#define S_IRWXU 0000700 /* RWX mask for owner */
|
|
#define S_IRUSR 0000400 /* R for owner */
|
|
#define S_IWUSR 0000200 /* W for owner */
|
|
#define S_IXUSR 0000100 /* X for owner */
|
|
|
|
#define S_IRWXG 0000070 /* RWX mask for group */
|
|
#define S_IRGRP 0000040 /* R for group */
|
|
#define S_IWGRP 0000020 /* W for group */
|
|
#define S_IXGRP 0000010 /* X for group */
|
|
|
|
#define S_IRWXO 0000007 /* RWX mask for other */
|
|
#define S_IROTH 0000004 /* R for other */
|
|
#define S_IWOTH 0000002 /* W for other */
|
|
#define S_IXOTH 0000001 /* X for other */
|
|
|
|
#define S_IFMT 0170000 /* type of file mask */
|
|
#define S_IFIFO 0010000 /* named pipe (fifo) */
|
|
#define S_IFCHR 0020000 /* character special */
|
|
#define S_IFDIR 0040000 /* directory */
|
|
#define S_IFBLK 0060000 /* block special */
|
|
#define S_IFREG 0100000 /* regular */
|
|
#define S_IFLNK 0120000 /* symbolic link */
|
|
#define S_IFSOCK 0140000 /* socket */
|
|
#define S_IFWHT 0160000 /* whiteout */
|
|
</pre></TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
In some versions of Unix, the sticky bit, <code>S_ISTXT</code>, is used
|
|
to indicate that an executable file's code should remain in memory
|
|
after the executable finishes; this can help performance if the same
|
|
executable is used again soon. Mac OS X does not use this optimization.
|
|
If the sticky bit is set for a directory, then Mac OS X restricts
|
|
movement, deletion, and renaming of files in that directory.
|
|
Files may be removed or renamed only if the user has write access
|
|
to the directory; and is the owner of the file or the directory,
|
|
or is the super-user.
|
|
</DD>
|
|
|
|
<DT><CODE>special</CODE></dt>
|
|
|
|
<DD>This field is used only for certain special kinds of files.
|
|
For directories, and most files, this field is unused and
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>. When used,
|
|
this field is used as one of the following:</dd>
|
|
|
|
<DT><CODE>iNodeNum</CODE></dt>
|
|
|
|
<DD>For hard link files, this field contains the link reference number.
|
|
See the <a HREF = "#HardLinks">Hard Links</a> section for more
|
|
information.</dd>
|
|
|
|
<DT><CODE>linkCount</CODE></dt>
|
|
|
|
<DD>For indirect node files, this field contains the number of hard links
|
|
that point at this indirect node file. See the
|
|
<a HREF = "#HardLinks">Hard Links</a> section for more information.</dd>
|
|
|
|
<DT><CODE>rawDevice</CODE></dt>
|
|
|
|
<DD>For block and character special devices files (when the <code>S_IFMT</code>
|
|
field contains <code>S_IFCHR</code> or <code>S_IFBLK</code>), this field
|
|
contains the device number.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>WARNING:</B><BR>
|
|
Mac OS 8 and 9 treat the permissions as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</p>
|
|
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B><a NAME="Union"></a>Note:</B><BR>
|
|
The <code>S_IFWHT</code> and <code>UF_OPAQUE</code>
|
|
values are used when the file system is mounted as part
|
|
of a union mount. A union mount presents the
|
|
combination (union) of several file systems as a single
|
|
file system. Conceptually, these file systems are
|
|
layered, one on top of another. If a file or directory
|
|
appears in multiple layers, the one in the top most
|
|
layer is used. All changes are made to the top most
|
|
file system only; the others are read-only. To delete a
|
|
file or directory that appears in a layer other than the
|
|
top layer, a whiteout entry (file type
|
|
<code>S_IFWHT</code>) is created in the top layer. If a
|
|
directory that appears in a layer other than the top
|
|
layer is deleted and later recreated, the contents in
|
|
the lower layer must be hidden by setting the
|
|
<code>UF_OPAQUE</code> flag in the directory in the top
|
|
layer. Both <code>S_IFWHT</code> and
|
|
<code>UF_OPAQUE</code> hide corresponding names in lower
|
|
layers by preventing a union mount from accessing the
|
|
same file or directory name in a lower layer.
|
|
</P></TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
If the <code>S_IFMT</code> field (upper 4 bits) of the <CODE>fileMode</CODE>
|
|
field is zero, then Mac OS X assumes that the permissions structure is
|
|
uninitialized, and internally uses default values for all of the fields.
|
|
The default user and group IDs are 99, but can be changed at the time the
|
|
volume is mounted. This default <CODE>ownerID</CODE> is then subject to
|
|
substitution as described above.</p>
|
|
|
|
<p>This means that files created by Mac OS 8 and 9, or any other implementation
|
|
that sets the permissions fields to zeroes, will behave as if the
|
|
"ignore ownership" option is enabled for those files, even if "ignore
|
|
ownership" is disabled for the volume as a whole.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H4><a NAME="ForkDataStructure"></a>Fork Data Structure
|
|
</H4>
|
|
|
|
<P>HFS Plus maintains information about the contents of a
|
|
file using the <CODE>HFSPlusForkData</CODE> structure. Two
|
|
such structures -- one for the resource and one for the data
|
|
fork -- are stored in the catalog record for each user file.
|
|
In addition, the volume header contains a fork data
|
|
structure for each special file.</P>
|
|
|
|
<P>An unused extent descriptor in an extent record would
|
|
have both <CODE>startBlock</CODE> and
|
|
<CODE>blockCount</CODE> set to zero. For example, if a given
|
|
fork occupied three extents, then the last five extent
|
|
descriptors would be all zeroes.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusForkData {
|
|
UInt64 logicalSize;
|
|
UInt32 clumpSize;
|
|
UInt32 totalBlocks;
|
|
HFSPlusExtentRecord extents;
|
|
};
|
|
typedef struct HFSPlusForkData HFSPlusForkData;
|
|
|
|
typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[8];</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>logicalSize</CODE></dt>
|
|
|
|
<DD>The size, in bytes, of the valid data in the fork.</dd>
|
|
|
|
<DT><CODE>clumpSize</CODE></dt>
|
|
|
|
<DD>For <code>HFSPlusForkData</code> structures in the
|
|
<a href="#VolumeHeader">volume header</a>, this is the fork's
|
|
<a href="#Clump">clump size</a>, which is used in preference to the
|
|
default clump size in the <a href="#VolumeHeader">volume header</a>.<BR><BR>
|
|
For <code>HFSPlusForkData</code> structures in a <a href="#CatalogFileRecord">
|
|
catalog record</a>, this field was intended to store a per-fork
|
|
<a href="#Clump">clump size</a> to override the default clump size
|
|
in the <a href="#VolumeHeader">volume header</a>. However, Apple
|
|
implementations prior to Mac OS X version 10.3 ignored this field.
|
|
As of Mac OS X version 10.3, this field is used to keep track of the
|
|
number of blocks actually read from the fork. See the <a href="#HotFile">Hot
|
|
Files</a> section for more information.
|
|
</DD>
|
|
|
|
<DT><CODE>totalBlocks</CODE></dt>
|
|
|
|
<DD>The total number of allocation blocks used by all the
|
|
extents in this fork.</dd>
|
|
|
|
<DT><CODE>extents</CODE></dt>
|
|
|
|
<DD>An array of extent descriptors for the fork. This
|
|
array holds the first eight extent descriptors. If more
|
|
extent descriptors are required, they are stored in the
|
|
<a HREF = "#ExtentsOverflowFile">extents overflow file</a>.</dd>
|
|
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The <CODE>HFSPlusExtentRecord</CODE> is also the
|
|
data record used in the
|
|
<a HREF = "#ExtentsOverflowFile">extents overflow
|
|
file</a> (the extent record).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The <CODE>HFSPlusExtentDescriptor</CODE> structure is
|
|
used to hold information about a specific extent.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusExtentDescriptor {
|
|
UInt32 startBlock;
|
|
UInt32 blockCount;
|
|
};
|
|
typedef struct HFSPlusExtentDescriptor HFSPlusExtentDescriptor;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>startBlock</CODE></dt>
|
|
|
|
<DD>The first allocation block in the extent.</dd>
|
|
|
|
<DT><CODE>blockCount</CODE></dt>
|
|
|
|
<DD>The length, in allocation blocks, of the extent.</dd>
|
|
</DL>
|
|
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="VolumeHeader"></a>Volume Header</H2>
|
|
|
|
<P>Each HFS Plus volume contains a volume header
|
|
1024 bytes from the start of the volume. The volume
|
|
header -- analogous to the master directory block (MDB)
|
|
for HFS -- contains information about the volume as a whole,
|
|
including the location of other key structures in the volume.
|
|
The implementation is responsible for ensuring that this
|
|
structure is updated before the volume is unmounted.</P>
|
|
|
|
<P>A copy of the volume header, the alternate volume header,
|
|
is stored starting 1024 bytes before the end of the volume. The
|
|
implementation should only update this copy when the length
|
|
or location of one of the special files changes. The alternate volume
|
|
header is intended for use solely by disk repair utilities.
|
|
</P>
|
|
|
|
<P>The first 1024 bytes and the
|
|
last 512 bytes of the volume are
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The first 1024 bytes are reserved for use as boot
|
|
blocks; the traditional Mac OS Finder will write to them when
|
|
the System Folder changes. The boot block format is
|
|
outside the scope of this specification. It is
|
|
defined in
|
|
<a href="http://developer.apple.com/techpubs/mac/Files/Files-101.html">Inside
|
|
Macintosh: Files</a>.</P>
|
|
|
|
<P>The last 512 bytes were used during Apple's CPU
|
|
manufacturing process.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The allocation block (or blocks)
|
|
containing the first 1536 bytes (reserved space plus volume header)
|
|
are marked as used in the allocation file (see the
|
|
<a HREF = "#AllocationFile">Allocation File</a> section).
|
|
Also, in order to accommodate the alternate volume header and
|
|
the reserved space following it, the last allocation block (or two
|
|
allocation blocks, if the volume is formatted with 512-byte
|
|
allocation blocks) is also marked as used in the allocation
|
|
file.</P>
|
|
|
|
<a NAME="OddSizeVolumes"></a>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The alternate volume header is always stored at offset
|
|
1024 bytes from the end of the volume. If the
|
|
disk size is not an even multiple of the allocation
|
|
block size, this area may lie beyond the last
|
|
allocation block. However, the last allocation
|
|
block (or two allocation blocks for a volume
|
|
formatted with 512-byte allocation blocks) is still
|
|
reserved even if the alternate volume header is not
|
|
stored there.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The volume header is described by the
|
|
<CODE>HFSPlusVolumeHeader</CODE> type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusVolumeHeader {
|
|
UInt16 signature;
|
|
UInt16 version;
|
|
UInt32 attributes;
|
|
UInt32 lastMountedVersion;
|
|
UInt32 journalInfoBlock;
|
|
|
|
UInt32 createDate;
|
|
UInt32 modifyDate;
|
|
UInt32 backupDate;
|
|
UInt32 checkedDate;
|
|
|
|
UInt32 fileCount;
|
|
UInt32 folderCount;
|
|
|
|
UInt32 blockSize;
|
|
UInt32 totalBlocks;
|
|
UInt32 freeBlocks;
|
|
|
|
UInt32 nextAllocation;
|
|
UInt32 rsrcClumpSize;
|
|
UInt32 dataClumpSize;
|
|
HFSCatalogNodeID nextCatalogID;
|
|
|
|
UInt32 writeCount;
|
|
UInt64 encodingsBitmap;
|
|
|
|
UInt32 finderInfo[8];
|
|
|
|
HFSPlusForkData allocationFile;
|
|
HFSPlusForkData extentsFile;
|
|
HFSPlusForkData catalogFile;
|
|
HFSPlusForkData attributesFile;
|
|
HFSPlusForkData startupFile;
|
|
};
|
|
typedef struct HFSPlusVolumeHeader HFSPlusVolumeHeader;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>signature</CODE></dt>
|
|
|
|
<DD>The volume signature, which must be
|
|
<CODE>kHFSPlusSigWord</CODE> (<CODE>'H+'</CODE>) for an
|
|
HFS Plus volume, or <code>kHFSXSigWord</code> (<code>'HX'</code>)
|
|
for an <a href="#HFSX">HFSX</a> volume.</dd>
|
|
|
|
<DT><CODE>version</CODE></dt>
|
|
|
|
<DD>The version of the volume format, which is currently
|
|
4 (<CODE>kHFSPlusVersion</CODE>) for HFS Plus volumes, or
|
|
5 (<code>kHFSXVersion</code>) for <a href="#HFSX">HFSX</a>
|
|
volumes.</dd>
|
|
|
|
<DT><CODE>attributes</CODE></dt>
|
|
|
|
<DD>Volume attributes, as
|
|
<a HREF = "#VolumeAttributes">described below</a>.</dd>
|
|
|
|
<DT><CODE>lastMountedVersion</CODE></dt>
|
|
|
|
<DD>A value which uniquely identifies the implementation
|
|
that last mounted this volume for writing. This value can
|
|
be used by future implementations to detect volumes that
|
|
were last mounted by older implementations and check them
|
|
for deficiencies. Any code which modifies the on disk
|
|
structures must also set this field to a unique value which
|
|
identifies that code. Third-party implementations of HFS
|
|
Plus should place a
|
|
<a HREF = "http://developer.apple.com/dev/cftype/register.html">registered creator
|
|
code</a> in this field. The value used by Mac OS 8.1 to
|
|
9.2.2 is <CODE>'8.10'</CODE>.
|
|
The value used by Mac OS X is <CODE>'10.0'</CODE>. The
|
|
value used by a <a HREF = "#Journal">journaled</a> volume
|
|
(including <a href="#HFSX">HFSX</a>) in Mac OS X is <CODE>'HFSJ'</CODE>.
|
|
The value used by fsck_hfs on Mac OS X is <CODE>'fsck'</CODE>.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
It is very important for implementations (and
|
|
utilities that directly modify the volume!) to set
|
|
the <CODE>lastMountedVersion</CODE>. It is also
|
|
important to choose different values when
|
|
non-trivial changes are made to an implementation
|
|
or utility. If a bug is found in an implementation
|
|
or utility, and it sets the
|
|
<CODE>lastMountedVersion</CODE> correctly, it will
|
|
be much easier for other implementations and
|
|
utilities to detect and correct any problems.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<DL>
|
|
<DT><CODE>journalInfoBlock</CODE></dt>
|
|
|
|
<DD>The allocation block number of the allocation block
|
|
which contains the
|
|
<a HREF = "#JournalInfoBlock"><CODE>JournalInfoBlock</CODE></a>
|
|
for this volume's journal. This field is valid only if bit
|
|
<CODE>kHFSVolumeJournaledBit</CODE> is set in the
|
|
<CODE>attribute</CODE> field; otherwise, this field is
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</DD>
|
|
|
|
<DT><CODE>createDate</CODE></dt>
|
|
|
|
<DD>The date and time when the volume was created. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a
|
|
description of the format.</dd>
|
|
|
|
<DT><CODE>modifyDate</CODE></dt>
|
|
|
|
<DD>The date and time when the volume was last modified.
|
|
See <a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a
|
|
description of the format.</dd>
|
|
|
|
<DT><CODE>backupDate</CODE></dt>
|
|
|
|
<DD>The date and time when the volume was last backed up.
|
|
The volume format requires no special action on this
|
|
field; it simply defines the field for the benefit of
|
|
user programs. See <a HREF = "#HFSPlusDates">HFS Plus
|
|
Dates</a> for a description of the format.</dd>
|
|
|
|
<DT><CODE>checkedDate</CODE></dt>
|
|
|
|
<DD>The date and time when the volume was last checked
|
|
for consistency. Disk checking tools, such as Disk First
|
|
Aid, must set this when they perform a disk check. A disk
|
|
checking tool may use this date to perform periodic
|
|
checking of a volume.</dd>
|
|
|
|
<DT><CODE>fileCount</CODE></dt>
|
|
|
|
<DD>The total number of files on the volume. The
|
|
<CODE>fileCount</CODE> field does not include the special
|
|
files. It should equal the number of file records found
|
|
in the catalog file.</dd>
|
|
|
|
<DT><CODE>folderCount</CODE></dt>
|
|
|
|
<DD>The total number of folders on the volume.
|
|
The<CODE>folderCount</CODE> field does not include the
|
|
root folder. It should equal the number of folder records
|
|
in the catalog file, minus one (since the root folder has
|
|
a folder record in the catalog file).</dd>
|
|
|
|
<DT><CODE>blockSize</CODE></dt>
|
|
|
|
<DD>The allocation block size, in bytes.</dd>
|
|
|
|
<DT><CODE>totalBlocks</CODE></dt>
|
|
|
|
<DD>The total number of allocation blocks on the disk.
|
|
For a disk whose size is an even
|
|
multiple of the allocation block size, all areas
|
|
on the disk are included in an allocation block,
|
|
including the volume header and alternate volume header.
|
|
For a disk whose size is not an
|
|
even multiple of the allocation block size, only the
|
|
allocation blocks that will fit entirely on the disk are
|
|
counted here. The remaining space at the end of the
|
|
disk is not used by the volume format (except for storing
|
|
the alternate volume header, as described <a HREF = "#OddSizeVolumes">above</a>).</dd>
|
|
|
|
<DT><CODE>freeBlocks</CODE></dt>
|
|
|
|
<DD>The total number of unused allocation blocks on the
|
|
disk.</dd>
|
|
|
|
<DT><CODE>nextAllocation</CODE></dt>
|
|
|
|
<DD>Start of next allocation search. The
|
|
<CODE>nextAllocation</CODE> field is used by Mac OS as a
|
|
hint for where to start searching for free allocation blocks
|
|
when allocating space for a file. It contains the allocation
|
|
block number where the search should begin. An
|
|
implementation that doesn't want to use this kind of hint
|
|
can just treat the field as <a HREF =
|
|
"#ReservedAndPadFields">reserved</a>. [Implementation
|
|
details: traditional Mac OS implementations typically
|
|
set it to the first allocation block of the extent most
|
|
recently allocated. It is not set to the allocation block
|
|
immediately following the most recently allocated extent
|
|
because of the likelihood of that extent being shortened
|
|
when the file is closed (since a whole <a
|
|
href="#Clump">clump </a> may have been allocated but not
|
|
actually used).] See <a HREF = "#AllocationFile">Allocation
|
|
File</a> section for details.</dd>
|
|
|
|
<DT><CODE>rsrcClumpSize</CODE></dt>
|
|
|
|
<DD>The default <a href="#Clump">clump
|
|
size</a> for resource forks, in bytes. This is a hint to the
|
|
implementation as to the size by which a growing file should
|
|
be extended. All Apple implementations to date ignore the
|
|
<code>rsrcClumpSize</code> and use
|
|
<code>dataClumpSize</code> for both data and resource
|
|
forks.</dd>
|
|
|
|
<DT><CODE>dataClumpSize</CODE></dt>
|
|
|
|
<DD>The default <a href="#Clump">clump
|
|
size</a> for data forks, in bytes. This is a hint to the
|
|
implementation as to the size by which a growing file should
|
|
be extended. All Apple implementations to date ignore the
|
|
<code>rsrcClumpSize</code> and use
|
|
<code>dataClumpSize</code> for both data and resource
|
|
forks.</dd>
|
|
|
|
<DT><CODE>nextCatalogID</CODE></dt>
|
|
|
|
<DD>The next unused catalog ID. See
|
|
<a HREF = "#CatalogFile">Catalog File</a> for a description
|
|
of catalog IDs.</dd>
|
|
|
|
<DT><CODE>writeCount</CODE></dt>
|
|
|
|
<DD>This field is incremented every time a volume is
|
|
mounted. This allows an implementation to keep the volume
|
|
mounted even when the media is ejected (or otherwise
|
|
inaccessible). When the media is re-inserted, the
|
|
implementation can check this field to determine when the
|
|
media has been changed while it was ejected. It is very
|
|
important that an implementation or utility change the
|
|
<CODE>writeCount</CODE> field if it modifies the volume's
|
|
structures directly. This is particularly important if it
|
|
adds or deletes items on the volume.</dd>
|
|
|
|
<DT><CODE>encodingsBitmap</CODE></dt>
|
|
|
|
<DD>This field keeps track of the text encodings used in
|
|
the file and folder names on the volume. This bitmap
|
|
enables some performance optimizations for
|
|
implementations that don't use Unicode names directly.
|
|
See the <a HREF = "#TextEncodings">Text Encoding</a>
|
|
sections for details.</dd>
|
|
|
|
<DT><a NAME="VolumeFinderInfo"></a><CODE>finderInfo</CODE></dt>
|
|
|
|
<DD>
|
|
This array of 32-bit items contains information used by the Mac OS
|
|
Finder, and the system software boot process.<BR><BR>
|
|
|
|
<CODE>finderInfo[0]</CODE> contains the directory ID of the
|
|
directory containing the bootable system (for example, the
|
|
System Folder in Mac OS 8 or 9, or <CODE>/System/Library/CoreServices</CODE>
|
|
in Mac OS X). It is zero if there is no bootable system on the volume.
|
|
This value is typically equal to either <CODE>finderInfo[3]</CODE>
|
|
or <CODE>finderInfo[5]</CODE>.<BR><BR>
|
|
|
|
<CODE>finderInfo[1]</CODE> contains the parent directory ID of
|
|
the startup application (for example, Finder), or zero if the volume
|
|
is not bootable.<BR><BR>
|
|
|
|
<CODE>finderInfo[2]</CODE> contains the directory ID of a directory
|
|
whose window should be displayed in the Finder when the volume is
|
|
mounted, or zero if no directory window should be opened. In
|
|
traditional Mac OS, this is the first in a linked list of windows
|
|
to open; the <code>frOpenChain</code> field of the directory's
|
|
<a href="#FinderInfo">Finder Info</a> contains the next directory ID
|
|
in the list. The open window list is deprecated. The Mac OS X
|
|
Finder will open this directory's window, but ignores the rest
|
|
of the open window list. The Mac OS X Finder does not modify
|
|
this field.<BR><BR>
|
|
|
|
<CODE>finderInfo[3]</CODE> contains the directory ID of a bootable
|
|
Mac OS 8 or 9 System Folder, or zero if there isn't one.<BR><BR>
|
|
|
|
<CODE>finderInfo[4]</CODE> is <a HREF = "#ReservedAndPadFields">reserved</a>.<BR><BR>
|
|
|
|
<CODE>finderInfo[5]</CODE> contains the directory ID of a bootable
|
|
Mac OS X system (the <CODE>/System/Library/CoreServices</CODE>
|
|
directory), or zero if there is no bootable Mac OS X system on
|
|
the volume.<BR><BR>
|
|
|
|
<CODE>finderInfo[6]</CODE> and <CODE>finderInfo[7]</CODE> are
|
|
used by Mac OS X to contain a 64-bit unique volume identifier.
|
|
One use of this identifier is for tracking whether a given
|
|
volume's ownership (user ID) information should be honored.
|
|
These elements may be zero if no such identifier has been
|
|
created for the volume.</dd>
|
|
|
|
<DT><CODE>allocationFile</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the
|
|
allocation file. See <a HREF = "#ForkDataStructure">Fork
|
|
Data Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
|
|
<DT><CODE>extentsFile</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the
|
|
extents file. See <a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
|
|
<DT><CODE>catalogFile</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the
|
|
catalog file. See <a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
|
|
<DT><CODE>attributesFile</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the
|
|
attributes file. See <a HREF = "#ForkDataStructure">Fork
|
|
Data Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
|
|
<DT><CODE>startupFile</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the
|
|
startup file. See <a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
</DL>
|
|
|
|
<H3><a NAME="VolumeAttributes"></a>Volume Attributes</H3>
|
|
|
|
<P>The <CODE>attributes</CODE> field of a volume header is
|
|
treated as a set of one-bit flags. The definition of the
|
|
bits is given by the constants listed below.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
/* Bits 0-6 are reserved */
|
|
kHFSVolumeHardwareLockBit = 7,
|
|
kHFSVolumeUnmountedBit = 8,
|
|
kHFSVolumeSparedBlocksBit = 9,
|
|
kHFSVolumeNoCacheRequiredBit = 10,
|
|
kHFSBootVolumeInconsistentBit = 11,
|
|
kHFSCatalogNodeIDsReusedBit = 12,
|
|
kHFSVolumeJournaledBit = 13,
|
|
/* Bit 14 is reserved */
|
|
kHFSVolumeSoftwareLockBit = 15
|
|
/* Bits 16-31 are reserved */
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The bits have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT>bits 0-7</dt>
|
|
|
|
<DD>An implementation must treat these as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> fields.</dd>
|
|
|
|
<DT><CODE>kHFSVolumeUnmountedBit</CODE> (bit 8)</dt>
|
|
|
|
<DD>This bit is set if the volume was correctly flushed
|
|
before being unmounted or ejected. An implementation must
|
|
clear this bit on the media when it mounts a volume for
|
|
writing. An implementation must set this bit on the media
|
|
as the last step of unmounting a writable volume, after
|
|
all other volume information has been flushed. If an
|
|
implementation is asked to mount a volume where this bit
|
|
is clear, it must assume the volume is inconsistent, and
|
|
do appropriate
|
|
<a HREF = "#VolumeConsistencyChecks">consistency
|
|
checking</a> before using the volume.</dd>
|
|
|
|
<DT><CODE>kHFSVolumeSparedBlocksBit</CODE> (bit 9)</dt>
|
|
|
|
<DD>This bit is set if there are any records in the
|
|
extents overflow file for bad blocks (belonging to file
|
|
ID <CODE>kHFSBadBlockFileID</CODE>). See
|
|
<a HREF = "#BadBlockFile">Bad Block File</a> for details.</dd>
|
|
|
|
<DT><CODE>kHFSVolumeNoCacheRequiredBit</CODE> (bit 10)</dt>
|
|
|
|
<DD>This bit is set if the blocks from this volume should
|
|
not be cached. For example, a RAM or ROM disk is actually
|
|
stored in memory, so using additional memory to cache the
|
|
volume's contents would be wasteful.</dd>
|
|
|
|
<DT><CODE>kHFSBootVolumeInconsistentBit</CODE> (bit 11)</dt>
|
|
|
|
<DD>This bit is similar to
|
|
<CODE>kHFSVolumeUnmountedBit</CODE>, but inverted in
|
|
meaning. An implementation must set this bit on the media
|
|
when it mounts a volume for writing. An implementation
|
|
must clear this bit on the media as the last step of
|
|
unmounting a writable volume, after all other volume
|
|
information has been flushed. If an implementation is
|
|
asked to mount a volume where this bit is set, it must
|
|
assume the volume is inconsistent, and do appropriate
|
|
<a HREF = "#VolumeConsistencyChecks">consistency
|
|
checking</a> before using the volume.</dd>
|
|
|
|
<DT><CODE>kHFSCatalogNodeIDsReusedBit</CODE> (bit 12)</dt>
|
|
|
|
<DD>This bit is set when the <CODE>nextCatalogID</CODE>
|
|
field overflows 32 bits, forcing smaller <a
|
|
href="#CNID">catalog node IDs</a> to be reused. When this
|
|
bit is set, it is common (and not an error) for catalog
|
|
records to exist with IDs greater than or equal to
|
|
<CODE>nextCatalogID</CODE>. If this bit is set, you must
|
|
ensure that IDs assigned to newly created catalog records do
|
|
not conflict with IDs used by existing records.</dd>
|
|
|
|
<DT><CODE>kHFSVolumeJournaledBit</CODE> (bit 13)</dt>
|
|
|
|
<DD>If this bit is set, the volume has a <a HREF = "#Journal">
|
|
journal</a>, which can be located using the <CODE>journalInfoBlock</CODE>
|
|
field of the Volume Header.</dd>
|
|
|
|
<DT>bit 14</dt>
|
|
|
|
<DD>An implementation must treat this bit as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</dd>
|
|
|
|
<DT><CODE>kHFSVolumeSoftwareLockBit</CODE> (bit 15)</dt>
|
|
|
|
<DD>This bit is set if the volume is write-protected due
|
|
to a software setting. Any implementations must refuse to
|
|
write to a volume with this bit set. This flag is
|
|
especially useful for write-protecting a volume on a
|
|
media that cannot be write-protected otherwise, or for
|
|
protecting an individual partition on a partitioned
|
|
device.</dd>
|
|
|
|
<DT>bits 16-31</dt>
|
|
|
|
<DD>An implementation must treat these bits as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Mac OS X versions 10.0 to 10.3 don't properly honor
|
|
<CODE>kHFSVolumeSoftwareLockBit</CODE>. They incorrectly
|
|
allow such volumes to be modified. This bug is expected
|
|
to be fixed in a future version of Mac OS X. (r. 3507614)</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
An implementation may keep a copy of the attributes
|
|
in memory and use bits 0-7 for its own runtime
|
|
flags. As an example, Mac OS uses bit 7,
|
|
<CODE>kHFSVolumeHardwareLockBit</CODE>, to indicate
|
|
that the volume is write-protected due to some
|
|
hardware setting.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The existence of two volume consistency bits
|
|
(<CODE>kHFSVolumeUnmountedBit</CODE> and
|
|
<CODE>kHFSBootVolumeInconsistentBit</CODE>)
|
|
deserves an explanation. Macintosh ROMs check the
|
|
consistency of a boot volume if
|
|
<CODE>kHFSVolumeUnmountedBit</CODE> is clear. The
|
|
ROM-based check is very slow, annoyingly so. This
|
|
checking code was significantly optimized in Mac OS
|
|
7.6. To prevent the ROM check from being used, Mac
|
|
OS 7.6 (and higher) leaves the original consistency
|
|
check bit (<CODE>kHFSVolumeUnmountedBit</CODE>) set
|
|
at all times. Instead, an alternative flag
|
|
(<CODE>kHFSBootVolumeInconsistentBit</CODE>) is
|
|
used to signal that the disk needs a consistency
|
|
check.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
For the boot volume, the
|
|
<CODE>kHFSBootVolumeInconsistentBit</CODE> should
|
|
be used as described but
|
|
<CODE>kHFSVolumeUnmountedBit</CODE> should remain
|
|
set; for all other volumes, use the
|
|
<CODE>kHFSVolumeUnmountedBit</CODE> as described
|
|
but keep the
|
|
<CODE>kHFSBootVolumeInconsistentBit</CODE> clear.
|
|
This is an optimization that prevents the Mac OS
|
|
ROM from doing a very slow consistency check when
|
|
the boot volume is mounted since it only checks
|
|
<CODE>kHFSVolumeUnmountedBit</CODE>, and won't do a
|
|
consistency check; later on, the File Manager will
|
|
see the <CODE>kHFSBootVolumeInconsistentBit</CODE>
|
|
set and do a better, faster consistency check. (It
|
|
would be OK to always use both bits at the expense
|
|
of a slower Mac OS boot.)</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="BTrees"></a>B-Trees</H2>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
For a practical description of the algorithms used
|
|
to maintain a B-tree, see<CITE>Algorithms in
|
|
C</CITE>, Robert Sedgewick, Addison-Wesley, 1992.
|
|
ISBN: 0201514257.</P>
|
|
|
|
<P>Many textbooks describe B-trees in which an
|
|
index node contains N keys and N+1 pointers, and
|
|
where keys less than key #X lie in the subtree
|
|
pointed to by pointer #X, and keys greater than key
|
|
#X lie in the subtree pointed to by pointer #X+1.
|
|
(The B-tree implementor defines whether to use
|
|
pointer #X or #X+1 for equal keys.)</P>
|
|
|
|
<P>HFS and HFS Plus are slightly different; in a
|
|
given subtree, there are no keys less than the
|
|
first key of that subtree's root node.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<P>This section describes the B-tree structure used for the
|
|
catalog, extents overflow, and attributes files. A B-tree is
|
|
stored in file data fork. Each B-tree has a
|
|
<CODE><a HREF = "#ForkDataStructure">HFSPlusForkData</a></CODE>
|
|
structure in the volume header that describes the size and
|
|
initial extents of that data fork.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Special files do not have a resource fork because
|
|
there is no place to store its
|
|
<CODE>HFSPlusForkData</CODE> in the volume header.
|
|
However, it's still important that the B-tree is in
|
|
the data fork because the fork is part of the key
|
|
used to store B-tree extents in the extents
|
|
overflow file.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>A B-tree file is divided up into fixed-size <B>nodes</B>,
|
|
each of which contains <B>records</B>, which consist of a
|
|
<B>key</B> and some data. The purpose of the B-tree is to
|
|
efficiently map a key into its corresponding data. To
|
|
achieve this, keys must be ordered, that is, there must be a
|
|
well-defined way to decide whether one key is smaller than,
|
|
equal to, or larger than another key.</P>
|
|
|
|
<P>The <B>node size</B> (which is expressed in bytes) must
|
|
be power of two, from 512 through 32,768, inclusive. The
|
|
node size of a B-tree is determined when the B-tree is
|
|
created. The logical length of a B-tree file is just the
|
|
number of nodes times the node size.</P>
|
|
|
|
<P>There are four kinds of nodes.</P>
|
|
|
|
<UL>
|
|
<li>Each B-tree contains a single <B>header node</B>. The
|
|
header node is always the first node in the B-tree. It
|
|
contains the information needed to find other any other
|
|
node in the tree.</li>
|
|
|
|
<li><B>Map nodes</B> contain <B>map records</B>, which
|
|
hold any allocation data (a bitmap that describes the
|
|
free nodes in the B-tree) that overflows the map record
|
|
in the header node.</li>
|
|
|
|
<li><B>Index nodes</B> hold <B>pointer records</B> that
|
|
determine the structure of the B-tree.</li>
|
|
|
|
<li><B>Leaf nodes</B> hold <B>data records</B> that
|
|
contain the data associated with a given key. The key for
|
|
each data record must be unique.</li>
|
|
</UL>
|
|
|
|
<P>All nodes share a common structure, described in the next
|
|
section.</P>
|
|
|
|
<H3>Node Structure</H3>
|
|
|
|
<P>Nodes are indicated by number. The node's number can be
|
|
calculated by dividing its offset into the file by the node
|
|
size. Each node has the same general structure, consisting
|
|
of three main parts: a node descriptor at the beginning of
|
|
the node, a list of record offsets at the end of the node,
|
|
and a list of records. This structure is depicted in Figure
|
|
2.</P>
|
|
|
|
|
|
|
|
<CENTER><img src="images/tn1150_002.gif" alt="Structure of a node" width=230 height=312 align=bottom>
|
|
<P><B>Figure 2</B>. The structure of a node.</P></center>
|
|
|
|
<P>The <B>node descriptor</B> contains basic information
|
|
about the node as well as forward and backward links to
|
|
other nodes. The <CODE>BTNodeDescriptor</CODE> data type
|
|
describes this structure.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct BTNodeDescriptor {
|
|
UInt32 fLink;
|
|
UInt32 bLink;
|
|
SInt8 kind;
|
|
UInt8 height;
|
|
UInt16 numRecords;
|
|
UInt16 reserved;
|
|
};
|
|
typedef struct BTNodeDescriptor BTNodeDescriptor;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>fLink</CODE></dt>
|
|
|
|
<DD>The node number of the next node of this type, or 0
|
|
if this is the last node.</dd>
|
|
|
|
<DT><CODE>bLink</CODE></dt>
|
|
|
|
<DD>The node number of the previous node of this type, or
|
|
0 if this is the first node.</dd>
|
|
|
|
<DT><CODE>kind</CODE></dt>
|
|
|
|
<DD>The type of this node. There are four node kinds,
|
|
defined by the constants listed below.</dd>
|
|
|
|
<DT><CODE>height</CODE></dt>
|
|
|
|
<DD>The level, or depth, of this node in the B-tree
|
|
hierarchy. For the header node, this field must be zero.
|
|
For leaf nodes, this field must be one. For index nodes,
|
|
this field is one greater than the height of the child
|
|
nodes it points to. The height of a map node is zero,
|
|
just like for a header node. (Think of map nodes as
|
|
extensions of the map record in the header node.)</dd>
|
|
|
|
<DT><CODE>numRecords</CODE></dt>
|
|
|
|
<DD>The number of records contained in this node.</dd>
|
|
|
|
<DT><CODE>reserved</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
</DL>
|
|
|
|
<P>A node descriptor is always 14 (which is
|
|
<CODE>sizeof(BTNodeDescriptor)</CODE>) bytes long, so the
|
|
<B>list of records</B> contained in a node always starts 14
|
|
bytes from the start of the node. The size of each record
|
|
can vary, depending on the record's type and the amount of
|
|
information it contains.</P>
|
|
|
|
<P>The records are accessed using the <B>list of record
|
|
offsets</B> at the end of the node. Each entry in this list
|
|
is a <CODE>UInt16</CODE> which contains the offset, in
|
|
bytes, from the start of the node to the start of the
|
|
record. The offsets are stored in reverse order, with the
|
|
offset for the first record in the last two bytes of the
|
|
node, the offset for the second record is in the previous
|
|
two bytes, and so on. Since the first record is always at
|
|
offset 14, the last two bytes of the node contain the value
|
|
14.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The list of record offsets always contains one more
|
|
entry than there is records in the node. This entry
|
|
contains the offset to the first byte of free space
|
|
in the node, and thus indicates the size of the
|
|
last record in the node. If there is no free space
|
|
in the node, the entry contains its own byte offset
|
|
from the start of the node.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The <CODE>kind</CODE> field of the node descriptor
|
|
describes the type of a node, which indicates what kinds of
|
|
records it contains and, therefore, its purpose in the
|
|
B-tree hierarchy. There are four kinds of node types given
|
|
by the following constants:</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kBTLeafNode = -1,
|
|
kBTIndexNode = 0,
|
|
kBTHeaderNode = 1,
|
|
kBTMapNode = 2
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>It's important to realise that the B-tree node type
|
|
determines the type of records found in the node. Leaf nodes
|
|
always contain data records. Index nodes always contain
|
|
pointer records. Map nodes always contain map records. The
|
|
header node always contains a header record, a reserved
|
|
record, and a map record. The four node types and their
|
|
corresponding records are described in the subsequent
|
|
sections.</P>
|
|
|
|
<H3><a NAME="HeaderNode"></a>Header Nodes</H3>
|
|
|
|
<P>The first node (node 0) in every B-tree file
|
|
is a header node, which contains essential information about
|
|
the entire B-tree file. There are three records in the header
|
|
node. The first record is the B-tree header record. The second
|
|
record is the user data record and is always 128 bytes long.
|
|
The last record is the B-tree map record; it occupies all of
|
|
the remaining space between the user data record and the record
|
|
offsets. The header node is shown in Figure 3.</P>
|
|
|
|
<CENTER>
|
|
<img src="images/tn1150_003.png" alt="Header node" width=198 height=306 align=bottom>
|
|
<P><B>Figure 3</B> Header node structure</P>
|
|
</center>
|
|
|
|
<P>The <CODE>fLink</CODE> field of the header node's node
|
|
descriptor contains the node number of the first map node,
|
|
or 0 if there are no map nodes. The <CODE>bLink</CODE> field
|
|
of the header node's node descriptor must be set to zero.
|
|
</P>
|
|
|
|
<H4><a NAME="HeaderRecord"></a>Header Record</H4>
|
|
|
|
<P>The B-tree <B>header record</B> contains general
|
|
information about the B-tree such as its size, maximum key
|
|
length, and the location of the first and last leaf nodes.
|
|
The data type <CODE>BTHeaderRec</CODE> describes the
|
|
structure of a header record.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct BTHeaderRec {
|
|
UInt16 treeDepth;
|
|
UInt32 rootNode;
|
|
UInt32 leafRecords;
|
|
UInt32 firstLeafNode;
|
|
UInt32 lastLeafNode;
|
|
UInt16 nodeSize;
|
|
UInt16 maxKeyLength;
|
|
UInt32 totalNodes;
|
|
UInt32 freeNodes;
|
|
UInt16 reserved1;
|
|
UInt32 clumpSize; // misaligned
|
|
UInt8 btreeType;
|
|
UInt8 keyCompareType;
|
|
UInt32 attributes; // long aligned again
|
|
UInt32 reserved3[16];
|
|
};
|
|
typedef struct BTHeaderRec BTHeaderRec;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The root node can be a leaf node (in the case where
|
|
there is only a single leaf node, and therefore no
|
|
index nodes, as might happen with the catalog file
|
|
on a newly initialized volume). If a tree has no
|
|
leaf nodes (like the extents overflow file on a
|
|
newly initialized volume), the
|
|
<CODE>firstLeafNode</CODE>,
|
|
<CODE>lastLeafNode</CODE>, and
|
|
<CODE>rootNode</CODE> fields will all be zero. If
|
|
there is only one leaf node (as may be the case
|
|
with the catalog file on a newly initialized
|
|
volume), <CODE>firstLeafNode</CODE>,
|
|
<CODE>lastLeafNode</CODE>, and
|
|
<CODE>rootNode</CODE> will all have the same value
|
|
(i.e., the node number of the sole leaf node). The
|
|
<CODE>firstLeafNode</CODE> and
|
|
<CODE>lastLeafNode</CODE> fields just make it easy
|
|
to walk through all the leaf nodes by just
|
|
following <CODE>fLink/bLink</CODE> fields.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>treeDepth</CODE></dt>
|
|
|
|
<DD>The current depth of the B-tree. Always equal to the
|
|
<CODE>height</CODE> field of the root node.</dd>
|
|
|
|
<DT><CODE>rootNode</CODE></dt>
|
|
|
|
<DD>The node number of the root node, the index node that
|
|
acts as the root of the B-tree. See
|
|
<a HREF = "#IndexNodes">Index Nodes</a> for details. There
|
|
is a possibility that the <CODE>rootNode</CODE> is a leaf
|
|
node. See <a href="http://developer.apple.com/techpubs/mac/Files/">Inside
|
|
Macintosh: Files</a>, pp. 2-69 for details.</dd>
|
|
|
|
<DT><CODE>leafRecords</CODE></dt>
|
|
|
|
<DD>The total number of records contained in all of the
|
|
leaf nodes.</dd>
|
|
|
|
<DT><CODE>firstLeafNode</CODE></dt>
|
|
|
|
<DD>The node number of the first leaf node. This may be
|
|
zero if there are no leaf nodes.</dd>
|
|
|
|
<DT><CODE>lastLeafNode</CODE></dt>
|
|
|
|
<DD>The node number of the last leaf node. This may be
|
|
zero if there are no leaf nodes.</dd>
|
|
|
|
<DT><CODE>nodeSize</CODE></dt>
|
|
|
|
<DD>The size, in bytes, of a node. This is a power of
|
|
two, from 512 through 32,768, inclusive.</dd>
|
|
|
|
<DT><CODE>maxKeyLength</CODE></dt>
|
|
|
|
<DD>The maximum length of a key in an index or leaf node.
|
|
HFSVolumes.h has the <CODE>maxKeyLength</CODE> values for
|
|
the catalog and extents files for both HFS and HFS Plus
|
|
(<CODE>kHFSPlusExtentKeyMaximumLength</CODE>,
|
|
<CODE>kHFSExtentKeyMaximumLength</CODE>,
|
|
<CODE>kHFSPlusCatalogKeyMaximumLength</CODE>,
|
|
<CODE>kHFSCatalogKeyMaximumLength</CODE>). The maximum
|
|
key length for the attributes B-tree will probably be a
|
|
little larger than for the catalog file. In general,
|
|
<CODE>maxKeyLength</CODE> has to be small enough
|
|
(compared to <CODE>nodeSize</CODE>) so that a single node
|
|
can fit two keys of maximum size plus the node descriptor
|
|
and offsets.</dd>
|
|
|
|
<DT><CODE>totalNodes</CODE></dt>
|
|
|
|
<DD>The total number of nodes (be they free or used) in
|
|
the B-tree. The length of the B-tree file is this value
|
|
times the <CODE>nodeSize</CODE>.</dd>
|
|
|
|
<DT><CODE>freeNodes</CODE></dt>
|
|
|
|
<DD>The number of unused nodes in the B-tree.</dd>
|
|
|
|
<DT><CODE>reserved1</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
|
|
<DT><CODE>clumpSize</CODE></dt>
|
|
|
|
<DD>Ignored for HFS Plus B-trees. The
|
|
<CODE>clumpSize</CODE> field of the
|
|
<CODE><a HREF = "#ForkDataStructure">HFSPlusForkData</a></CODE>
|
|
record is used instead. For maximum compatibility, an
|
|
implementation should probably set the
|
|
<CODE>clumpSize</CODE> in the node descriptor to the same
|
|
value as the <CODE>clumpSize</CODE> in the
|
|
<CODE>HFSPlusForkData</CODE> when initializing a volume.
|
|
Otherwise, it should treat the header records's
|
|
<CODE>clumpSize</CODE> as reserved.</dd>
|
|
|
|
<DT><CODE>btreeType</CODE></dt>
|
|
|
|
<DD>The value stored in this field is of type
|
|
<code>BTreeTypes</code>:
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum BTreeTypes{
|
|
kHFSBTreeType = 0, // control file
|
|
kUserBTreeType = 128, // user btree type starts from 128
|
|
kReservedBTreeType = 255
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
<P>This field must be equal to <code>kHFSBTreeType</code>
|
|
for the <a href="#CatalogFile">catalog</a>, <a
|
|
href="#ExtentsOverflowFile">extents</a>, and <a
|
|
href="#AttributesFile">attributes</a> B-trees. This field
|
|
must be equal to <code>kUserBTreeType</code> for the <a
|
|
href="#HotFile">hot file</a> B-tree. Historically, values of
|
|
1 to 127 and <code>kReservedBTreeType</code> were used in
|
|
B-trees used by system software in Mac OS 9 and earlier.</P>
|
|
</dd>
|
|
|
|
<DT><CODE>keyCompareType</CODE></dt>
|
|
|
|
<DD>For <a href="#HFSX">HFSX</a> volumes, this field in the
|
|
catalog B-tree header defines the ordering of the keys (whether
|
|
the volume is case-sensitive or case-insensitive). In all
|
|
other cases, an implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.
|
|
|
|
<table>
|
|
<tr><th>Constant name <th>Value <th>Meaning</tr>
|
|
<tr><td scope="row"><CODE>kHFSCaseFolding</CODE> <td><CODE>0xCF</CODE> <td>Case folding (case-insensitive)</tr>
|
|
<tr><td scope="row"><CODE>kHFSBinaryCompare</CODE> <td><CODE>0xBC</CODE> <td>Binary compare (case-sensitive)</tr>
|
|
</table>
|
|
</dd>
|
|
|
|
<DT><CODE>attributes</CODE></dt>
|
|
|
|
<DD>A set of bits used to describe various attributes of
|
|
the B-tree. The meaning of these bits is given below.</dd>
|
|
|
|
<DT><CODE>reserved3</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
</DL>
|
|
|
|
<P>The following constants define the various bits that may
|
|
be set in the <CODE>attributes</CODE> field of the header
|
|
record.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kBTBadCloseMask = 0x00000001,
|
|
kBTBigKeysMask = 0x00000002,
|
|
kBTVariableIndexKeysMask = 0x00000004
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The bits have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>kBTBadCloseMask</CODE></dt>
|
|
|
|
<DD>This bit indicates that the B-tree was not closed
|
|
properly and should be checked for consistency. This bit
|
|
is not used for HFS Plus B-trees. An implementation must
|
|
treat this as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</dd>
|
|
|
|
<DT><CODE>kBTBigKeysMask</CODE></dt>
|
|
|
|
<DD>If this bit is set, the <CODE>keyLength</CODE> field
|
|
of the keys in index and leaf nodes is
|
|
<CODE>UInt16</CODE>; otherwise, it is a
|
|
<CODE>UInt8</CODE>. This bit must be set for all HFS Plus
|
|
B-trees.</dd>
|
|
|
|
<DT><CODE>kBTVariableIndexKeysMask</CODE></dt>
|
|
|
|
<DD>If this bit is set, the keys in index nodes occupy
|
|
the number of bytes indicated by their
|
|
<CODE>keyLength</CODE> field; otherwise, the keys in
|
|
index nodes always occupy <CODE>maxKeyLength</CODE>
|
|
bytes. This bit must be set for the HFS Plus Catalog
|
|
B-tree, and cleared for the HFS Plus Extents B-tree.</dd>
|
|
</DL>
|
|
|
|
<P>Bits not specified here must be treated as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</P>
|
|
|
|
<H4><a NAME="UserDataRecord"></a>User Data Record</H4>
|
|
|
|
<P>The second record in a header node is always 128 bytes long.
|
|
It provides a small space to store information associated with
|
|
a B-tree.</P>
|
|
|
|
<P>In the HFS Plus <a href="#CatalogFile">catalog</a>, <a
|
|
href="#ExtentsOverflowFile">extents</a>, and <a
|
|
href="#AttributesFile">attributes</a> B-trees, this record is
|
|
unused and <a HREF = "#ReservedAndPadFields">reserved</a>. In
|
|
the HFS Plus <a href="#HotFile">hot file</a> B-tree, this
|
|
record contains general information about the hot file
|
|
recording process.</P>
|
|
|
|
<H4>Map Record</H4>
|
|
|
|
<P>The remaining space in the header node is occupied by a
|
|
third record, the <B>map record</B>. It is a bitmap that
|
|
indicates which nodes in the B-tree are used and which are
|
|
free. The bits are interpreted in the same way as the bits
|
|
in the <a HREF = "#AllocationFile">allocation file</a>.</P>
|
|
|
|
<P>All tolled, the node descriptor, header record, reserved
|
|
record, and record offsets occupy 256 bytes of the header
|
|
node. So the size of the map record (in bytes) is
|
|
<CODE>nodeSize</CODE> minus 256. If there are more nodes in
|
|
the B-tree than can be represented by the map record in the
|
|
header node, map nodes are used to store additional
|
|
allocation data.</P>
|
|
|
|
<H3>Map Nodes</H3>
|
|
|
|
<P>If the map record of the header node is not large enough
|
|
to represent all of the nodes in the B-tree, <B>map
|
|
nodes</B> are used to store the remaining allocation data.
|
|
In this case, the <CODE>fLink</CODE> field of the header
|
|
node's node descriptor contains the node number of the first
|
|
map node.</P>
|
|
|
|
<P>A map node consists of the node descriptor and a single
|
|
map record. The map record is a continuation of the map
|
|
record contained in the header node. The size of the map
|
|
record is the size of the node, minus the size of the node
|
|
descriptor (14 bytes), minus the size of two offsets (4
|
|
bytes), minus two bytes of free space. That is, the size of
|
|
the map record is the size of the node minus 20 bytes; this
|
|
keeps the length of the map record an even multiple of 4
|
|
bytes. Note that the start of the map record is <EM>not</EM>
|
|
aligned to a 4-byte boundary: it starts immediately after
|
|
the node descriptor (at an offset of 14 bytes).</P>
|
|
|
|
<P>The B-tree uses as many map nodes as needed to provide
|
|
allocation data for all of the nodes in the B-tree. The map
|
|
nodes are chained through the <CODE>fLink</CODE> fields of
|
|
their node descriptors, starting with the header node. The
|
|
<CODE>fLink</CODE> field of the last map node's node
|
|
descriptor is zero. The <CODE>bLink</CODE> field is not used
|
|
for map nodes and must be set to zero for all map nodes.
|
|
</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Not using the <CODE>bLink</CODE> field is
|
|
consistent with the HFS volume format, but not
|
|
really consistent with the overall design.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3><a NAME="KeyedRecords"></a>Keyed Records</H3>
|
|
|
|
<P>The records in index and leaf nodes share a common
|
|
structure. They contain a <CODE>keyLength</CODE>, followed
|
|
by the <B>key</B> itself, followed by the record data.</P>
|
|
|
|
<P>The first part of the record, <CODE>keyLength</CODE>, is
|
|
either a <CODE>UInt8</CODE> or a <CODE>UInt16</CODE>,
|
|
depending on the <CODE>attributes</CODE> field in the
|
|
B-tree's header record. If the <CODE>kBTBigKeysMask</CODE>
|
|
bit is set in <CODE>attributes</CODE>, the
|
|
<CODE>keyLength</CODE> is a <CODE>UInt16</CODE>; otherwise,
|
|
it's a <CODE>UInt8</CODE>. The length of the key, as stored
|
|
in this field, does not include the size of the
|
|
<CODE>keyLength</CODE> field itself.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
All HFS Plus B-trees use a <CODE>UInt16</CODE> for
|
|
their key length.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Immediately following the <CODE>keyLength</CODE> is the
|
|
key itself. The length of the key is determined by the node
|
|
type and the B-tree attributes. In leaf nodes, the length is
|
|
always determined by <CODE>keyLength</CODE>. In index nodes,
|
|
the length depends on the value of the
|
|
<CODE>kBTVariableIndexKeysMask</CODE> bit in the B-tree
|
|
attributes in the <a HREF = "#HeaderRecord">header record</a>.
|
|
If the bit is clear, the key occupies a constant number of
|
|
bytes, determined by the <CODE>maxKeyLength</CODE> field of
|
|
the B-tree header record. If the bit is set, the key length
|
|
is determined by the <CODE>keyLength</CODE> field of the
|
|
keyed record.</P>
|
|
|
|
<P>Following the key is the record's data. The format of
|
|
this data depends on the node type, as explained in the next
|
|
two sections. However, the data is always aligned on a
|
|
two-byte boundary and occupies an even number of bytes. To
|
|
meet the first alignment requirement, a pad byte must be
|
|
inserted between the key and the data if the size of the
|
|
<CODE>keyLength</CODE> field plus the size of the key is
|
|
odd. To meet the second alignment requirement, a pad byte
|
|
must be added after the data if the data size is odd.</P>
|
|
|
|
<H3><a NAME="IndexNodes"></a>Index Nodes</H3>
|
|
|
|
<P>The records in an index node are called <B>pointer
|
|
records</B>. They contain a <CODE>keyLength</CODE>, a key,
|
|
and a node number, expressed a <CODE>UInt32</CODE>. The node
|
|
whose number is in a pointer record is called a <B>child
|
|
node</B> of the index node. An index node has two or more
|
|
children, depending on the size of the node and the size of
|
|
the keys in the node.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
A root node does not need to exist (if the tree is
|
|
empty). And even if one does exist, it need not
|
|
be an index node (i.e., it could be a leaf node
|
|
if all the records fit in a single node).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3>Leaf Nodes</H3>
|
|
|
|
<P>The bottom level of a B-tree is occupied exclusively by
|
|
<B>leaf nodes</B>, which contain <B>data records</B> instead
|
|
of pointer records. The data records contain a
|
|
<CODE>keyLength</CODE>, a key, and the data associated with
|
|
that key. The data may be of variable length.</P>
|
|
|
|
<P>In an HFS Plus B-tree, the data in the data record is the
|
|
HFS Plus volume structure (such as a
|
|
<CODE>CatalogRecord</CODE>, <CODE>ExtentRecord</CODE>, or
|
|
<CODE>AttributeRecord</CODE>) associated with the key.</P>
|
|
|
|
<H3><a NAME="SearchingForKeyedRecords"></a>Searching for
|
|
Keyed Records</H3>
|
|
|
|
<P>A B-tree is highly structured to allow for efficient
|
|
searching, insertion, and removal. This structure primarily
|
|
affects the keyed records (pointer records and data records)
|
|
and the nodes in which they are stored (index nodes and leaf
|
|
nodes). The following are the ordering requirements for
|
|
index and leaf nodes:</P>
|
|
|
|
<UL>
|
|
<li>Keyed records must be placed in a node such that
|
|
their keys are in ascending order.</li>
|
|
|
|
<li>All the nodes in a given level (whose
|
|
<CODE>height</CODE> field is the same) must be chained
|
|
via their <CODE>fLink</CODE> and <CODE>bLink</CODE>
|
|
field. The node with the smallest keys must be first in
|
|
the chain and its <CODE>bLink</CODE> field must be zero.
|
|
The node with the largest keys must be last in the chain
|
|
and its <CODE>fLink</CODE> field must be zero.</li>
|
|
|
|
<li>For any given node, all the keys in the node must be
|
|
less than all the keys in the next node in the chain
|
|
(pointed to by <CODE>fLink</CODE>). Similarly, all the
|
|
keys in the node must be greater than all the keys in the
|
|
previous node in the chain (pointed to by
|
|
<CODE>bLink</CODE>).</li>
|
|
</UL>
|
|
|
|
<P>Keeping the keys ordered in this way makes it possible to
|
|
quickly search the B-tree to find the data associated with a
|
|
given key. Figure 4 shows a sample B-tree containing
|
|
hypothetical keys (in this case, the keys are simply
|
|
integers).</P>
|
|
|
|
<P>When an implementation needs to find the data associated
|
|
with a particular <B>search key</B>, it begins searching at
|
|
the root node. Starting with the first record, it searches
|
|
for the record with the greatest key that is less than or
|
|
equal to the search key. In then moves to the child node
|
|
(typically an index node) and repeats the same process.</P>
|
|
|
|
<P>This process continues until a leaf node is reached. If
|
|
the key found in the leaf node is equal to the search key,
|
|
the found record contains the desired data associated with
|
|
the search key. If the found key is not equal to the search
|
|
key, the search key is not present in the B-tree.</P>
|
|
|
|
|
|
|
|
<CENTER><img src="images/tn1150_004.gif" width=361 height=182 align=bottom alt="1150.4.gif"><P><B>Figure 4</B>. A sample B-Tree</P></center>
|
|
|
|
<H3>HFS and HFS Plus B-Trees Compared</H3>
|
|
|
|
<P>The structure of the B-trees on an HFS Plus volume is a
|
|
closely related to the
|
|
<a href="http://developer.apple.com/techpubs/mac/Files/Files-104.html">B-tree
|
|
structure used on an HFS volume</a>. There are three
|
|
principal differences: the size of nodes, the size of keys
|
|
within index nodes, and the size of a key length (UInt8 vs.
|
|
UInt16).</P>
|
|
|
|
<H4><a NAME="NodeSizes"></a>Node Sizes</H4>
|
|
|
|
<P>In an HFS B-tree, nodes always have a fixed size of 512
|
|
bytes.</P>
|
|
|
|
<P>In an HFS Plus B-tree, the node size is determined by a
|
|
field (<CODE>nodeSize</CODE>) in the header node. The node
|
|
size must be a power from 512 through 32,768. An
|
|
implementation must use the <CODE>nodeSize</CODE> field to
|
|
determine the actual node size.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The header node is always located at the start of
|
|
the B-tree, so you can find it without knowing the
|
|
B-tree node size.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>HFS Plus uses the following default node sizes:</P>
|
|
|
|
<UL>
|
|
<li>4 KB (8KB in Mac OS X) for the catalog file</li>
|
|
<li>1 KB (4KB in Mac OS X) for the extents overflow file</li>
|
|
<li>4 KB for the attributes file</li>
|
|
</UL>
|
|
|
|
<P>These sizes are set when the volume is initialized and
|
|
cannot be easily changed. It is legal to initialize an HFS
|
|
Plus volume with different node sizes, but the node sizes
|
|
must be large enough for an index node to contain two keys
|
|
of maximum size (plus the other overhead such as a node
|
|
descriptor, record offsets, and pointers to children).</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The node size of the catalog file must be at least
|
|
<CODE>kHFSPlusCatalogMinNodeSize</CODE> (4096).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The node size of the attributes file must be at
|
|
least <CODE>kHFSPlusAttrMinNodeSize</CODE> (4096).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H4>Key Size in an Index Node</H4>
|
|
|
|
<P>In an HFS B-tree, all of the keys in an index node occupy
|
|
a fixed amount of space: the maximum key length for that
|
|
B-tree. This simplifies the algorithms for inserting and
|
|
deleting records because, within an index node, one key can
|
|
be replaced by another key without worrying whether there is
|
|
adequate room for the new key. However, it is also somewhat
|
|
wasteful when the keys are variable length (such as in the
|
|
catalog file, where the key length varies with the length of
|
|
the file name).</P>
|
|
|
|
<P>In an HFS Plus B-tree, the keys in an index node are
|
|
allowed to vary in size. This complicates the algorithms for
|
|
inserting and deleting records, but reduces wasted space
|
|
when the length of a key can vary (such as in the catalog
|
|
file). It also means that the number of keys in an index
|
|
node will vary with the actual size of the keys.</p><BR>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="CatalogFile"></a>Catalog File</H2>
|
|
|
|
<P>HFS Plus uses a catalog file to maintain information
|
|
about the hierarchy of files and folders on a volume. A
|
|
catalog file is organized as a <a HREF = "#BTrees">B-tree</a>
|
|
file, and hence consists of a header node, index nodes, leaf
|
|
nodes, and (if necessary) map nodes. The location of the
|
|
first extent of the catalog file (and hence of the file's
|
|
header node) is stored in the volume header. From the
|
|
catalog file's header node, an implementation can obtain the
|
|
node number of the root node of the B-tree. From the root
|
|
node, an implementation can search the B-tree for keys, as
|
|
described in the
|
|
<a HREF = "#SearchingForKeyedRecords">previous section</a>.
|
|
</P>
|
|
|
|
<P>The B-Trees chapter defined a standard rule for the
|
|
<a HREF = "#NodeSizes">node size of HFS Plus B-trees</a>. As
|
|
the catalog file is a B-tree, it inherits the requirements
|
|
of this rule. In addition, the node size of the catalog file
|
|
must be at least 4 KB
|
|
(<CODE>kHFSPlusCatalogMinNodeSize</CODE>).</P>
|
|
|
|
<P><a NAME="CNID"></a>Each file or folder in the catalog file is assigned a
|
|
unique catalog node ID (CNID). For folders, the CNID is the
|
|
<B>folder ID</B>, sometimes called a directory ID, or dirID;
|
|
for files, it's the <B>file ID</B>. For any given file or
|
|
folder, the parent ID is the CNID of the folder containing
|
|
the file or folder, known as the parent folder.</P>
|
|
|
|
<P>The catalog node ID is defined by the
|
|
<CODE>CatalogNodeID</CODE> data type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>typedef UInt32 HFSCatalogNodeID;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The first 16 CNIDs are reserved for use by Apple
|
|
Computer, Inc., and include the following standard
|
|
assignments:</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kHFSRootParentID = 1,
|
|
kHFSRootFolderID = 2,
|
|
kHFSExtentsFileID = 3,
|
|
kHFSCatalogFileID = 4,
|
|
kHFSBadBlockFileID = 5,
|
|
kHFSAllocationFileID = 6,
|
|
kHFSStartupFileID = 7,
|
|
kHFSAttributesFileID = 8,
|
|
kHFSRepairCatalogFileID = 14,
|
|
kHFSBogusExtentFileID = 15,
|
|
kHFSFirstUserCatalogNodeID = 16
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>These constants have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>kHFSRootParentID</CODE></dt>
|
|
|
|
<DD>Parent ID of the root folder.</dd>
|
|
|
|
<DT><CODE>kHFSRootFolderID</CODE></dt>
|
|
|
|
<DD>Folder ID of the root folder.</dd>
|
|
|
|
<DT><CODE>kHFSExtentsFileID</CODE></dt>
|
|
|
|
<DD>File ID of the <a HREF = "#ExtentsOverflowFile">extents
|
|
overflow file</a>.</dd>
|
|
|
|
<DT><CODE>kHFSCatalogFileID</CODE></dt>
|
|
|
|
<DD>File ID of the <a HREF = "#CatalogFile">catalog
|
|
file</a>.</dd>
|
|
|
|
<DT><CODE>kHFSBadBlockFileID</CODE></dt>
|
|
|
|
<DD>File ID of the <a HREF = "#BadBlockFile">bad block
|
|
file</a>. The bad block file is not a file in the same
|
|
sense as a special file and a user file. See
|
|
<a HREF = "#BadBlockFile">Bad Block File</a> for details.</dd>
|
|
|
|
<DT><CODE>kHFSAllocationFileID</CODE></dt>
|
|
|
|
<DD>File ID of the <a HREF = "#AllocationFile">allocation
|
|
file</a> (introduced with HFS Plus).</dd>
|
|
|
|
<DT><CODE>kHFSStartupFileID</CODE></dt>
|
|
|
|
<DD>File ID of the <a HREF = "#StartupFile">startup
|
|
file</a> (introduced with HFS Plus).</dd>
|
|
|
|
<DT><CODE>kHFSAttributesFileID</CODE></dt>
|
|
|
|
<DD>File ID of the <a HREF = "#AttributesFile">attributes
|
|
file</a> (introduced with HFS Plus).</dd>
|
|
|
|
<DT><CODE>kHFSRepairCatalogFileID</CODE></dt>
|
|
|
|
<DD>Used temporarily by <CODE>fsck_hfs</CODE> when
|
|
rebuilding the catalog file.</dd>
|
|
|
|
<DT><CODE>kHFSBogusExtentFileID</CODE></dt>
|
|
|
|
<DD>Used temporarily during <CODE>ExchangeFiles</CODE>
|
|
operations.</dd>
|
|
|
|
<DT><CODE>kHFSFirstUserCatalogNodeID</CODE></dt>
|
|
|
|
<DD>First CNID available for use by user files and
|
|
folders.</dd>
|
|
</DL>
|
|
|
|
<P>In addition, the CNID of zero is never used and serves as
|
|
a nil value.</P>
|
|
|
|
<P>Typically, CNIDs are allocated sequentially, starting at
|
|
<CODE>kHFSFirstUserCatalogNodeID</CODE>. Versions of the HFS
|
|
Plus specification prior to Jan. 18, 2000, required the
|
|
<CODE>nextCatalogID</CODE> field of the <a HREF =
|
|
"#VolumeHeader">volume header</a> to be greater than the
|
|
largest CNID used on the volume (so that an implementation
|
|
could use <CODE>nextCatalogID</CODE> to determine the CNID to
|
|
assign to a newly created file or directory). However, this
|
|
can be a problem for volumes that create files or directories
|
|
at a high rate (for example, a busy server), since they might
|
|
run out of CNID values.</p>
|
|
|
|
<P>HFS Plus volumes now allow CNID values to wrap around and be
|
|
reused. The <code>kHFSCatalogNodeIDsReusedBit</code> in the
|
|
<code>attributes</code> field of the <a HREF = "#VolumeHeader">
|
|
volume header</a> is set to indicate when CNID values have
|
|
wrapped around and been reused. When
|
|
<code>kHFSCatalogNodeIDsReusedBit</code> is set, the
|
|
<CODE>nextCatalogID</CODE> field is no longer required to be
|
|
greater than any existing CNID.</p>
|
|
|
|
<P>When <code>kHFSCatalogNodeIDsReusedBit</code> is set,
|
|
<CODE>nextCatalogID</CODE> may still be used as a hint for the
|
|
CNID to assign to newly created files or directories, but the
|
|
implementation must verify that CNID is not currently in use
|
|
(and pick another value if it is in use). When CNID number
|
|
<CODE>nextCatalogID</CODE> is already in use, an implementation
|
|
could just increment <CODE>nextCatalogID</CODE> until it finds
|
|
a CNID that is not in use. If <CODE>nextCatalogID</CODE>
|
|
overflows to zero, <code>kHFSCatalogNodeIDsReusedBit</code>
|
|
must be set and <CODE>nextCatalogID</CODE> set to
|
|
<CODE>kHFSFirstUserCatalogNodeID</CODE> (to avoid using any
|
|
reserved CNID values).</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Mac OS X versions 10.2 and later, and all versions
|
|
of Mac OS 9 support
|
|
<code>kHFSCatalogNodeIDsReusedBit</code>.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>As the catalog file is a B-tree file, it inherits its
|
|
basic structure from the definition in
|
|
<a HREF = "#BTrees">B-Trees</a>. Beyond that, you need to know
|
|
only two things about an HFS Plus catalog file to interpret
|
|
its data:</P>
|
|
|
|
<OL>
|
|
<li>the format of the key used both in index and leaf
|
|
nodes, and</li>
|
|
|
|
<li>the format of the leaf node data records (file,
|
|
folder, and thread records).</li>
|
|
</OL>
|
|
|
|
<H3>Catalog File Key</H3>
|
|
|
|
<P>For a given file, folder, or thread record, the catalog file
|
|
key consists of the parent folder's <a href="#CNID">CNID</a>
|
|
and the name of the file or folder. This structure is described
|
|
using the <CODE>HFSPlusCatalogKey</CODE> type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusCatalogKey {
|
|
UInt16 keyLength;
|
|
HFSCatalogNodeID parentID;
|
|
HFSUniStr255 nodeName;
|
|
};
|
|
typedef struct HFSPlusCatalogKey HFSPlusCatalogKey;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>keyLength</CODE></dt>
|
|
|
|
<DD>The <CODE>keyLength</CODE> field is required by all
|
|
<a HREF = "#KeyedRecords">keyed records</a> in a B-tree.
|
|
The catalog file, in common with all HFS Plus B-trees,
|
|
uses a large key length (<CODE>UInt16</CODE>).</dd>
|
|
|
|
<DT><CODE>parentID</CODE></dt>
|
|
|
|
<DD>For file and folder records, this is the folder
|
|
containing the file or folder represented by the record. For
|
|
thread records, this is the <a href="#CNID">CNID</a> of the
|
|
file or folder itself.</dd>
|
|
|
|
<DT><CODE>nodeName</CODE></dt>
|
|
|
|
<DD>This field contains Unicode characters,
|
|
<a HREF = "#CanonicalDecomposition">fully decomposed and in
|
|
canonical order</a>. For file or folder records, this is
|
|
the name of the file or folder inside the <CODE>parentID</CODE>
|
|
folder. For thread records, this is the empty string.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The length of the key varies with the length of the
|
|
string stored in the <CODE>nodeName</CODE> field;
|
|
it occupies only the number of bytes required to
|
|
hold the name. The <CODE>keyLength</CODE> field
|
|
determines the actual length of the key; it varies
|
|
between
|
|
<CODE>kHFSPlusCatalogKeyMinimumLength</CODE> (6) to
|
|
<CODE>kHFSPlusCatalogKeyMaximumLength</CODE> (516).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The catalog file key mirrors the standard way you
|
|
specify a file or folder with the Mac OS File
|
|
Manager programming interface, with the exception
|
|
of the volume reference number, which determines
|
|
which volume's catalog to search.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Catalog file keys are compared first by
|
|
<CODE>parentID</CODE> and then by <CODE>nodeName</CODE>. The
|
|
<CODE>parentID</CODE> is compared as an unsigned 32-bit
|
|
integer. For case-sensitive <a href="#HFSX">HFSX</a> volumes,
|
|
the characters in <code>nodeName</code> are compared as a
|
|
sequence of unsigned 16-bit integers. For case-insensitive
|
|
HFSX volumes and HFS Plus volumes, the <CODE>nodeName</CODE>
|
|
must be compared in a case-insensitive way, as described in the
|
|
<a HREF = "#StringComparisonAlgorithm">Case-Insensitive String
|
|
Comparison Algorithm</a> section.</P>
|
|
|
|
<P>For more information about how catalog keys are used to
|
|
find file, folder, and thread records within the catalog
|
|
tree, see <a HREF = "#CatalogTreeUsage">Catalog Tree
|
|
Usage</a>.</P>
|
|
|
|
<H3>Catalog File Data</H3>
|
|
|
|
<P>A catalog file leaf node can contain four different types
|
|
of data records:</P>
|
|
|
|
<OL>
|
|
<li>A <B>folder record</B> contains information about a
|
|
single folder.</li>
|
|
|
|
<li>A <B>file record</B> contains information about a
|
|
single file.</li>
|
|
|
|
<li>A <B>folder thread record</B> provides a link between
|
|
a folder and its parent folder, and lets you find a
|
|
folder record given just the folder ID.</li>
|
|
|
|
<li>A <B>file thread record</B> provides a link between a
|
|
file and its parent folder, and lets you find a file
|
|
record given just the file ID. (In both the folder thread
|
|
and the file thread record, the thread record is used to
|
|
map the file or folder ID to the actual parent directory
|
|
ID and name.)</li>
|
|
</OL>
|
|
|
|
<P>Each record starts with a <CODE>recordType</CODE> field,
|
|
which describes the type of catalog data record. The
|
|
<CODE>recordType</CODE> field contains one of the following
|
|
values:</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kHFSPlusFolderRecord = 0x0001,
|
|
kHFSPlusFileRecord = 0x0002,
|
|
kHFSPlusFolderThreadRecord = 0x0003,
|
|
kHFSPlusFileThreadRecord = 0x0004
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The values have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>kHFSPlusFolderRecord</CODE></dt>
|
|
|
|
<DD>This record is a
|
|
<a HREF = "#CatalogFolderRecord">folder record</a>. You can
|
|
use the <CODE>HFSPlusCatalogFolder</CODE> type to
|
|
interpret the data.</dd>
|
|
|
|
<DT><CODE>kHFSPlusFileRecord</CODE></dt>
|
|
|
|
<DD>This record is a <a HREF = "#CatalogFileRecord">file
|
|
record</a>. You can use the
|
|
<CODE>HFSPlusCatalogFile</CODE> type to interpret the
|
|
data.</dd>
|
|
|
|
<DT><CODE>kHFSPlusFolderThreadRecord</CODE></dt>
|
|
|
|
<DD>This record is a folder
|
|
<a HREF = "#CatalogThreadRecord">thread record</a>. You can
|
|
use the <CODE>HFSPlusCatalogThread</CODE> type to
|
|
interpret the data.</dd>
|
|
|
|
<DT><CODE>kHFSPlusFileThreadRecord</CODE></dt>
|
|
|
|
<DD>This record is a file
|
|
<a HREF = "#CatalogThreadRecord">thread record</a>. You can
|
|
use the <CODE>HFSPlusCatalogThread</CODE> type to
|
|
interpret the data.</dd>
|
|
</DL>
|
|
|
|
<P>The next three sections describe the folder, file, and
|
|
thread records in detail.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The position of the <CODE>recordType</CODE> field,
|
|
and the constants chosen for the various record
|
|
types, are especially useful if you're writing
|
|
common code to handle HFS and HFS Plus volumes.
|
|
</P>
|
|
|
|
<P>In HFS, the record type field is one byte, but
|
|
it's always followed by a one-byte reserved field
|
|
whose value is always zero. In HFS Plus, the record
|
|
type field is two bytes. You can use the HFS Plus
|
|
two-byte record type to examine an HFS record if
|
|
you use the appropriate constants, as shown below.
|
|
</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kHFSFolderRecord = 0x0100,
|
|
kHFSFileRecord = 0x0200,
|
|
kHFSFolderThreadRecord = 0x0300,
|
|
kHFSFileThreadRecord = 0x0400
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
|
|
<P>The values have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>kHFSFolderRecord</CODE></dt>
|
|
|
|
<DD>This record is an HFS folder record. You can
|
|
use the <CODE>HFSCatalogFolder</CODE> type to
|
|
interpret the data.</dd>
|
|
|
|
<DT><CODE>kHFSFileRecord</CODE></dt>
|
|
|
|
<DD>This record is an HFS file record. You can
|
|
use the <CODE>HFSCatalogFile</CODE> type to
|
|
interpret the data.</dd>
|
|
|
|
<DT><CODE>kHFSFolderThreadRecord</CODE></dt>
|
|
|
|
<DD>This record is an HFS folder thread record.
|
|
You can use the <CODE>HFSCatalogThread</CODE>
|
|
type to interpret the data.</dd>
|
|
|
|
<DT><CODE>kHFSFileThreadRecord</CODE></dt>
|
|
|
|
<DD>This record is an HFS file thread record.
|
|
You can use the <CODE>HFSCatalogThread</CODE>
|
|
type to interpret the data.</dd>
|
|
</DL>
|
|
|
|
|
|
<H4><a NAME="CatalogFolderRecord"></a>Catalog Folder Records
|
|
</H4>
|
|
|
|
<P>The catalog folder record is used in the catalog B-tree
|
|
file to hold information about a particular folder on the
|
|
volume. The data of the record is described by the
|
|
<CODE>HFSPlusCatalogFolder</CODE> type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusCatalogFolder {
|
|
SInt16 recordType;
|
|
UInt16 flags;
|
|
UInt32 valence;
|
|
HFSCatalogNodeID folderID;
|
|
UInt32 createDate;
|
|
UInt32 contentModDate;
|
|
UInt32 attributeModDate;
|
|
UInt32 accessDate;
|
|
UInt32 backupDate;
|
|
HFSPlusBSDInfo permissions;
|
|
FolderInfo userInfo;
|
|
ExtendedFolderInfo finderInfo;
|
|
UInt32 textEncoding;
|
|
UInt32 reserved;
|
|
};
|
|
typedef struct HFSPlusCatalogFolder HFSPlusCatalogFolder;
|
|
</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>recordType</CODE></dt>
|
|
|
|
<DD>The catalog data record type. For folder records,
|
|
this is always <CODE>kHFSPlusFolderRecord</CODE>.</dd>
|
|
|
|
<DT><CODE>flags</CODE></dt>
|
|
|
|
<DD>This field contains bit flags about the folder. No
|
|
bits are currently defined for folder records. An
|
|
implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
|
|
<DT><CODE>valence</CODE></dt>
|
|
|
|
<DD>The number of files and folders directly contained by
|
|
this folder. This is equal to the number of file and
|
|
folder records whose key's <CODE>parentID</CODE> is equal
|
|
to this folder's <CODE>folderID</CODE>.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The traditional Mac OS File Manager programming
|
|
interfaces require folders to have a valence less
|
|
than 32,767. An implementation must enforce this
|
|
restriction if it wants the volume to be usable by
|
|
Mac OS. Values of 32,768 and larger are
|
|
problematic; 32,767 and smaller are OK. It's an
|
|
implementation restriction for the older Mac OS
|
|
APIs; items 32,768 and beyond would be unreachable
|
|
by <CODE>PBGetCatInfo</CODE>. As a practical
|
|
matter, many programs are likely to fails with
|
|
anywhere near that many items in a single folder.
|
|
So, the volume format allows more than 32,767 items
|
|
in a folder, but it's probably not a good idea to
|
|
exceed that limit right now.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<DL>
|
|
<DT><CODE>folderID</CODE></dt>
|
|
|
|
<DD>The <a HREF = "#CNID">CNID</a> of this folder.
|
|
Remember that the key for a folder record contains the
|
|
CNID of the folders parent, not the CNID of the folder
|
|
itself.</dd>
|
|
|
|
<DT><CODE>createDate</CODE></dt>
|
|
|
|
<DD>The date and time the folder was created. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a
|
|
description of the format. Again, the
|
|
<CODE>createDate</CODE> of the Volume Header is NOT
|
|
stored in GMT; it is local time. (Further, if the volume
|
|
has an HFS wrapper, the creation date in the MDB should
|
|
be the same as the <CODE>createDate</CODE> in the Volume
|
|
Header).</dd>
|
|
|
|
<DT><CODE>contentModDate</CODE></dt>
|
|
|
|
<DD>The date and time the folder's contents were last
|
|
changed. This is the time when a file or folder was
|
|
created or deleted inside this folder, or when a file or
|
|
folder was moved in or out of this folder. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a
|
|
description of the format.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The traditional Mac OS APIs use the
|
|
<CODE>contentModDate</CODE> when getting and
|
|
setting the modification date. The traditional Mac OS
|
|
APIs treat <CODE>attributeModDate</CODE> as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<DL>
|
|
<DT><CODE>attributeModDate</CODE></dt>
|
|
|
|
<DD>The last date and time that any field in the
|
|
folder's catalog record was changed. An implementation may treat
|
|
this field as <a HREF = "#ReservedAndPadFields">reserved</a>.
|
|
In Mac OS X, the BSD APIs use this field as the folder's change time
|
|
(returned in the <code>st_ctime</code> field of <code>struct stat</code>).
|
|
All versions of Mac OS 8 and 9 treat this field as reserved. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a description of
|
|
the format.</dd>
|
|
|
|
<DT><CODE>accessDate</CODE></dt>
|
|
|
|
<DD>The date and time the folder's contents were last
|
|
read. This field has no analog in the HFS catalog record.
|
|
It represents the last time the folder's contents were
|
|
read. This field exists to support POSIX semantics when
|
|
the volume is mounted on Mac OS X and some non-Mac OS platforms. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a
|
|
description of the format.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The traditional Mac OS implementation of HFS Plus does not
|
|
maintain the <CODE>accessDate</CODE> field. Folders
|
|
created by traditional Mac OS have an
|
|
<CODE>accessDate</CODE> of zero.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<DL>
|
|
<DT><CODE>backupDate</CODE></dt>
|
|
|
|
<DD>The date and time the folder was last backed up. The
|
|
volume format requires no special action on this field;
|
|
it simply defines the field for the benefit of user
|
|
programs. See <a HREF = "#HFSPlusDates">HFS Plus Dates</a>
|
|
for a description of the format.</dd>
|
|
|
|
<DT><CODE>permissions</CODE></dt>
|
|
|
|
<DD>This field contains folder permissions, similar to
|
|
those defined by POSIX or AFP. See
|
|
<a HREF = "#HFSPlusPermissions">HFS Plus Permissions</a>
|
|
for a description of the format.</DD>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The traditional Mac OS implementation of HFS Plus does not use
|
|
the <CODE>permissions</CODE> field. Folders created
|
|
by traditional Mac OS have the entire field set to 0.</p>
|
|
</TD></TR></table></CENTER>
|
|
<DL>
|
|
<DT><CODE>userInfo</CODE></dt>
|
|
|
|
<DD>This field contains information used by the Mac OS
|
|
Finder. The contents of this structure are not strictly part of the HFS Plus
|
|
specification, but general information is in the <a href="#FinderInfo">
|
|
Finder Info</a> section of this note.</dd>
|
|
|
|
<DT><CODE>finderInfo</CODE></dt>
|
|
|
|
<DD>This field contains information used by the Mac OS
|
|
Finder. The contents of this structure are not strictly part of the HFS Plus
|
|
specification, but general information is in the <a href="#FinderInfo">
|
|
Finder Info</a> section of this note.</dd>
|
|
|
|
<DT><CODE>textEncoding</CODE></dt>
|
|
|
|
<DD>A hint as to text encoding from which the folder name
|
|
was derived. This hint can be used to improve the quality
|
|
of the conversion of the name to a Mac OS-encoded Pascal
|
|
string. See <a HREF = "#TextEncodings">Text Encodings</a>
|
|
for details.</dd>
|
|
|
|
<DT><CODE>reserved</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
</DL>
|
|
|
|
<H4><a NAME="CatalogFileRecord"></a>Catalog File Records
|
|
</H4>
|
|
|
|
<P>The catalog file record is used in the catalog B-tree
|
|
file to hold information about a particular file on the
|
|
volume. The data of the record is described by the
|
|
<CODE>HFSPlusCatalogFile</CODE> type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusCatalogFile {
|
|
SInt16 recordType;
|
|
UInt16 flags;
|
|
UInt32 reserved1;
|
|
HFSCatalogNodeID fileID;
|
|
UInt32 createDate;
|
|
UInt32 contentModDate;
|
|
UInt32 attributeModDate;
|
|
UInt32 accessDate;
|
|
UInt32 backupDate;
|
|
HFSPlusBSDInfo permissions;
|
|
FileInfo userInfo;
|
|
ExtendedFileInfo finderInfo;
|
|
UInt32 textEncoding;
|
|
UInt32 reserved2;
|
|
|
|
HFSPlusForkData dataFork;
|
|
HFSPlusForkData resourceFork;
|
|
};
|
|
typedef struct HFSPlusCatalogFile HFSPlusCatalogFile;
|
|
</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>recordType</CODE></dt>
|
|
|
|
<DD>The catalog data record type. For files records, this
|
|
is always <CODE>kHFSPlusFileRecord</CODE>.</dd>
|
|
|
|
<DT><CODE>flags</CODE></dt>
|
|
|
|
<DD>This field contains bit flags about the file. The
|
|
currently defined bits are <a HREF = "#FileFlags">described
|
|
below</a>. An implementation must treat undefined bits as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</dd>
|
|
|
|
<DT><CODE>reserved1</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
|
|
<DT><CODE>fileID</CODE></dt>
|
|
|
|
<DD>The <a HREF = "#CNID">CNID</a> of this file.</dd>
|
|
|
|
<DT><CODE>createDate</CODE></dt>
|
|
|
|
<DD>The date and time the file was created. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a
|
|
description of the format.</dd>
|
|
|
|
<DT><CODE>contentModDate</CODE></dt>
|
|
|
|
<DD>The date and time the file's contents were last
|
|
changed by extending, truncating, or writing either of
|
|
the forks. See <a HREF = "#HFSPlusDates">HFS Plus Dates</a>
|
|
for a description of the format.</dd>
|
|
|
|
<DT><CODE>attributeModDate</CODE></dt>
|
|
|
|
<DD>The last date and time that any field in the
|
|
file's catalog record was changed. An implementation may treat
|
|
this field as <a HREF = "#ReservedAndPadFields">reserved</a>.
|
|
In Mac OS X, the BSD APIs use this field as the file's change time
|
|
(returned in the <code>st_ctime</code> field of <code>struct stat</code>).
|
|
All versions of Mac OS 8 and 9 treat this field as reserved. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a description of
|
|
the format.</dd>
|
|
|
|
<DT><CODE>accessDate</CODE></dt>
|
|
|
|
<DD>The date and time the file's contents were last read.
|
|
This field has no analog in the HFS catalog record. It
|
|
represents the last time either of a file's forks was
|
|
read. This field exists to support POSIX semantics when
|
|
the volume is mounted on Mac OS X and some non-Mac OS platforms. See
|
|
<a HREF = "#HFSPlusDates">HFS Plus Dates</a> for a
|
|
description of the format.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The traditional Mac OS implementation of HFS Plus does not
|
|
maintain the <CODE>accessDate</CODE> field. Files
|
|
created by traditional Mac OS have an
|
|
<CODE>accessDate</CODE> of zero.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<DL>
|
|
<DT><CODE>backupDate</CODE></dt>
|
|
|
|
<DD>The date and time the file was last backed up. The
|
|
volume format requires no special action on this field;
|
|
it simply defines the field for the benefit of user
|
|
programs. See <a HREF = "#HFSPlusDates">HFS Plus Dates</a>
|
|
for a description of the format.</dd>
|
|
|
|
<DT><CODE>permissions</CODE></dt>
|
|
|
|
<DD>This field contains file permissions, similar to
|
|
those defined by POSIX. See
|
|
<a HREF = "#HFSPlusPermissions">HFS Plus Permissions</a>
|
|
for a description of the format.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The traditional Mac OS implementation of HFS Plus does not use
|
|
the <CODE>permissions</CODE> field. Files created
|
|
by traditional Mac OS have the entire field set to 0.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<DL>
|
|
<DT><CODE>userInfo</CODE></dt>
|
|
|
|
<DD>This field contains information used by the Mac OS
|
|
Finder. For more information, see the <a href="#FinderInfo">
|
|
Finder Info</a> section of this note.</dd>
|
|
|
|
<DT><CODE>finderInfo</CODE></dt>
|
|
|
|
<DD>This field contains information used by the Mac OS
|
|
Finder. The contents of this structure are not strictly part of the HFS Plus
|
|
specification, but general information is in the <a href="#FinderInfo">
|
|
Finder Info</a> section of this note.</dd>
|
|
|
|
<DT><CODE>textEncoding</CODE></dt>
|
|
|
|
<DD>A hint as to text encoding from which the file name
|
|
was derived. This hint can be used to improved the
|
|
quality of the conversion of the name to a Mac OS encoded
|
|
Pascal string. See <a HREF = "#TextEncodings">Text
|
|
Encodings</a> for details.</dd>
|
|
|
|
<DT><CODE>reserved2</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
|
|
<DT><CODE>dataFork</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the data
|
|
fork. See <a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
|
|
<DT><CODE>resourceFork</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the
|
|
resource fork. See <a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
</DL>
|
|
|
|
<P>For each fork, the first eight extents are described by
|
|
the <CODE>HFSPlusForkData</CODE> field in the catalog file
|
|
record. If a fork is sufficiently fragmented to require more
|
|
than eight extents, the remaining extents are described by
|
|
extent records in the <a HREF = "#ExtentsOverflowFile">extents
|
|
overflow file</a>.</P>
|
|
|
|
<P><a NAME="FileFlags"></a>The following constants define
|
|
bit flags in the file record's <CODE>flags</CODE> field:
|
|
</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kHFSFileLockedBit = 0x0000,
|
|
kHFSFileLockedMask = 0x0001,
|
|
kHFSThreadExistsBit = 0x0001,
|
|
kHFSThreadExistsMask = 0x0002
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The values have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>kHFSFileLockedBit</CODE>,
|
|
<CODE>kHFSFileLockedMask</CODE></dt>
|
|
|
|
<DD>If <CODE>kHFSFileLockedBit</CODE> is set, then none
|
|
of the forks may be extended, truncated, or written to.
|
|
They may only be opened for reading (not for writing).
|
|
The catalog information (like <CODE>finderInfo</CODE> and
|
|
<CODE>userInfo</CODE>) may still be changed.</dd>
|
|
|
|
<DT><CODE>kHFSThreadExistsBit</CODE>,
|
|
<CODE>kHFSThreadExistsMask</CODE></dt>
|
|
|
|
<DD>This bit incidates that the file has a thread record.
|
|
As all files in HFS Plus have thread records, this bit
|
|
must be set.</dd>
|
|
</DL>
|
|
|
|
<H4><a NAME="CatalogThreadRecord"></a>Catalog Thread Records
|
|
</H4>
|
|
|
|
<P>The catalog thread record is used in the catalog B-tree file
|
|
to link a <a href="#CNID">CNID</a> to the file or folder record
|
|
using that CNID. The data of the record is described by the
|
|
<CODE>HFSPlusCatalogThread</CODE> type.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
In HFS, thread records were required for folders
|
|
but optional for files. In HFS Plus, thread records
|
|
are required for both files and folders.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusCatalogThread {
|
|
SInt16 recordType;
|
|
SInt16 reserved;
|
|
HFSCatalogNodeID parentID;
|
|
HFSUniStr255 nodeName;
|
|
};
|
|
typedef struct HFSPlusCatalogThread HFSPlusCatalogThread;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>recordType</CODE></dt>
|
|
|
|
<DD>The catalog data record type. For thread records,
|
|
this is <CODE>kHFSPlusFileRecord</CODE> or
|
|
<CODE>kHFSPlusFolderRecord</CODE>, depending on whether
|
|
the thread record refers to a file or a folder. Both
|
|
types of thread record contain the same data.</dd>
|
|
|
|
<DT><CODE>reserved1</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
|
|
<DT><CODE>parentID</CODE></dt>
|
|
|
|
<DD>The <a href="#CNID">CNID</a> of the parent of the file
|
|
or folder referenced by this thread record.</dd>
|
|
|
|
<DT><CODE>nodeName</CODE></dt>
|
|
|
|
<DD>The name of the file or folder referenced by this
|
|
thread record.</dd>
|
|
</DL>
|
|
|
|
<P>The next section explains how thread records can be used
|
|
to find a file or folder using just its <a href="#CNID">CNID</a>.</P>
|
|
|
|
<H3><a NAME="CatalogTreeUsage"></a>Catalog Tree Usage</H3>
|
|
|
|
<P>File and folder records always have a key that contains a
|
|
non-empty <CODE>nodeName</CODE>. The file and folder records
|
|
for the children are all consecutive in the catalog, since
|
|
they all have the same <CODE>parentID</CODE> in the key, and
|
|
vary only by <CODE>nodeName</CODE>.</P>
|
|
|
|
<P>The key for a thread record is the file's or folder's <a
|
|
href="#CNID">CNID</a> (not the CNID of the parent folder) and
|
|
an empty (zero length) <CODE>nodeName</CODE>. This allows a
|
|
file or folder to by found using just the CNID. The thread
|
|
record contains the <CODE>parentID</CODE> and
|
|
<CODE>nodeName</CODE> field of the file or folder itself.</P>
|
|
|
|
<P>Finding a file or folder by its CNID is a two-step
|
|
process. The first step is to use the CNID to look up the
|
|
thread record for the file or folder. This yields the file
|
|
or folder's parent folder ID and name. The second step is to
|
|
use that information to look up the real file or folder
|
|
record.</P>
|
|
|
|
<P>Since files do not contain other files or folders, there
|
|
are no catalog records whose key has a <CODE>parentID</CODE>
|
|
equal to a file's CNID and <CODE>nodeName</CODE> with
|
|
non-zero length. These unused key values are reserved.</p>
|
|
|
|
<H3><a NAME="FinderInfo"></a>Finder Info</H3>
|
|
|
|
<P>See the <a href="http://developer.apple.com/documentation/Carbon/Reference/Finder_Interface/index.html">
|
|
Finder Interface Reference</a> for more detailed information
|
|
about these data types and how the Finder uses them.</P>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct Point {
|
|
SInt16 v;
|
|
SInt16 h;
|
|
};
|
|
typedef struct Point Point;
|
|
|
|
struct Rect {
|
|
SInt16 top;
|
|
SInt16 left;
|
|
SInt16 bottom;
|
|
SInt16 right;
|
|
};
|
|
typedef struct Rect Rect;
|
|
|
|
/* OSType is a 32-bit value made by packing four 1-byte characters
|
|
together. */
|
|
typedef UInt32 FourCharCode;
|
|
typedef FourCharCode OSType;
|
|
|
|
/* Finder flags (finderFlags, fdFlags and frFlags) */
|
|
enum {
|
|
kIsOnDesk = 0x0001, /* Files and folders (System 6) */
|
|
kColor = 0x000E, /* Files and folders */
|
|
kIsShared = 0x0040, /* Files only (Applications only) If */
|
|
/* clear, the application needs */
|
|
/* to write to its resource fork, */
|
|
/* and therefore cannot be shared */
|
|
/* on a server */
|
|
kHasNoINITs = 0x0080, /* Files only (Extensions/Control */
|
|
/* Panels only) */
|
|
/* This file contains no INIT resource */
|
|
kHasBeenInited = 0x0100, /* Files only. Clear if the file */
|
|
/* contains desktop database resources */
|
|
/* ('BNDL', 'FREF', 'open', 'kind'...) */
|
|
/* that have not been added yet. Set */
|
|
/* only by the Finder. */
|
|
/* Reserved for folders */
|
|
kHasCustomIcon = 0x0400, /* Files and folders */
|
|
kIsStationery = 0x0800, /* Files only */
|
|
kNameLocked = 0x1000, /* Files and folders */
|
|
kHasBundle = 0x2000, /* Files only */
|
|
kIsInvisible = 0x4000, /* Files and folders */
|
|
kIsAlias = 0x8000 /* Files only */
|
|
};
|
|
|
|
/* Extended flags (extendedFinderFlags, fdXFlags and frXFlags) */
|
|
enum {
|
|
kExtendedFlagsAreInvalid = 0x8000, /* The other extended flags */
|
|
/* should be ignored */
|
|
kExtendedFlagHasCustomBadge = 0x0100, /* The file or folder has a */
|
|
/* badge resource */
|
|
kExtendedFlagHasRoutingInfo = 0x0004 /* The file contains routing */
|
|
/* info resource */
|
|
};
|
|
|
|
struct FileInfo {
|
|
OSType fileType; /* The type of the file */
|
|
OSType fileCreator; /* The file's creator */
|
|
UInt16 finderFlags;
|
|
Point location; /* File's location in the folder. */
|
|
UInt16 reservedField;
|
|
};
|
|
typedef struct FileInfo FileInfo;
|
|
|
|
struct ExtendedFileInfo {
|
|
SInt16 reserved1[4];
|
|
UInt16 extendedFinderFlags;
|
|
SInt16 reserved2;
|
|
SInt32 putAwayFolderID;
|
|
};
|
|
typedef struct ExtendedFileInfo ExtendedFileInfo;
|
|
|
|
struct FolderInfo {
|
|
Rect windowBounds; /* The position and dimension of the */
|
|
/* folder's window */
|
|
UInt16 finderFlags;
|
|
Point location; /* Folder's location in the parent */
|
|
/* folder. If set to {0, 0}, the Finder */
|
|
/* will place the item automatically */
|
|
UInt16 reservedField;
|
|
};
|
|
typedef struct FolderInfo FolderInfo;
|
|
|
|
struct ExtendedFolderInfo {
|
|
Point scrollPosition; /* Scroll position (for icon views) */
|
|
SInt32 reserved1;
|
|
UInt16 extendedFinderFlags;
|
|
SInt16 reserved2;
|
|
SInt32 putAwayFolderID;
|
|
};
|
|
typedef struct ExtendedFolderInfo ExtendedFolderInfo;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="ExtentsOverflowFile"></a>Extents Overflow File</H2>
|
|
|
|
<P>HFS Plus tracks which allocation blocks belong to a
|
|
file's forks by maintaining a list of extents (contiguous
|
|
allocation blocks) that belong to that file, in the
|
|
appropriate order. Each extent is represented by a pair of
|
|
numbers: the first allocation block number of the extent and
|
|
the number of allocation blocks in the extent. The file
|
|
record in the catalog B-tree contains a record of the first
|
|
eight extents of each fork. If there are more than eight
|
|
extents in a fork, the remaining extents are stored in the
|
|
extents overflow file.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
<a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a> discusses how HFS Plus maintains
|
|
information about a fork.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Like the catalog file, the extents overflow file is
|
|
<a HREF = "#BTrees">B-tree</a>. However, the structure of the
|
|
extents overflow file is relatively simple compared to that
|
|
of a catalog file. The extents overflow file has a simple,
|
|
fixed length key and a single type of data record.</P>
|
|
|
|
<H3>Extents Overflow File Key</H3>
|
|
|
|
<P>The structure of the key for the extents overflow file is
|
|
described by the <CODE>HFSPlusExtentKey</CODE> type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusExtentKey {
|
|
UInt16 keyLength;
|
|
UInt8 forkType;
|
|
UInt8 pad;
|
|
HFSCatalogNodeID fileID;
|
|
UInt32 startBlock;
|
|
};
|
|
typedef struct HFSPlusExtentKey HFSPlusExtentKey;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>keyLength</CODE></dt>
|
|
|
|
<DD>The <CODE>keyLength</CODE> field is required by all
|
|
<a HREF = "#KeyedRecords">keyed records</a> in a B-tree.
|
|
The extents overflow file, in common with all HFS Plus
|
|
B-trees, uses a large key length (<CODE>UInt16</CODE>).
|
|
Keys in the extents overflow file always have the same
|
|
length, <CODE>kHFSPlusExtentKeyMaximumLength</CODE> (10).</dd>
|
|
|
|
<DT><CODE>forkType</CODE></dt>
|
|
|
|
<DD>The type of fork for which this extent record
|
|
applies. This must be either 0 for the data fork or 0xFF
|
|
for the resource fork.</dd>
|
|
|
|
<DT><CODE>pad</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">pad</a> field.</dd>
|
|
|
|
<DT><CODE>fileID</CODE></dt>
|
|
|
|
<DD>The <a href="#CNID">CNID</a> of the file for which this
|
|
extent record applies.</dd>
|
|
|
|
<DT><CODE>startBlock</CODE></dt>
|
|
|
|
<DD>The offset, in allocation blocks, into the fork of
|
|
the first extent described by this extent record. The
|
|
startBlock field lets you directly find the particular
|
|
extents for a given offset into a fork.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Typically, an implementation will keep a copy of
|
|
the initial extents from the catalog record. When
|
|
trying to access part of the fork, they see whether
|
|
that position is beyond the extents described in
|
|
the catalog record; if so, they use that offset (in
|
|
allocation blocks) to find the appropriate extents
|
|
B-tree record. See
|
|
<a HREF = "#ExtentsOverflowFileUsage"> Extents
|
|
Overflow File Usage for more information.</a></p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Two <CODE>HFSPlusExtentKey</CODE> structures are compared
|
|
by comparing their fields in the following order:
|
|
<CODE>fileID</CODE>, <CODE>forkType</CODE>,
|
|
<CODE>startBlock</CODE>. Thus, all the extent records for a
|
|
particular fork are grouped together in the B-tree, right
|
|
next to all the extent records for the other fork of the
|
|
file.</P>
|
|
|
|
<H3>Extents Overflow File Data</H3>
|
|
|
|
<P>The data records for an extents overflow file (the
|
|
<B>extent records</B>) are described by the
|
|
<CODE>HFSPlusExtentRecord</CODE> type, which is described in
|
|
detail in <a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a>.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
Remember that the <CODE>HFSPlusExtentRecord</CODE>
|
|
contains descriptors for eight extents. The first
|
|
eight extents in a fork are held in its
|
|
<a HREF = "#CatalogFileRecord">catalog file
|
|
record</a>. So the number of extent records for a
|
|
fork is ((number of extents - 8 + 7) / 8).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<H3><a NAME="ExtentsOverflowFileUsage"></a>Extents Overflow
|
|
File Usage</H3>
|
|
|
|
<P>The most important thing to remember about extents
|
|
overflow file is that it is only used for forks with more
|
|
than eight extents. In most cases, forks have fewer extents,
|
|
and all the extents information for the fork is held in its
|
|
catalog file record. However, for more fragmented forks, the
|
|
extra extents information is stored in the extents overflow
|
|
file.</P>
|
|
|
|
<P>When an implementation needs to map a fork offset into a
|
|
position on disk, it first looks through the extent records in
|
|
the catalog file record. If the fork offset is within one
|
|
these extents, the implementation can find the corresponding
|
|
position without consulting the extents overflow file.</P>
|
|
|
|
<P>If, on the other hand, the fork offset is beyond the last
|
|
extent recorded in the catalog file record, the
|
|
implementation must look in the next extent record, which is
|
|
stored in the extents overflow file. To find this record,
|
|
the implementation must form a key, which consists of
|
|
information about the fork (the fork type and the file ID)
|
|
and the offset info the fork (the start block).</P>
|
|
|
|
<P>Because extent records are partially keyed off the fork
|
|
offset of the first extent in the record, the implementation
|
|
must have all the preceding extent records in order to know
|
|
the fork offset to form the key of the next extent record.
|
|
For example, if the fork has two extent records in the
|
|
extents overflow file, the implementation must read the
|
|
first extent record to calculate the fork offset for the key
|
|
for the second extent record.</P>
|
|
|
|
<P>However, you can use the <CODE>startBlock</CODE> in the
|
|
extent key to go directly to the record you need. Here's a
|
|
complicated example:</P>
|
|
|
|
<P>We've got a fork with a total of 23 extents (very
|
|
fragmented!). The <CODE>blockCounts</CODE> for the extents,
|
|
in order, are as follows: one extent of 6 allocation blocks,
|
|
14 extents of one allocation block each, two extents of two
|
|
allocation blocks each, one extent of 7 allocation blocks,
|
|
and five more extents of one allocation block each. The fork
|
|
contains a total of 36 allocation blocks.</P>
|
|
|
|
<P>The block counts for the catalog's fork data are: 6, 1,
|
|
1, 1, 1, 1, 1, 1. There is an extent overflow record whose
|
|
startBlock is 13 (0+6+1+1+1+1+1+1+1), and has the following
|
|
block counts: 1, 1, 1, 1, 1, 1, 1, 2. There is a second
|
|
extent overflow record whose <CODE>startBlock</CODE> is 22
|
|
(13+1+1+1+1+1+1+1+2), and has the following block counts: 2,
|
|
7, 1, 1, 1, 1, 1, 0. Note this last record only contains
|
|
seven extents.</P>
|
|
|
|
<P>Suppose the allocation block size for the volume is 4K.
|
|
Suppose we want to start reading from the file at an offset
|
|
of 108K. We want to know where the data is on the volume,
|
|
and how much contiguous data is there.</P>
|
|
|
|
<P>First, we divide 108K (the fork offset) by 4K (the
|
|
allocation block size) to get 27, which is the number of
|
|
allocation blocks from the start of the fork. So, we want to
|
|
know where fork allocation block #27 is. We notice that 27
|
|
is greater than or equal to 13 (the number of allocation
|
|
blocks in the catalog's fork data), so we're going to have
|
|
to look in the extents B-tree.</P>
|
|
|
|
<P>We construct a search key with the appropriate
|
|
<CODE>fileID</CODE> and <CODE>forkType</CODE>, and set
|
|
<CODE>startBlock</CODE> to 27 (the desired fork allocation
|
|
block number). We then search the extents B-tree for the
|
|
record whose key is less than or equal to our search key. We
|
|
find the second extent overflow record (the one with
|
|
<CODE>startBlock</CODE>=22). It has the same
|
|
<CODE>fileID</CODE> and <CODE>forkType</CODE>, so things are
|
|
good. Now we just need to figure out which extent within
|
|
that record to use.</P>
|
|
|
|
<P>We compute 27 (the desired fork allocation block) minus
|
|
22 (the <CODE>startBlock</CODE>) and get 5. So, we want the
|
|
extent that is 5 allocation blocks "into" the record. We try
|
|
the first extent. It's only two allocation blocks long, so
|
|
the desired extent is 3 allocation blocks after that first
|
|
extent in the record. The next extent is 7 allocation blocks
|
|
long. Since 7 is greater than 3, we know the desired fork
|
|
position is within this extent (the second extent in the
|
|
second overflow record). Further, we know that there are
|
|
7-3=4 contiguous allocation blocks (i.e., 16K).</P>
|
|
|
|
<P>We grab the <CODE>startBlock</CODE> for that second
|
|
extent (i.e., the one whose <CODE>blockCount</CODE> is 7);
|
|
suppose this number is 444. We add 3 (since the desired
|
|
position was 3 allocation blocks into the extent we found).
|
|
So, the desired position is in allocation block 444+3=447 on
|
|
the volume. That is 447*4K=1788K from the start of the HFS
|
|
Plus volume. (Since the Volume Header always starts 1K after
|
|
the start of the HFS Plus volume, the desired fork position
|
|
is 1787K after the start of the Volume Header.)</P>
|
|
|
|
<H3><a NAME="BadBlockFile"></a>Bad Block File</H3>
|
|
|
|
<P>The extent overflow file is also used to hold information
|
|
about the bad block file. The bad block file is used to mark
|
|
areas on the disk as bad, unable to be used for storing
|
|
data. This is typically used to map out bad sectors on the
|
|
disk.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
All space on an HFS Plus volume is allocated in
|
|
terms of allocation blocks. Typically, allocation
|
|
blocks are larger than sectors. If a sector is
|
|
found to be bad, the entire allocation block is
|
|
unusable.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>When an HFS Plus volume is embedded within an HFS wrapper
|
|
(the way Mac OS normally initializes a hard disk), the space
|
|
used by the HFS Plus volume is marked as part of the bad
|
|
block file <EM>within the HFS wrapper itself</EM>. (This
|
|
sounds confusing because you have a volume within another
|
|
volume.)</P>
|
|
|
|
<P>The bad block file is not a file in the same sense as a
|
|
user file (it doesn't have a file record in the catalog) or
|
|
one of the special files (it's not referenced by the volume
|
|
header). Instead, the bad block file uses a special
|
|
<a HREF = "#CNID">CNID</a>
|
|
(<CODE>kHFSBadBlockFileID</CODE>) as the key for extent
|
|
records in the extents overflow file. When a block is marked
|
|
as bad, an extent with this CNID and encompassing the bad
|
|
block is added to the extents overflow file. The block is
|
|
marked as used in the <a HREF = "#AllocationFile">allocation
|
|
file</a>. These steps prevent the block from being used for
|
|
data by the file system.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The bad block file is necessary because marking a
|
|
bad block as used in the allocation file is
|
|
insufficient. One
|
|
<a HREF = "#AllocationFileConsistencyCheck">common
|
|
consistency check</a> for HFS Plus volumes is to
|
|
verify that all the allocation blocks on the volume
|
|
are being used by real data. If such a check were
|
|
run on a volume with bad blocks that weren't also
|
|
covered by extents in the bad block file, the bad
|
|
blocks would be freed and might be reused for file
|
|
system data.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Bad block extent records are always assumed to reference
|
|
the data fork. The <CODE>forkType</CODE> field of the key
|
|
must be 0.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Because an extent record holds up to eight extents,
|
|
adding a bad block extent to the bad block file
|
|
does not necessarily require the addition of a new
|
|
extent record.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>HFS uses a similar mechanism to store information about
|
|
bad blocks. This facility is used by the
|
|
<a HREF = "#HFSWrapper">HFS Wrapper</a> to hold an entire HFS
|
|
Plus volume as bad blocks on an HFS disk.</p>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="AllocationFile"></a>Allocation File</H2>
|
|
|
|
<P>HFS Plus uses an allocation file to keep track of whether
|
|
each allocation block in a volume is currently allocated to
|
|
some file system structure or not. The contents of the
|
|
allocation file is a bitmap. The bitmap contains one bit for
|
|
each allocation block in the volume. If a bit is set, the
|
|
corresponding allocation block is currently in use by some
|
|
file system structure. If a bit is clear, the corresponding
|
|
allocation block is not currently in use, and is available
|
|
for allocation.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
HFS stores allocation information in a special area
|
|
on the volume, known as the <B>volume bitmap</B>.
|
|
The allocation file mechanism used by HFS Plus has
|
|
a number of advantages.</P>
|
|
|
|
<UL>
|
|
<li>Using a file allows the bitmap itself to be
|
|
allocated from allocation blocks. This
|
|
simplifies the design, since volumes are now
|
|
comprised of only one type of block -- the
|
|
allocation block. The HFS is slightly more
|
|
complex because it uses 512-byte blocks to hold the
|
|
allocation bitmap and allocation blocks to hold
|
|
file data.</li>
|
|
|
|
<li>The allocation file does not have to be
|
|
contiguous, which allows allocation information
|
|
and user data to be interleaved. Many modern
|
|
file systems do this to reduce head travel when
|
|
growing files.</li>
|
|
|
|
<li>The allocation file can be extended, which
|
|
makes it significantly easier to increase the
|
|
number of allocation blocks on a disk. This is
|
|
useful if you want to either decrease the
|
|
allocation block size on a disk, or increase the
|
|
total disk size.</li>
|
|
|
|
<li>The allocation file may be shrunk. This
|
|
makes it easy to create a disk images suitable
|
|
for volumes of varying sizes. The allocation
|
|
file in the disk image is sized to hold enough
|
|
allocation data for the largest disk, and shrunk
|
|
back when the disk is written to a smaller disk.</li>
|
|
</UL>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Each byte in the allocation file holds the state of eight
|
|
allocation blocks. The byte at offset X into the file
|
|
contains the allocation state of allocations blocks (X * 8)
|
|
through (X * 8 + 7). Within each byte, the most significant
|
|
bit holds information about the allocation block with the
|
|
lowest number, the least significant bit holds information
|
|
about the allocation block with the highest number. Listing
|
|
1 shows how you would test whether an allocation block is in
|
|
use, assuming that you've read the entire allocation file
|
|
into memory.</P>
|
|
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>static Boolean IsAllocationBlockUsed(UInt32 thisAllocationBlock,
|
|
UInt8 *allocationFileContents)
|
|
{
|
|
UInt8 thisByte;
|
|
|
|
thisByte = allocationFileContents[thisAllocationBlock / 8];
|
|
return (thisByte & (1 << (7 - (thisAllocationBlock % 8)))) != 0;
|
|
}</pre>
|
|
</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD><P><B>Listing 1</B> Determining whether an
|
|
allocation block is in use.</P>
|
|
</td></tr>
|
|
</table></CENTER>
|
|
|
|
|
|
|
|
|
|
<P>The size of the allocation file depends on the number of
|
|
allocation blocks in the volume, which in turn depends both
|
|
on the size of the disk and on the size of the
|
|
volume's allocation blocks. For example, a volume on a 1 GB disk and
|
|
having an allocation block size of 4 KB needs an allocation
|
|
file size of 256 Kbits (32 KB, or 8 allocation blocks).
|
|
Since the allocation file itself is allocated using
|
|
allocation blocks, it always occupies an integral number of
|
|
allocation blocks (its size may be rounded up).</P>
|
|
|
|
<P>The allocation file may be larger than the minimum number
|
|
of bits required for the given volume size. Any unused bits
|
|
in the bitmap must be set to zero.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Since the number of allocation blocks is determined
|
|
by a 32-bit number, the size of the allocation file
|
|
can be up to 512 MB in size, a radical increase
|
|
over HFS's 8 KB limit.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
Because the entire volume is composed of allocation
|
|
blocks (with the possible
|
|
exception of the alternate volume header, as
|
|
described <a HREF = "#OddSizeVolumes">above</a>), the volume header,
|
|
alternate volume header, and reserved areas (the
|
|
first 1024 bytes and the last 512 bytes) must be
|
|
marked as allocated in the allocation file. The
|
|
actual number of allocation blocks allocated for
|
|
these areas varies with the size of the
|
|
allocation blocks. Any allocation block that
|
|
contains any of these areas must be marked
|
|
allocated.</P>
|
|
|
|
<P>For example, if 512-byte allocation blocks are
|
|
used, the first three and last two allocation
|
|
blocks are allocated. With 1024-byte allocation
|
|
blocks, the first two and the last allocation
|
|
blocks are allocated. For larger allocation block
|
|
sizes, only the first and last allocation blocks
|
|
are allocated for these areas.</P>
|
|
|
|
<P>See the <a HREF = "#VolumeHeader">Volume
|
|
Header</a> section for a description of these
|
|
areas.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="AttributesFile"></a>Attributes File</H2>
|
|
|
|
<P>The HFS Plus attributes file is reserved for implementing
|
|
named forks in the future. An attributes file is organized
|
|
as a <a HREF = "#BTrees">B-tree</a> file. It a special file,
|
|
described by an <CODE>HFSPlusForkData</CODE> record in the
|
|
volume header, with no entry in the catalog file. An
|
|
attributes files has a variable length key and three data
|
|
record types, which makes it roughly as complex as the
|
|
catalog file.</P>
|
|
|
|
<P>It is possible for a volume to have no attributes file.
|
|
If the first extent of the attributes file (stored in the
|
|
volume header) has zero allocation blocks, the attributes
|
|
file does not exist.</P>
|
|
|
|
<P>The B-Trees chapter defined a standard rule for the
|
|
<a HREF = "#NodeSizes">node size of HFS Plus B-trees</a>. As
|
|
the attributes file is a B-tree, it inherits the
|
|
requirements of this rule. In addition, the node size of the
|
|
attributes file must be at least 4 KB
|
|
(<CODE>kHFSPlusAttrMinNodeSize</CODE>).</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The exact organization of the attributes B-tree has
|
|
not been fully designed. Specifically:</P>
|
|
|
|
<UL>
|
|
<li>the structure of the keys in the attribute
|
|
B-tree has not been finalized and is subject to
|
|
change, and</li>
|
|
|
|
<li>addition attribute's file data record types
|
|
may be defined.</li>
|
|
</UL>
|
|
|
|
<P>An implementation written to this specification
|
|
may use the details that are final to perform basic
|
|
consistency checks on attributes. These checks will
|
|
be compatible with future implementations written
|
|
to a final attributes specification. See
|
|
<a HREF = "#AttributesAllocationFileConsistency">Attributes
|
|
and the Allocation File Consistency Check</a>.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<H3>Attributes File Data</H3>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
Several types of attributes file data records are
|
|
defined. It is possible that additional record
|
|
types will be defined in future specifications.
|
|
Implementations written to this specification must
|
|
ignore record types not defined here.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The leaf nodes of an attributes file contain data
|
|
records, known as <B>attributes</B>. There are two types of
|
|
attributes:</P>
|
|
|
|
<OL>
|
|
<li><B>Fork data attributes</B> are used for attributes
|
|
whose data is large. The attribute's data is stored in
|
|
extents on the volume and the attribute merely contains a
|
|
reference to those extents.</li>
|
|
|
|
<li><B>Extension attributes</B> augment fork data
|
|
attributes, allowing an fork data attribute to have more
|
|
than eight extents.</li>
|
|
</OL>
|
|
|
|
<P>Each record starts with a <CODE>recordType</CODE> field,
|
|
which describes the type of attribute data record. The
|
|
<CODE>recordType</CODE> field contains one of the following
|
|
values.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kHFSPlusAttrInlineData = 0x10,
|
|
kHFSPlusAttrForkData = 0x20,
|
|
kHFSPlusAttrExtents = 0x30
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The values have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>kHFSPlusAttrInlineData</CODE></dt>
|
|
|
|
<DD>Reserved for future use.</dd>
|
|
|
|
<DT><CODE>kHFSPlusAttrForkData</CODE></dt>
|
|
|
|
<DD>This record is a <a HREF = "#ForkDataAttributes">fork
|
|
data attribute</a>. You can use the
|
|
<CODE>HFSPlusAttrForkData</CODE> type to interpret the
|
|
data.</dd>
|
|
|
|
<DT><CODE>kHFSPlusAttrExtents</CODE></dt>
|
|
|
|
<DD>This record is an
|
|
<a HREF = "#ExtensionAttributes">extension attribute</a>.
|
|
You can use the <CODE>HFSPlusAttrExtents</CODE> type to
|
|
interpret the data. A record of type
|
|
<CODE>kHFSPlusAttrExtents</CODE> is really just overflow
|
|
extents for a corresponding record of type
|
|
<CODE>kHFSPlusAttrForkData</CODE>. (Think of
|
|
<CODE>kHFSPlusAttrForkData</CODE> as being like a catalog
|
|
record and <CODE>kHFSPlusAttrExtents</CODE> as being like
|
|
an extents overflow record.)</dd>
|
|
</DL>
|
|
|
|
<P>The next two sections describe the fork data and
|
|
extension attributes in detail.</P>
|
|
|
|
<H4><a NAME="ForkDataAttributes"></a>Fork Data Attributes
|
|
</H4>
|
|
|
|
<P>A fork data attribute is defined by the
|
|
<CODE>HFSPlusAttrForkData</CODE> data type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusAttrForkData {
|
|
UInt32 recordType;
|
|
UInt32 reserved;
|
|
HFSPlusForkData theFork;
|
|
};
|
|
typedef struct HFSPlusAttrForkData HFSPlusAttrForkData;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>recordType</CODE></dt>
|
|
|
|
<DD>The attribute data record type. For fork data
|
|
attributes, this is always
|
|
<CODE>kHFSPlusAttrForkData</CODE>.</dd>
|
|
|
|
<DT><CODE>reserved</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
|
|
<DT><CODE>theFork</CODE></dt>
|
|
|
|
<DD>Information about the location and size of the
|
|
attribute data. See <a HREF = "#ForkDataStructure">Fork
|
|
Data Structure</a> for a description of the
|
|
<CODE>HFSPlusForkData</CODE> type.</dd>
|
|
</DL>
|
|
|
|
<H4><a NAME="ExtensionAttributes"></a>Extension Attributes
|
|
</H4>
|
|
|
|
<P>A extension attribute is defined by the
|
|
<CODE>HFSPlusAttrExtents</CODE> data type.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HFSPlusAttrExtents {
|
|
UInt32 recordType;
|
|
UInt32 reserved;
|
|
HFSPlusExtentRecord extents;
|
|
};
|
|
typedef struct HFSPlusAttrExtents HFSPlusAttrExtents;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>recordType</CODE></dt>
|
|
|
|
<DD>The attribute data record type. For extension
|
|
attributes, this is always
|
|
<CODE>kHFSPlusAttrExtents</CODE>.</dd>
|
|
|
|
<DT><CODE>reserved</CODE></dt>
|
|
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">reserved</a> field.</dd>
|
|
|
|
<DT><CODE>extents</CODE></dt>
|
|
|
|
<DD>The eight extents of the attribute data described by
|
|
this record. See <a HREF = "#ForkDataStructure">Fork Data
|
|
Structure</a> for a description of the
|
|
<CODE>HFSPlusExtentRecord</CODE> type.</dd>
|
|
</DL>
|
|
|
|
<H3><a NAME="AttributesAllocationFileConsistency"></a>Attributes
|
|
and the Allocation File Consistency Check</H3>
|
|
|
|
<P>While the key structure for the attributes file is not
|
|
fully specified, it is still possible for an implementation
|
|
to use attribute file information in its allocation file
|
|
consistency check. The leaf records of the attribute file
|
|
are fully defined, so the implementation can simply iterate
|
|
over them to determine which allocation blocks on the disk
|
|
are being used by fork data attributes.</P>
|
|
|
|
<P>See <a HREF = "#AllocationFileConsistencyCheck">Allocation
|
|
File Consistency Check</a> for details.</p><BR>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="StartupFile"></a>Startup File</H2>
|
|
|
|
<P>The startup file is a special file intended to hold
|
|
information needed when booting a system that does not have
|
|
built-in (ROM) support for HFS Plus. A boot loader can find
|
|
the startup file without full knowledge of the HFS Plus
|
|
volume format (B-trees, catalog file, and so on). Instead,
|
|
the <a HREF = "#VolumeHeader">volume header</a> contains the
|
|
location of the first eight extents of the startup file.</p>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
It is legal for the startup file to contain more than eight
|
|
extents, and for the remaining extents to be placed in the
|
|
extents overflow file. However, doing so defeats the purpose
|
|
of the startup file.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Mac OS does not use the startup file to boot from HFS Plus
|
|
disks. Instead, it uses the HFS wrapper, as
|
|
<a HREF = "#HFSWrapper">described later in this document</a>.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="HardLinks"></a>Hard Links</H2>
|
|
|
|
<P><B>Hard links</b> are a feature that allows multiple directory entries
|
|
to refer to a single file's content. They are a way to give a single
|
|
file multiple names, possibly in multiple directories. This section
|
|
describes how Mac OS X implements hard links on HFS Plus volumes.</p>
|
|
|
|
<P>The Mac OS X implementation of hard links on HFS Plus volumes
|
|
was done using the existing metadata fields of the catalog records.
|
|
This makes it possible to back up and restore a volume using hard
|
|
links, by backing up and restoring individual files, without having
|
|
to understand or interpret the hard links. An HFS Plus implementation
|
|
may choose to automatically follow hard links, or not.</p>
|
|
|
|
<P>Hard links in HFS Plus are represented by a set of several files.
|
|
The actual file content (which is shared by each of the hard links)
|
|
is stored in a special <B>indirect node file</b>. This indirect node file
|
|
is the equivalent of an inode in a traditional UNIX file system.</p>
|
|
|
|
<P>HFS Plus uses special <b>hard link files</b> (or <b>links</b>)
|
|
to refer (or point) to an indirect node file. There is one hard link
|
|
file for each directory entry or name that refers to the file content.</p>
|
|
|
|
<P>Indirect node files exist in a special directory called the
|
|
<b>metadata directory</b>. This directory exists in the volume's root
|
|
directory. The name of the metadata directory is four null
|
|
characters followed by the string "HFS+ Private Data". The
|
|
directory's creation date is set to the creation date of the
|
|
volume's root directory. The <code>kIsInvisible</code> and
|
|
<code>kNameLocked</code> bits are set in the directory's
|
|
<a href="#FinderInfo">Finder information</a>. The icon
|
|
<code>location</code> in the Finder info is set to the point
|
|
(22460, 22460). These Finder info settings are not mandatory,
|
|
but they tend to reduce accidental changes to the metadata directory.
|
|
An implementation that automatically follows hard links should
|
|
make the metadata directory inaccessable from its normal
|
|
file system interface.</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The <a href="#StringComparisonAlgorithm">case-insensitive Unicode
|
|
string comparison</a> used by HFS Plus and case-insensitive
|
|
<a href="#HFSX">HFSX</a> sorts null characters after all other
|
|
characters, so the metadata directory will typically be the last
|
|
item in the root directory. On case-sensitive <a href="#HFSX">HFSX</a>
|
|
volumes, null characters sort before other characters, so the
|
|
metadata directory will typically be the first item in the root directory.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Indirect node files have a special identifying number called a
|
|
<b>link reference</b>. The link reference is unique among indirect
|
|
node files on a given volume. The link reference is not related to
|
|
<a HREF = "#CNID">catalog node IDs</a>. When a new indirect node
|
|
file is created, it is assigned a new link reference randomly chosen
|
|
from the range 100 to 1073741923.</p>
|
|
|
|
<P>The file name of an indirect node file is the string "iNode"
|
|
immediately followed by the link reference converted to decimal text,
|
|
with no leading zeroes. For example, an indirect node file with link
|
|
reference 123 would have the name "iNode123".</p>
|
|
|
|
<P>An indirect node file <B>must</B> be a file, not a directory.
|
|
Hard links to directories are not allowed because they could cause cycles
|
|
in the directory hierarchy if a hard link pointed to one of its ancestor
|
|
directories.</p>
|
|
|
|
<P>The <CODE>linkCount</CODE> field in the
|
|
<a HREF = "#HFSPlusPermissions">permissions</a> is an estimate of
|
|
the number of links referring to this indirect node file. An
|
|
implementation that understands hard links should increment this
|
|
value when creating an additional link, and decrement the value
|
|
when removing a link. However, some implementations (such as
|
|
traditional Mac OS) do not understand hard links and may make
|
|
changes that cause the <CODE>linkCount</CODE> to be inaccurate.
|
|
Similarly, it is possible for a link to refer to an indirect
|
|
node file that does not exist. When removing a link, an
|
|
implementation should not allow the <CODE>linkCount</CODE>
|
|
to underflow; if it is already zero, do not change it.</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The inode number returned by the POSIX <CODE>stat</CODE>
|
|
or <CODE>lstat</CODE> routines in the <CODE>st_ino</CODE>
|
|
field of the <CODE>stat</CODE> structure is actually the
|
|
<a HREF = "#CNID">catalog node ID</a> of the indirect
|
|
node file, not the link reference mentioned above.</p>
|
|
|
|
<P>The reason for using a separate link reference number, instead of a
|
|
<a HREF = "#CNID">catalog node ID</a>, is to allow hard links to be
|
|
backed up and restored by utilities that are not specifically aware
|
|
of hard links. As long as they preserve filenames, Finder info,
|
|
and <a HREF = "#HFSPlusPermissions">permissions</a>, then
|
|
the hard links will be preserved.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Hard link files are ordinary files in the catalog. The
|
|
<a HREF = "#CNID">catalog node ID</a> of a hard link
|
|
file is different from the <a HREF = "#CNID">catalog node ID</a>
|
|
of the indirect node file it refers to, and different from the
|
|
<a HREF = "#CNID">catalog node ID</a> of any other hard link file.</p>
|
|
|
|
<P>The <CODE>fileType</CODE> and <CODE>fileCreator</CODE> fields
|
|
of the <CODE>userInfo</CODE> in the <a href="#CatalogFileRecord">
|
|
catalog record</a> of a hard link file must be set to
|
|
<CODE>kHardLinkFileType</CODE> and <CODE>kHFSPlusCreator</CODE>,
|
|
respectively. The hard link file's creation date should be set to
|
|
the creation date of the metadata directory. The hard link file's
|
|
creation date may also be set to the creation date of the volume's
|
|
root directory (if it differs from the creation date of the metadata
|
|
directory), though this is deprecated. The <CODE>iNodeNum</CODE> field
|
|
in the <a HREF = "#HFSPlusPermissions">permissions</a> is set to the
|
|
link reference of the indirect node file that the link refers to.
|
|
For better compatibility with older versions of the Mac OS Finder,
|
|
the <CODE>kHasBeenInited</CODE> flag should be set in the Finder
|
|
flags. The other Finder information, and other dates in the catalog
|
|
record are <a HREF = "#ReservedAndPadFields">reserved</a>.</p>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kHardLinkFileType = 0x686C6E6B, /* 'hlnk' */
|
|
kHFSPlusCreator = 0x6866732B /* 'hfs+' */
|
|
};</pre></TD></TR>
|
|
</table></CENTER>
|
|
|
|
<P><a NAME = "UnlinkedFiles"></a>POSIX semantics allow an open file to
|
|
be unlinked (deleted). These open but unlinked files are stored on HFS
|
|
Plus volumes much like a hard link. When the open file is deleted, it
|
|
is renamed and moved into the metadata directory. The new name is the
|
|
string "temp" followed by the <a HREF = "#CNID">catalog node ID</a>
|
|
converted to decimal text. When the file is eventually closed, this
|
|
temporary file may be removed. All such temporary files may be removed
|
|
when repairing an unmounted HFS Plus volume.</p>
|
|
|
|
<H3>Repairing the Metadata Directory</H3>
|
|
<P>When repairing an HFS Plus volume with hard links or a metadata
|
|
directory, there are several conditions that might need to be repaired:</P>
|
|
<UL>
|
|
<li>Opened but deleted files (which are now orphaned).</li>
|
|
<li>Orphaned indirect node files (no hard links refer to them).</li>
|
|
<li>Broken hard link (hard link exists, but indirect node file does not).</li>
|
|
<li>Incorrect link count.</li>
|
|
<li>Link reference was 0.</li>
|
|
</UL>
|
|
|
|
<P>Opened but deleted files are files whose names start with "temp",
|
|
and are in the metadata directory. If the volume is not in use
|
|
(not mounted, and not being used by any other utility), then these
|
|
files can be deleted. Volumes with a <a href="#Journal">journal</a>,
|
|
even one with no active transactions, may have opened but undeleted
|
|
files that need to be deleted.</P>
|
|
|
|
<P>Detecting an orphaned indirect node file, broken hard link, or incorrect link
|
|
count requires finding all hard link files in the catalog, and comparing
|
|
the number of found hard links for each link reference with the link
|
|
count of the corresponding indirect node file.</P>
|
|
|
|
<P>A hard link with a link reference equal to 0 is invalid. Such a hard
|
|
link may be the result of a hard link being copied or restored by an
|
|
implementation or utility that does not use the <a href="#HFSPlusPermissions">
|
|
permissions</a> in catalog records. It may be possible to repair the
|
|
hard link by determining the proper link reference. Otherwise, the
|
|
hard link should be deleted.</P>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="Symlinks"></a>Symbolic Links</H2>
|
|
|
|
<P>Similar to a hard link, a <b>symbolic link</b> is
|
|
a special kind of file that refers to another file or directory.
|
|
A symbolic link stores the path name of the file or directory it
|
|
refers to.</p>
|
|
|
|
<P>On an HFS Plus volume, a symbolic link is stored
|
|
as an ordinary file with special values in some of the fields of its
|
|
<a href="#CatalogFileRecord">catalog record</a>. The pathname of the
|
|
file being referred to is stored in the data fork. The file type in
|
|
the <code>fileMode</code> field of the <a href="#HFSPlusPermissions">
|
|
permissions</a> is set to <code>S_IFLNK</code>. For compatibility
|
|
with Carbon and Classic applications, the file type of a symbolic
|
|
link is set to <code>kSymLinkFileType</code>, and the creator code
|
|
is set to <code>kSymLinkCreator</code>. The resource fork of the
|
|
symbolic link has zero length and is
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</p>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kSymLinkFileType = 0x736C6E6B, /* 'slnk' */
|
|
kSymLinkCreator = 0x72686170 /* 'rhap' */
|
|
};</pre></TD></TR>
|
|
</table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The pathname stored in a symbolic link is assumed to be a POSIX
|
|
pathname, as used by the Mac OS X BSD and Cocoa programming interfaces.
|
|
It is <b>not</b> a traditional Mac OS, or Carbon, pathname. The path
|
|
is encoded in UTF-8. It must be a valid UTF-8 sequence, with no null
|
|
(zero) bytes. The path may refer to another volume. The path need
|
|
not refer to any existing file or directory. The path may be full
|
|
or partial (with or without a leading forward slash). For maximum
|
|
compatibility, the length of the path should be 1024 bytes or less.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="Journal"></a>Journal</H2>
|
|
|
|
<P>An HFS Plus volume may have an optional <b>journal</b> to speed
|
|
recovery when mounting a volume that was not unmounted safely
|
|
(for example, as the result of a power outage or crash). The
|
|
journal makes it quick and easy to restore the volume
|
|
structures to a consistent state, without having to scan all of
|
|
the structures. The journal is used only for the volume
|
|
structures and metadata; it does not protect the contents of a
|
|
fork.</P>
|
|
|
|
<H3>Background</H3>
|
|
|
|
<P>A single change to the volume may require writing coordinated
|
|
changes to many different places on the volume. If a failure
|
|
happens after some, but not all, of the changes have been
|
|
written, then the volume may be seriously damaged and may result
|
|
in catastrophic loss of data.</P>
|
|
|
|
<P>For example, creating a file or directory requires adding two
|
|
records (the file or folder record, and its thread record) to the
|
|
catalog B-tree. A leaf node may not have enough room for a new
|
|
record, so it may have to be split. That means that some records
|
|
will be removed from the existing node and put into a newly
|
|
allocated node. This requires adding a new key and pointer to
|
|
the index node that is the parent of the leaf being split (which
|
|
might require splitting the index node, and so on). If a failure
|
|
occurs after the split, but before the index node is updated,
|
|
all of the items in the new node would become inaccessible.
|
|
Recovery from this sort of damage is time consuming at best,
|
|
and may be impossible.</P>
|
|
|
|
<P>The purpose of the journal is to ensure that when a group of
|
|
related changes are being made, that either all of those changes
|
|
are actually made, or none of them are made. This is done by
|
|
gathering up all of the changes, and storing them in a separate
|
|
place (in the journal). Once the journal copy of the changes
|
|
is completely written to disk, the changes can actually be
|
|
written to their normal locations on disk. If a failure happens
|
|
at that time, the changes can simply be copied from the
|
|
journal to their normal locations. If a failure happens when
|
|
the changes are being written to the journal, but before they are
|
|
marked complete, then all of those changes are ignored.</P>
|
|
|
|
<P>A group of related changes is called a <b>transaction</b>.
|
|
When all of the changes of a transaction have been written to
|
|
their normal locations on disk, that transaction has been
|
|
<b>committed</b>, and is removed from the journal. The journal
|
|
may contain several transactions. Copying changes from all
|
|
transactions to their normal locations on disk is called
|
|
<b>replaying</b> the journal.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
Implementations accessing a journaled volume with transactions
|
|
must either refuse to access the volume, or replay the journal
|
|
to be sure the volume is consistent. If the
|
|
<code>lastModifiedVersion</code> field of the
|
|
<a href="#VolumeHeader">volume header</a> does not match the
|
|
signature of an implementation known to properly use and update
|
|
the journal, then the journal must not be replayed (since it may
|
|
no longer match the on-disk structures, and could cause
|
|
corruption if replayed).</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3>Overview of Journal Data Structures</H3>
|
|
|
|
<P>If <code>kHFSVolumeJournaledBit</code> is set in the <a
|
|
href="#VolumeHeader">volume header's</a> <code>attributes</code> field, the
|
|
volume has a journal. The journal data stuctures consist of a
|
|
journal info block, journal header, and journal buffer. The
|
|
<b>journal info block</b> indicates the location and size of the
|
|
journal header and journal buffer. The <b>journal buffer</b> is
|
|
the space set aside to hold transactions. The <b>journal
|
|
header</b> describes which part of the journal buffer is active
|
|
and contains transactions waiting to be committed.</P>
|
|
|
|
<CENTER><img src="images/tn1150_007.png" alt="Overview of an HFS Plus Journal" width="441" height="504" align=bottom>
|
|
<P><B>Figure 7</B>. Overview of an HFS Plus Journal.</P>
|
|
</center>
|
|
|
|
<P>On HFS Plus volumes, the journal info block is stored as a
|
|
file (so that its space can be properly represented in a catalog
|
|
record and the allocation bitmap). The name of that file is
|
|
<code>".journal_info_block"</code> and it is stored in the
|
|
volume's root directory. The journal header and journal buffer
|
|
are stored together in a different file named
|
|
<code>".journal"</code>, also in the volume's root directory.
|
|
Each of these files are contiguous on disk (they occupy exactly
|
|
one extent). An implementation that uses the journal must
|
|
prevent the files from being accessed as normal files.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
An implementation must find the journal info block by using the
|
|
<code>journalInfoBlock</code> field of the volume header, not by
|
|
the file name. Similarly, an implementation must find the
|
|
journal header and journal buffer using the contents of the
|
|
journal info block, not the file name.</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>A single transaction consists of several blocks, including
|
|
both the data to be written, and the location where that data is
|
|
to be written. This is represented on disk by a <b>block list
|
|
header</b>, which describes the number and sizes of the blocks,
|
|
immediately followed by the contents of those blocks.</P>
|
|
|
|
<CENTER><img src="images/tn1150_008.png" alt="A Simple Transaction" width="288" height="360" align=bottom>
|
|
<P><B>Figure 8</B>. A Simple Transaction.</P>
|
|
</center>
|
|
|
|
<P>Since block list headers are of limited size, a single
|
|
transaction may consist of several block list headers and their
|
|
associated block contents (one block list header followed by the
|
|
contents of the blocks that header describes, then the next
|
|
block list header and its block contents, and so on). If the
|
|
<code>next</code> field of the first <code>block_info</code> is
|
|
non-zero, then the next block list header is a continuation of
|
|
the same transaction.</P>
|
|
|
|
<CENTER><img src="images/tn1150_009.png" alt="A Transaction with Multiple Block Lists" width="288" height="657">
|
|
<P><B>Figure 9</B>. A Transaction with Multiple Block Lists.</P>
|
|
</center>
|
|
|
|
<P>The journal buffer is treated as a circular buffer. When
|
|
reading or writing the journal buffer, the I/O operation must
|
|
stop at the end of the journal buffer and resume (wrap around)
|
|
immediately following the journal header. Block list headers or
|
|
the contents of blocks may wrap around in this way. Only a
|
|
portion of the journal buffer is active at any given time; this
|
|
portion is indicated by the <code>start</code> and
|
|
<code>end</code> fields of the journal header. The part of the
|
|
journal buffer that is not active contains no meaningful data,
|
|
and must be ignored.</P>
|
|
|
|
<P>To prevent ambiguity when <code>start</code> equals
|
|
<code>end</code>, the journal is never allowed to be perfectly
|
|
full (all of the journal buffer used by block lists and blocks).
|
|
If the journal was perfectly full, and <code>start</code> was
|
|
not equal to <code>jhdr_size</code>, then <code>end</code> would
|
|
be equal to <code>start</code>. You would then be unable to
|
|
differentiate between an empty and full journal.</p>
|
|
|
|
<P>When the journal is not empty (contains transactions),
|
|
it must be <a href="#ReplayJournal">replayed</a> to be sure the volume
|
|
is consistent. That is, the data from each of the transactions must be
|
|
written to the correct blocks on disk.</p>
|
|
|
|
<H3><a NAME="JournalInfoBlock"></a>Journal Info Block</H3>
|
|
|
|
<P>The <b>journal info block</b> describes where the journal header and
|
|
journal buffer are stored. The journal info block is stored at the
|
|
beginning of the allocation block whose number is stored in the
|
|
<CODE>journalInfoBlock</CODE> field of the
|
|
<a href="#VolumeHeader">volume header</a>. The journal info block
|
|
is described by the data type <CODE>JournalInfoBlock</CODE>.</P>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct JournalInfoBlock {
|
|
UInt32 flags;
|
|
UInt32 device_signature[8];
|
|
UInt64 offset;
|
|
UInt64 size;
|
|
UInt32 reserved[32];
|
|
};
|
|
typedef struct JournalInfoBlock JournalInfoBlock;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>flags</CODE></dt>
|
|
|
|
<DD>Contains a set of one-bit flags. The currently
|
|
defined bits are <a HREF = "#JIBFlags">described below</a>.
|
|
An implementation must treat the undefined bits as
|
|
<a HREF = "#ReservedAndPadFields">reserved</a>.</dd>
|
|
|
|
<DT><CODE>device_signature</CODE></dt>
|
|
|
|
<DD>This space is <a HREF = "#ReservedAndPadFields">reserved</a>
|
|
for describing the device containing the journal when the
|
|
journal is not stored in the volume itself (when
|
|
<CODE>kJIJournalOnOtherDeviceMask</CODE> is set).</dd>
|
|
|
|
<DT><CODE>offset</CODE></dt>
|
|
|
|
<DD>The offset in bytes from the start of the device to
|
|
the start of the journal header. When the journal is stored
|
|
in the volume itself (<CODE>kJIJournalInFSMask</CODE>
|
|
is set), this offset is relative to the start of the volume.</dd>
|
|
|
|
<DT><CODE>size</CODE></dt>
|
|
|
|
<DD>The size in bytes of the journal, including the journal
|
|
header and the journal buffer. This size does not include
|
|
the journal info block.</dd>
|
|
|
|
<DT><CODE>reserved</CODE></dt>
|
|
|
|
<DD>This space is <a HREF = "#ReservedAndPadFields">reserved</a>.</dd>
|
|
</DL>
|
|
|
|
<P>The following constants define bit flags in the <CODE>flags</CODE> field:
|
|
</P>
|
|
|
|
<a NAME="JIBFlags"></a><CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>enum {
|
|
kJIJournalInFSMask = 0x00000001,
|
|
kJIJournalOnOtherDeviceMask = 0x00000002,
|
|
kJIJournalNeedInitMask = 0x00000004
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The values have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>kJIJournalInFSMask</CODE></dt>
|
|
|
|
<DD>When set, the space for the journal header and
|
|
transactions resides inside the volume being journaled.
|
|
The <CODE>offset</CODE> field of the journal info block
|
|
is relative to the start of the volume (allocation
|
|
block number zero).</dd>
|
|
|
|
<DT><CODE>kJIJournalOnOtherDeviceMask</CODE></dt>
|
|
|
|
<DD>When set, the space for the journal header and
|
|
journal buffer does not reside inside the volume being
|
|
journaled. The <CODE>device_signature</CODE> field
|
|
in the journal info block describes the device containing
|
|
the journal.</dd>
|
|
|
|
<DT><CODE>kJIJournalNeedInitMask</CODE></dt>
|
|
|
|
<DD>This bit is set to indicate that the journal header is invalid
|
|
and needs to be initialized. This bit is typically set when the
|
|
journal is first created, and the space has been allocated; the first
|
|
mount of the journaled volume typically initializes the journal header
|
|
and clears this bit. When this bit is set, there are no valid transactions
|
|
in the journal.</dd>
|
|
</DL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Implementations must currently set
|
|
<code>kJIJournalInFSMask</code>, but not
|
|
<CODE>kJIJournalOnOtherDeviceMask</CODE>. Journals stored on a
|
|
separate device are not currently supported. The format of the
|
|
<code>device_signature</code> field is not yet defined.</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3><a NAME="JournalHeader"></a>Journal Header</H3>
|
|
|
|
<P>The journal begins with a journal header, whose main purpose
|
|
is to describe the location of transactions in the journal
|
|
buffer. The journal header is stored using the
|
|
<CODE>journal_header</CODE> data type.</p>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>typedef struct journal_header {
|
|
UInt32 magic;
|
|
UInt32 endian;
|
|
UInt64 start;
|
|
UInt64 end;
|
|
UInt64 size;
|
|
UInt32 blhdr_size;
|
|
UInt32 checksum;
|
|
UInt32 jhdr_size;
|
|
} journal_header;
|
|
|
|
#define JOURNAL_HEADER_MAGIC 0x4a4e4c78
|
|
#define ENDIAN_MAGIC 0x12345678</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>magic</CODE></dt>
|
|
|
|
<DD>Contains the value <CODE>JOURNAL_HEADER_MAGIC</CODE> (0x4a4e4c78).
|
|
This is used to verify the integrity of the journal header.</dd>
|
|
|
|
<DT><CODE>endian</CODE></dt>
|
|
|
|
<DD>Contains the value <code>ENDIAN_MAGIC</code> (0x12345678).
|
|
This is used to verify the integrity of the journal header.</dd>
|
|
|
|
<DT><CODE>start</CODE></dt>
|
|
|
|
<DD>Contains the offset in bytes from the start of the journal
|
|
header to the start of the first (oldest) transaction.</dd>
|
|
|
|
<DT><CODE>end</CODE></dt>
|
|
|
|
<DD>Contains the offset in bytes from the start of the journal
|
|
header to the end of the last (newest) transaction. Note
|
|
that this field may be less than the <CODE>start</CODE> field,
|
|
indicating that the transactions wrap around the end of the
|
|
journal's circular buffer. If <CODE>end</CODE> equals
|
|
<CODE>start</CODE>, then the journal is empty, and there are
|
|
no transactions that need to be replayed.</dd>
|
|
|
|
<DT><CODE>size</CODE></dt>
|
|
|
|
<DD>The size of the journal, in bytes. This includes the journal
|
|
header and the journal buffer. This value must
|
|
be equal to the value in the <CODE>size</CODE> field of the
|
|
<a HREF = "#JournalInfoBlock">journal info block</a>.</dd>
|
|
|
|
<DT><CODE>blhdr_size</CODE></dt>
|
|
|
|
<DD>The size of one <a HREF = "#BlockListHeader">block list header</a>,
|
|
in bytes. This value typically ranges from 4096 to 16384.</dd>
|
|
|
|
<DT><CODE>checksum</CODE></dt>
|
|
|
|
<DD>The checksum of the journal header, computed as described
|
|
<a HREF = "#Checksum">below</a>.</dd>
|
|
|
|
<DT><CODE>jhdr_size</CODE></dt>
|
|
|
|
<DD>The size of the journal header, in bytes. The journal header
|
|
always occupies exactly one sector so that it
|
|
can be updated atomically. Therefore, this value is equal to the
|
|
sector size (for example, 2048 on many types of optical
|
|
media).</dd>
|
|
</DL>
|
|
|
|
<H3><a NAME="BlockListHeader"></a>Block List Header</H3>
|
|
|
|
<P>The <b>block list header</b> describes a list of blocks
|
|
included in a transaction. A transaction may include several
|
|
block lists if it modifies more blocks than can be represented
|
|
in a single block list. The block list header is stored in a
|
|
structure of type <CODE>block_list_header</CODE>.</p>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>typedef struct block_list_header {
|
|
UInt16 max_blocks;
|
|
UInt16 num_blocks;
|
|
UInt32 bytes_used;
|
|
UInt32 checksum;
|
|
UInt32 pad;
|
|
block_info binfo[1];
|
|
} block_list_header;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><CODE>max_blocks</CODE></dt>
|
|
|
|
<DD>The maximum number of blocks (<CODE>block_info</CODE>
|
|
items) this block list can describe. This field is used
|
|
while in memory to keep track of journal buffer sizes. On
|
|
disk, this field is <a HREF =
|
|
"#ReservedAndPadFields">reserved</a>.</dd>
|
|
|
|
<DT><CODE>num_blocks</CODE></dt>
|
|
|
|
<DD>The number of elements in the <code>binfo</code> array. Since
|
|
the first element of the <code>binfo</code> array is used to
|
|
chain multiple block lists into a single transaction, the
|
|
actual number of data blocks is <code>num_blocks - 1</code>.
|
|
</dd>
|
|
|
|
<DT><CODE>bytes_used</CODE></dt>
|
|
|
|
<DD>The number of bytes occupied in the journal for this block list,
|
|
including the block list header and the data for each of the blocks
|
|
in the list. The next block list header (if any) will be
|
|
<CODE>bytes_used</CODE> bytes from the start of the current block
|
|
list header, wrapping from the end of the journal buffer to the
|
|
start of the journal buffer if needed.</dd>
|
|
|
|
<DT><CODE>checksum</CODE></dt>
|
|
|
|
<DD>The <a HREF = "#Checksum">checksum</a> of the block list header,
|
|
including the first element of the <CODE>binfo</CODE> array
|
|
(a total of 32 bytes).</dd>
|
|
|
|
<DT><CODE>pad</CODE></dt>
|
|
|
|
<DD>Alignment padding. An implementation must treat this
|
|
field as <a HREF = "#ReservedAndPadFields">reserved</a>.</dd>
|
|
|
|
<DT><CODE>binfo</CODE></dt>
|
|
|
|
<DD>A variable-sized array of blocks. The array contains
|
|
<CODE>num_blocks+1</CODE> entries. The first entry is used
|
|
when a single transaction occupies multiple block lists,
|
|
using the <CODE>next</CODE> field as described above. The
|
|
remaining <CODE>num_blocks</CODE> entries describe where the
|
|
data from this block list will be written to disk.</dd>
|
|
</DL>
|
|
|
|
<P>The first element of the <code>binfo</code> array is
|
|
used to indicate whether the transaction contains additional
|
|
block lists. Each of the other elements of the
|
|
<code>binfo</code> array represent a single block of data
|
|
in the journal buffer which must be copied to its correct
|
|
location on disk. The fields have the following meaning:</P>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>typedef struct block_info {
|
|
UInt64 bnum;
|
|
UInt32 bsize;
|
|
UInt32 next;
|
|
} block_info;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<DL>
|
|
<DT><CODE>bnum</CODE></dt>
|
|
|
|
<DD>The sector number where the data in this block
|
|
must be written. If this field is 0xFFFFFFFFFFFFFFFF (all
|
|
64 bits set), then this block must be skipped and not
|
|
written. This field is <a HREF =
|
|
"#ReservedAndPadFields">reserved</a> for the first element
|
|
of the <code>binfo</code> array.</dd>
|
|
|
|
<DT><CODE>bsize</CODE></dt>
|
|
|
|
<DD>The number of bytes to be copied from the journal buffer
|
|
to the above sector number. This value will be a
|
|
multiple of 512. This field is <a HREF =
|
|
"#ReservedAndPadFields">reserved</a> for the first element
|
|
of the <code>binfo</code> array.</dd>
|
|
|
|
<DT><CODE>next</CODE></dt>
|
|
|
|
<DD>This field is used while in memory to keep track of
|
|
transactions that span multiple block lists. If this field
|
|
is zero in the first <code>block_info</code> of a block
|
|
list, then the transaction ends with this block list;
|
|
otherwise, the transaction has one or more additional block
|
|
lists. This field is meaningful only for the first element
|
|
of the block list array. The actual on-disk value has no
|
|
meaning beyond testing for zero or non-zero.</dd>
|
|
</DL>
|
|
|
|
<H3><a NAME="Checksum"></a>Journal Checksums</H3>
|
|
|
|
<P>The <a HREF = "#JournalHeader">journal header</a> and
|
|
<a HREF = "#BlockListHeader">block list header</a> both contain
|
|
<CODE>checksum</CODE> fields. These checksums can be verified as part of a
|
|
basic consistency check of these structures. To verify the
|
|
checksum, temporarily set the <CODE>checksum</CODE> field to zero and then call
|
|
the <CODE>calc_checksum</CODE> routine with the address and size of the
|
|
header being checksummed. The function result should equal the
|
|
original value of the <CODE>checksum</CODE> field.</p>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>static int
|
|
calc_checksum(unsigned char *ptr, int len)
|
|
{
|
|
int i, cksum=0;
|
|
|
|
for(i=0; i < len; i++, ptr++) {
|
|
cksum = (cksum << 8) ^ (cksum + *ptr);
|
|
}
|
|
|
|
return (~cksum);
|
|
}</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<H3><a NAME="ReplayJournal"></a>Replaying the Journal</H3>
|
|
|
|
<P>In order to replay the journal, an implementation just loops
|
|
over the transactions, copying each individual block in the
|
|
transaction from the journal to its proper location on the
|
|
volume. Once those blocks have been flushed to the media (not
|
|
just the driver!), it may update the journal header to remove
|
|
the transactions.</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Replaying the journal does not guarantee that the temporary files
|
|
associated with <a HREF = "#UnlinkedFiles">open but unlinked</a>
|
|
files are deleted. After replaying the journal, these temporary
|
|
files may be deleted.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>Here are the steps to replay the journal:</p>
|
|
|
|
<OL>
|
|
<li>Read the <a href="#VolumeHeader">volume header</a>
|
|
into variable <code>vhb</code>. The volume may have an
|
|
<a href="#HFSWrapper">HFS wrapper</a>; if so, you will need to use
|
|
it to determine the location of the volume header.</li>
|
|
<li>Test the <code>kHFSVolumeJournaledBit</code> in the
|
|
<code>attributes</code> field of the volume header. If
|
|
it is not set, there is no journal to replay, and you
|
|
are done.</li>
|
|
<li>Read the <a href="#JournalInfoBlock">journal info block</a>
|
|
from the allocation block number <code>vhb.journalInfoBlock</code>,
|
|
into variable <code>jib</code>.</li>
|
|
<li>If <code>kJIJournalNeedsInitMask</code> is set in <code>jib.flags</code>,
|
|
the journal was never initialized, so there is no journal to replay.</li>
|
|
<li>Verify that <code>kJIJournalInFSMask</code> is set and
|
|
<code>kJIJournalOnOtherDeviceMask</code> is clear in
|
|
<code>jib.flags</code>.</li>
|
|
<li>Read the <a href="#JournalHeader">journal header</a> at
|
|
<code>jib.offset</code> bytes from the start of the volume, and
|
|
place it in variable <code>jhdr</code>.</li>
|
|
<li>If <code>jhdr.start</code> equals <code>jhdr.end</code>, the
|
|
journal does not have any transactions, so there is nothing
|
|
to replay.</li>
|
|
<li>Set the current offset in the journal (typically a local variable)
|
|
to the start of the journal buffer, <code>jhdr.start</code>.</li>
|
|
<li>While <code>jhdr.start</code> does not equal <code>jhdr.end</code>,
|
|
perform the following steps:
|
|
<OL>
|
|
<li>Read a <a href="#BlockListHeader">block list header</a> of
|
|
<code>jhdr.blhdr_size</code> bytes from the current offset
|
|
in the journal into variable <code>blhdr</code>.</li>
|
|
<li>For each block in <code>bhdr.binfo[1]</code> to
|
|
<code>bhdr.binfo[blhdr.num_blocks]</code>, inclusive, copy
|
|
<code>bsize</code> bytes from the current offset in the
|
|
journal to sector <code>bnum</code> on the volume (to byte
|
|
offset <code>bnum*jdhr.jhdr_size</code>). Remember that
|
|
<code>jhdr_size</code> is the size of a sector, in bytes.</li>
|
|
<li>If <code>bhdr.binfo[0].next</code> is zero, you have completed
|
|
the last block list of the current transaction; set
|
|
<code>jhdr.start</code> to the current offset in the journal.</li>
|
|
</ol>
|
|
</li>
|
|
</ol>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Remember that the journal is a circular buffer. When reading a
|
|
<a href="#BlockListHeader">block list header</a> or block from
|
|
the journal buffer (in the loop described above), you will need
|
|
to check whether it wraps around the end of the journal buffer.
|
|
If it would extend beyond the end of the journal buffer, you
|
|
must stop reading at the end of the journal buffer, and resume
|
|
reading at the start of the journal buffer (offset
|
|
<code>jhdr.jhdr_size</code> bytes from the start of the
|
|
journal).</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>After replaying an entire transaction (all blocks
|
|
in a block list, when <code>bhdr.binfo[0]</code> is zero), or after
|
|
replaying all transactions, you may update the value of the <code>start</code>
|
|
field in the <a href="#JournalHeader">journal header</a> to the current
|
|
offset in the journal. This will remove those block lists from the
|
|
journal since they have been written to their correct locations on disk.</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>WARNING:</B><BR>
|
|
You must ensure that previous block writes complete before updating
|
|
the journal header's <code>start</code> field on disk. One way to
|
|
do this is to issue a flush to the device driver and wait until
|
|
the device driver has written all dirty blocks, and then flush the
|
|
device itself and wait for the device to write all dirty blocks
|
|
to the media.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="HFSX"></a>HFSX</H2>
|
|
|
|
<P>HFSX is an extension to HFS Plus to allow additional features
|
|
that are incompatible with HFS Plus. The only such feature currently
|
|
defined is case-sensitive filenames.</P>
|
|
|
|
<P>HFSX volumes have a signature of <CODE>'HX'</CODE>
|
|
(<CODE>0x4858</CODE>) in the <CODE>signature</CODE> field of the
|
|
<a HREF = "#VolumeHeader">volume header</a>. The <CODE>version</CODE>
|
|
field identifies the version of HFSX used on the volume; the only
|
|
value currently defined is <CODE>5</CODE>. If features are added that
|
|
would be incompatible with older versions (that is, older versions
|
|
cannot safely access or modify the volume because of the new features),
|
|
then a different <CODE>version</CODE> number will be used.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
A new <CODE>signature</CODE> was required because some utilities
|
|
did not use the <CODE>version</CODE> field properly. They
|
|
would attempt to use or repair the volume (including changing the
|
|
<CODE>version</CODE> field) when they encountered a
|
|
<CODE>version</CODE> value that was not previously documented.</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>WARNING:</B><BR>
|
|
If your implementation encounters an HFSX volume with a
|
|
<CODE>version</CODE> value it does not recognize, it must
|
|
not attempt to access or repair the volume. Catastrophic
|
|
data loss may result. In particular, do <b>NOT</b> change the
|
|
<code>version</code> field.</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>It is intended that future HFSX features will result in the definition
|
|
of new <a href="#VolumeAttributes">volume attribute</a> bits, and that
|
|
those bits will be used to indicate which features are in use on the
|
|
volume.</P>
|
|
|
|
<P>An HFSX volume never has an <a href="#HFSWrapper">HFS wrapper</a>.</P>
|
|
|
|
<P>In an Apple partition map, the partition type (<code>PMPartType</code>)
|
|
of an HFSX volume is set to "Apple_HFSX".</P>
|
|
|
|
<H3><a NAME="HFSX_V5"></a>HFSX Version 5</H3>
|
|
|
|
<P>Introduced in Mac OS X 10.3, HFSX version 5 allows volumes with
|
|
case-sensitive file and directory names. Case-sensitive names
|
|
means that you can have two objects, whose names differ only by
|
|
the case of the letters, in the same directory at the same time.
|
|
For example, you could have "Bob", "BOB", and "bob" in the same
|
|
directory.</P>
|
|
|
|
<P>An HFSX volume may be either case-sensitive or case-insensitive.
|
|
Case sensitivity (or lack thereof) is global to the volume; the
|
|
setting applies to all file and directory names on the volume.
|
|
To determine whether an HFSX volume is case-sensitive, use the
|
|
<CODE>keyCompareType</CODE> field of the <a href="#HeaderRecord">
|
|
B-tree header</a> of the <a href="#CatalogFile">catalog file</a>.
|
|
A value of <CODE>kHFSBinaryCompare</CODE> means the volume is
|
|
case-sensitive. A value of <CODE>kHFSCaseFolding</CODE> means the
|
|
volume is case-insensitive.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Do not assume that an HFSX volume is case-sensitive.
|
|
Always use the <CODE>keyCompareType</CODE> to determine
|
|
case-sensitivity or case-insensitivity.
|
|
</P></TD></TR></table></CENTER>
|
|
|
|
<P>A case-insensitive HFSX volume (one whose <CODE>keyCompareType</CODE>
|
|
is <CODE>kHFSCaseFolding</CODE> uses the same <a href="#StringComparisonAlgorithm">
|
|
Unicode string comparison algorithm</a> as HFS Plus.</P>
|
|
|
|
<P>A case-sensitive HFSX volume (one whose <CODE>keyCompareType</CODE>
|
|
is <CODE>kHFSBinaryCompare</CODE>) simply compares each character of
|
|
the name as an unsigned 16-bit integer. The first character (the one
|
|
with the smallest offset from the start of the string) that is
|
|
different determines the relative order. The string with the
|
|
numerically smaller character value is ordered before the string
|
|
with the larger character value. For example, the string "Bob"
|
|
would sort before the string "apple" because the code for the
|
|
character "B" (decimal 66) is less than the code for the character
|
|
"a" (decimal 97).</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
Case-sensitive names do not ignore Unicode "ignorable"
|
|
characters. This means that a single directory may have several
|
|
names which would be considered equivalent using Unicode comparison
|
|
rules, but which are considered distinct on a case-sensitive
|
|
HFSX volume.
|
|
</P></TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The null character (<CODE>0x0000</CODE>), as used in the name
|
|
of the "HFS+ Private Data" directory used by
|
|
<a href="#HardLinks">hard links</a>, sort first with
|
|
case-sensitive compares, but last with case-insensitive
|
|
compares.
|
|
</P></TD></TR></table></CENTER>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="MetadataZone"></a>Metadata Zone</H2>
|
|
|
|
<P>Mac OS X version 10.3 introduced a new policy for determining
|
|
where to allocate space for files, which improves performance
|
|
for most users. This policy places the volume metadata and
|
|
frequently used small files ("<a href="#HotFile">hot files</a>")
|
|
near each other on disk, which reduces the seek time for typical
|
|
accesses. This area on disk is known as the <b>metadata
|
|
zone</b>.</P>
|
|
|
|
<P>The volume metadata are the structures that let the file system
|
|
manage the contents of the volume. It includes the
|
|
<a href="#AllocationFile">allocation bitmap file</a>,
|
|
<a HREF = "#ExtentsOverflowFile">extents overflow file</a>,
|
|
and the <a HREF = "#CatalogFile">catalog file</a>, and the
|
|
<a HREF = "#Journal">journal file</a>. The
|
|
<a href="#VolumeHeader">volume header</a> and alternate volume
|
|
header are also metadata, but they have fixed locations within
|
|
the volume, so they are not located in the hot file area. Mac OS
|
|
X may use a quota users file and quota groups file to manage disk
|
|
space quotas on a volume. These files aren't strictly metadata,
|
|
but they are included in the metadata zone because of their
|
|
heavy use by the OS and they are too large to be considered
|
|
ordinary hot files.</P>
|
|
|
|
<P>Implementations are encouraged not to interfere with the metadata
|
|
zone policy. For example, a disk optimizer should avoid moving files
|
|
into the metadata zone unless that file is known to be
|
|
frequently accessed, in which case it may be added to the "<a
|
|
href="#HotFile">hot file</a>" list. Similarly, files in the
|
|
metadata zone should not be moved elsewhere on disk unless they
|
|
are also removed from the hot file list.</P>
|
|
|
|
<P>This policy is only applied to volumes whose size is at least
|
|
10GB, and which have <a href="#Journal">journaling</a> enabled.
|
|
The metadata zone is established when the volume is mounted. The
|
|
size of the zone is based upon the following sizes:</P>
|
|
|
|
<table border=1>
|
|
<tr>
|
|
<th>Item</th>
|
|
<th>Contribution to the Metadata Zone size</th>
|
|
</tr>
|
|
<tr>
|
|
<td scope="row"><a HREF = "#AllocationFile">Allocation Bitmap File</a></td>
|
|
<td>Physical size (<code>totalBlocks</code> times the volume's
|
|
allocation block size) of the allocation bitmap file.</td>
|
|
</tr>
|
|
<tr>
|
|
<td scope="row"><a HREF = "#ExtentsOverflowFile">Extents Overflow File</a></td>
|
|
<td>4MB, plus 4MB per 100GB (up to 128MB maximum)</td>
|
|
</tr>
|
|
<tr>
|
|
<td scope="row"><a HREF = "#Journal">Journal File</a></td>
|
|
<td>8MB, plus 8MB per 100GB (up to 512MB maximum)</td>
|
|
</tr>
|
|
<tr>
|
|
<td scope="row"><a HREF = "#CatalogFile">Catalog File</a></td>
|
|
<td>10 bytes per KB (1GB minimum)</td>
|
|
</tr>
|
|
<tr>
|
|
<td scope="row"><a href="#HotFile">Hot Files</a></td>
|
|
<td>5 bytes per KB (10MB minimum; 512MB maximum)</td>
|
|
</tr>
|
|
<tr>
|
|
<td scope="row">Quota Users File</td>
|
|
<td>Described below</td>
|
|
</tr>
|
|
<tr>
|
|
<td scope="row">Quota Groups File</td>
|
|
<td>Described below</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<P>In Mac OS X version 10.3, the amount of space reserved for the
|
|
allocation file is actually the minimum allocation file size for
|
|
the volume (the total number of allocation blocks, divided by 8,
|
|
rounded up to a multiple of the allocation block size). If the
|
|
allocation file is larger than that (which is sometimes done to
|
|
allow a volume to be more easily grown at a later time), then
|
|
there will be less space available for other metadata or
|
|
<a href="#HotFile">hot files</a> in the metadata zone. This
|
|
is a bug (r. 3522516).</P>
|
|
|
|
<P>The amount of space reserved for each type of metadata (except for
|
|
the allocation bitmap file) is based on the total size of the volume.
|
|
For the purposes of these computations, the total size of the volume is
|
|
the allocation block size multiplied by the total number of allocation blocks.</P>
|
|
|
|
<P>The sizes reserved for quota users and groups files are the result of
|
|
complex calculations. In each case, the size reserved is a value of
|
|
the form <code>(items + 1) * 64</code> bytes, where <code>items</code>
|
|
is based on the size of the volume in gigabytes, rounded down. For the
|
|
quota users file, <code>items</code> is 256 per gigabyte, rounded up to
|
|
a power of 2, with a minimum of 2048, and a maximum of 2097152 (2M).
|
|
For the quota groups file, <code>items</code> is 32 per gigabyte,
|
|
rounded up to a power of 2, with a minimum of 2048, and a maximum of
|
|
262144 (256K). The quota files are considered hot files, and occupy
|
|
the <a href="#HotFile">hot file</a> area, even though they are larger
|
|
than the maximum file size normally eligible to be a hot file.</P>
|
|
|
|
<P>The total size of the metadata zone is the sum of the above sizes,
|
|
rounded up so that the metadata zone is represented by a whole number
|
|
of allocation blocks within the volume bitmap. That is, the start and
|
|
end of the metadata zone fall on allocation block boundaries in the
|
|
volume bitmap. That means that the size of the metadata zone is rounded
|
|
up to a multiple of 8 times the square of the allocation block size.
|
|
In Mac OS X version 10.3, the extra space due to the round up of the
|
|
metadata zone is split up between the catalog and the
|
|
<a href="#HotFile">hot file</a> area (2/3 and 1/3, respectively).</P>
|
|
|
|
<P>The calculations for the extents overflow file and journal file divide
|
|
the total size of the volume by 100GB, rounding down. Then they add one
|
|
(to compensate for any remainder lost as part of the rounding). The result
|
|
is then multiplied by 4MB or 8MB, respectively. If the volume's total
|
|
size is not a multiple of 100GB, this is equivalent to 4MB (or 8MB) per 100GB,
|
|
rounded up.</P>
|
|
|
|
<P>In Mac OS X version 10.3, the metadata zone is located at the
|
|
start of the volume, following the
|
|
<a href="#VolumeHeader">volume header</a>. The
|
|
<a href="#HotFile">hot file</a> area is located towards the end of
|
|
the metadata zone.</P>
|
|
|
|
<P>When performing normal file allocations, the allocator will
|
|
skip over the metadata zone. This ensures that the metadata will be
|
|
less fragmented, and all of the metadata will be located in the
|
|
same area on the disk. If the area outside the metadata zone is
|
|
exhausted, the allocator will then use space inside the metadata
|
|
zone for normal file allocations. Similarly, when allocating
|
|
space for metadata, the allocator will use space inside the
|
|
metadata zone first. If all of the metadata zone is in use,
|
|
then metadata allocations will use space outside the metadata
|
|
zone.</P>
|
|
|
|
<H2><a NAME="HotFile"></a>Hot Files</H2>
|
|
|
|
<P>Most files on a disk are rarely, if ever, accessed. Most
|
|
frequently accessed (<b>hot</b>) files are small. To improve
|
|
performance of these small, frequently access files, they are
|
|
moved near the volume's metadata, into the metadata zone. This
|
|
reduces seek times for most accesses. As files are moved into
|
|
the metadata zone, they are also defragmented (allocated in a
|
|
single extent), which further improves performance. This
|
|
process is known as <b>adaptive hot file clustering</b>. </P>
|
|
|
|
<P><a NAME="Temperature"></a>The relative importance of a
|
|
frequently used (hot) file is called its <b>temperature</b>.
|
|
Files with the hottest (largest) temperatures are the ones
|
|
actually moved into the metadata zone. In Mac OS X version 10.3,
|
|
a file's temperature is computed as the number of bytes read
|
|
from the file during the recording period divided by the file's
|
|
size in bytes. This is a measure of how often the file is
|
|
read.</P>
|
|
|
|
<P>This section describes the on-disk structures used for
|
|
tracking hot files. The algorithms used at run time are subject
|
|
to change, and are not documented here.</P>
|
|
|
|
<P>Migration of files into or out of the hot file area of the metadata zone is a
|
|
gradual process, based upon the user's actual file access
|
|
patterns. The migration happens in several phases:</P>
|
|
|
|
<DL>
|
|
<DT>Recording</DT>
|
|
<DD>Watch file accesses to determine which files are used most</DD>
|
|
|
|
<DT>Evaluation</DT>
|
|
<DD>Merge recently used hot files with previously found hot files</DD>
|
|
|
|
<DT>Eviction</DT>
|
|
<DD>Move older and less frequently used hot files out of metadata zone
|
|
to make room for newer, hotter files</DD>
|
|
|
|
<DT>Adoption</DT>
|
|
<DD>Move newer and hotter files into the metadata zone</DD>
|
|
</DL>
|
|
|
|
<H3>Hot File B-Tree</H3>
|
|
|
|
<P>A <a HREF = "#BTrees">B-Tree</a> is used to keep track of the
|
|
files that currently occupy the hot file area of the <a HREF =
|
|
"#MetadataZone"> metadata zone</a>. The hot file B-tree is an
|
|
ordinary file on the volume (that is, it has records in the <a
|
|
href="#CatalogFile">catalog</a>). It is a file named
|
|
"<CODE>.hotfiles.btree</CODE>" in the root directory. To avoid
|
|
accidental manipulation of this file, the
|
|
<code>kIsInvisible</code> and <code>kNameLocked</code> bits in
|
|
the <code>finderFlags</code> field of the <a
|
|
href="#FinderInfo">Finder info</a> should be set.</P>
|
|
|
|
<P>The node size of the hot file B-tree is at least 512 bytes,
|
|
and is typically the same as the the volume's allocation block
|
|
size. Like other B-trees on an HFS Plus volume, the key length
|
|
field is 16 bits, and <code>kBTBigKeysMask</code> is set in the
|
|
B-tree header's <code>attributes</code>. The
|
|
<code>btreeType</code> in the <a href="#HeaderRecord">header
|
|
record</a> must be set to <code>kUserBTreeType</code>.</P>
|
|
|
|
<P>The B-tree's <a href="#UserDataRecord">user data record</a>
|
|
contains information about hot file recording. The format of the user
|
|
data is described by the <code>HotFilesInfo</code> structure:</P>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>#define HFC_MAGIC 0xFF28FF26
|
|
#define HFC_VERSION 1
|
|
#define HFC_DEFAULT_DURATION (3600 * 60)
|
|
#define HFC_MINIMUM_TEMPERATURE 16
|
|
#define HFC_MAXIMUM_FILESIZE (10 * 1024 * 1024)
|
|
char hfc_tag[] = "CLUSTERED HOT FILES B-TREE ";
|
|
|
|
struct HotFilesInfo {
|
|
UInt32 magic;
|
|
UInt32 version;
|
|
UInt32 duration; /* duration of sample period */
|
|
UInt32 timebase; /* recording period start time */
|
|
UInt32 timeleft; /* recording period stop time */
|
|
UInt32 threshold;
|
|
UInt32 maxfileblks;
|
|
UInt32 maxfilecnt;
|
|
UInt8 tag[32];
|
|
};
|
|
typedef struct HotFilesInfo HotFilesInfo;</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
<DL>
|
|
<DT><code>magic</code></DT>
|
|
<DD>Must contain the value <code>HFC_MAGIC</code> (0xFF28FF26).</DD>
|
|
|
|
<DT><code>version</code></DT>
|
|
<DD>Contains the version of the <code>HotFilesInfo</code>
|
|
structure. Version 1 of the structure is described here.
|
|
If your implementation encounters any other version number,
|
|
it should not read or modify the hot file B-tree.</DD>
|
|
|
|
<DT><code>duration</code></DT>
|
|
<DD>Contains the duration of the current recording phase, in seconds.
|
|
In Mac OS X 10.3, this value is typically <code>HFC_DEFAULT_DURATION</code>
|
|
(60 hours).</DD>
|
|
|
|
<DT><code>timebase</code></DT>
|
|
<DD>Contains the time that the current recording phase began, in seconds
|
|
since Jan 1, 1970 GMT.</DD>
|
|
|
|
<DT><code>timeleft</code></DT>
|
|
<DD>Contains the time remaining in the current recording phase, in seconds.</DD>
|
|
|
|
<DT><code>threshold</code></DT>
|
|
<DD>Contains the minimum temperature for a file to be eligible to be
|
|
moved into the hot file area. Files whose temperature is less than
|
|
this value will be moved out of the hot file area.</DD>
|
|
|
|
<DT><code>maxfileblks</code></DT>
|
|
<DD>Contains the maximum file size, in allocation blocks, for a
|
|
file to be eligible to be moved into the hot file area. Files
|
|
larger than this size will not be moved into the hot file area.
|
|
In Mac OS X 10.3, this value is typically
|
|
<code>HFC_MAXIMUM_FILESIZE</code> divided by the volume's
|
|
allocation block size.</DD>
|
|
|
|
<DT><code>maxfilecnt</code></DT>
|
|
<DD>Contains the maximum number of files to place into the hot file
|
|
area. Note that the hot file area may actually contain more than
|
|
this number of files, especially if they previously existed in the
|
|
hot file area before the beginning of the recording phase. This number
|
|
represents the number of files that the hot file recording code
|
|
intents to track and eventually place into the hot file area.</DD>
|
|
|
|
<DT><code>tag</code></DT>
|
|
<DD>Contains the null-terminated (C-style) string containing the
|
|
ASCII text
|
|
<code>"CLUSTERED HOT FILES B-TREE "</code> (not including the
|
|
quotes). Note that the last six bytes are five spaces and the
|
|
null (zero) byte. This field exists to make it easier to recognize
|
|
the hot file B-tree when debugging or using a disk editor. An
|
|
implementation should not attempt to verify or change this field.</DD>
|
|
</DL>
|
|
|
|
<H4>Hot File Record Key</H4>
|
|
<P>A key in the hot file B-tree is of type <code>HotFileKey</code>.</P>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>struct HotFileKey {
|
|
UInt16 keyLength;
|
|
UInt8 forkType;
|
|
UInt8 pad;
|
|
UInt32 temperature;
|
|
UInt32 fileID;
|
|
};
|
|
typedef struct HotFileKey HotFileKey;
|
|
|
|
#define HFC_LOOKUPTAG 0xFFFFFFFF
|
|
#define HFC_KEYLENGTH (sizeof(HotFileKey) - sizeof(UInt32))</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
<P>The fields have the following meaning:</P>
|
|
|
|
<DL>
|
|
<DT><code>keyLength</code></DT>
|
|
<DD>The length of a hot file key, not including the <code>keyLength</code>
|
|
field itself. Hot file keys are of fixed size. This field must contain
|
|
the value 10.</DD>
|
|
|
|
<DT><code>forkType</code></DT>
|
|
<DD>Indicates whether the fork being tracked is a data fork
|
|
(value <code>0x00</code>) or a resource fork (value <code>0xFF</code>).
|
|
In Mac OS X version 10.3, only data forks are eligible for placement
|
|
into the hot file area.</DD>
|
|
|
|
<DT><code>pad</code></DT>
|
|
<DD>An implementation must treat this as a
|
|
<a HREF = "#ReservedAndPadFields">pad</a> field.</DD>
|
|
|
|
<DT><code>temperature</code></DT>
|
|
<DD>The fork's <a href="#Temperature">temperature</a>. For hot file
|
|
thread records, this field contains the value <code>HFC_LOOKUPTAG</code>
|
|
(<code>0xFFFFFFFF</code>).</DD>
|
|
|
|
<DT><code>fileID</code></DT>
|
|
<DD>The <a href="#CNID">catalog node ID</a> of the file being tracked.</DD>
|
|
</DL>
|
|
|
|
<P>Hot file keys are compared first by <code>temperature</code>,
|
|
then <code>fileID</code>, and lastly by <code>forkType</code>.
|
|
All of these comparisons are unsigned.</P>
|
|
|
|
<H4>Hot File Records</H4>
|
|
|
|
<P>Much like the <a HREF = "#CatalogFile">catalog file</a>,
|
|
the hot file B-tree stores two kinds of records: hot file records and thread
|
|
records. Every fork in the hot file area has both a hot file record and
|
|
a thread record in the hot file B-tree. Hot file records are used to find
|
|
hot files based on their temperature. Thread records are used to find
|
|
hot files based on their <a href="#CNID">catalog node ID</a> and fork type.</P>
|
|
|
|
<P>Thread records in the hot file B-tree use a special value
|
|
(<code>HFC_LOOKUPTAG</code>) in the <code>temperature</code>
|
|
field of the key. The data for a thread record is the
|
|
<code>temperature</code> of that fork, stored as a
|
|
<code>UInt32</code>. So, given a <a href="#CNID">catalog node
|
|
ID</a> and fork type, it is possible to construct a key for the
|
|
fork's thread record. If a thread record exists, you can get
|
|
the temperature from the thread's data to construct the key for
|
|
the hot file record. If a thread record does not exist, then
|
|
the fork is not being tracked as a hot file.</P>
|
|
|
|
<P>Hot file records use all of the key fields as described
|
|
above. The data for a hot file record is 4 bytes. The data in a
|
|
hot file record is not meaningful. To aid in debugging, Mac OS X
|
|
version 10.3 typically stores the first four bytes of the file
|
|
name (encoded in UTF-8), or the ASCII text "????".</P>
|
|
|
|
<P>When an implementation changes a hot file's temperature, the
|
|
old hot file record must be removed, a new hot file with the new
|
|
temperature must be inserted, and the thread record's data must
|
|
be changed to contain the new temperature.</P>
|
|
|
|
<H3>Recording Hot File Temperatures</H3>
|
|
|
|
<P>The recording phase gathers information about file usage over time.
|
|
In order to gather useful statistics, the recording phase may last longer
|
|
than the duration of a single mount. Therefore, information about file
|
|
usage is stored on disk so that it can accumulate over time.</P>
|
|
|
|
<P>The <code>clumpSize</code> field of the <a href="#ForkDataStructure">
|
|
fork data structure</a> is used to record the amount of data actually
|
|
read from a fork. Since the field is only 32 bits long, it stores
|
|
the number of allocation blocks read from the file. The fork's
|
|
temperature can be computed by dividing its <code>clumpSize</code> by
|
|
its <code>totalBlocks</code>.</P>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="UnicodeSubtleties"></a>Unicode Subtleties</H2>
|
|
|
|
<P>HFS Plus makes heavy use of Unicode strings to store file
|
|
and folder names. However, Unicode is still evolving, and
|
|
its use within a file system presents a number of
|
|
challenges. This section describes some of the challenges,
|
|
along with the solutions used by HFS Plus.</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
Before reading this section, you should read
|
|
<a HREF = "#HFSPlusNames">HFS Plus Names</a>.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
An implementation must not use the Unicode utilities
|
|
implemented by its native platform (for decomposition and
|
|
comparison), unless those algorithms are equivalent to the
|
|
HFS Plus algorithms defined here, and are guaranteed to be
|
|
so forever. This is rarely the case. Platform algorithms
|
|
tend to evolve with the Unicode standard. The HFS Plus
|
|
algorithms cannot evolve because such evolution would
|
|
invalidate existing HFS Plus volumes.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The Mac OS Text Encoding Converter provides several
|
|
constants that let you convert to and from the canonical,
|
|
decomposed form stored on HFS Plus volumes. When using
|
|
<CODE>CreateTextEncoding</CODE> to create a text encoding,
|
|
you should set the <CODE>TextEncodingBase</CODE> to
|
|
<CODE>kTextEncodingUnicodeV2_0</CODE>, set the
|
|
<CODE>TextEncodingVariant</CODE> to
|
|
<CODE>kUnicodeCanonicalDecompVariant</CODE>, and set the
|
|
<CODE>TextEncodingFormat</CODE> to
|
|
<CODE>kUnicode16BitFormat</CODE>. Using these values ensures
|
|
that the Unicode will be in the same form as on an HFS Plus
|
|
volume, even as the Unicode standard evolves.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3><a NAME="CanonicalDecomposition"></a>Canonical Decomposition</H3>
|
|
|
|
<P>Unicode allows some sequences of characters to be
|
|
represented by multiple, equivalent forms. For example, the
|
|
character "<img src="images/tn1150_005.gif" width=7 height=11 align=bottom alt="1150.5.gif">"
|
|
can be represented as the single Unicode character
|
|
u+00E9 (latin small letter e with acute), or as
|
|
the two Unicode characters u+0065 and u+0301 (the letter "e"
|
|
plus a combining acute symbol).</P>
|
|
|
|
<P>To reduce complexity in the B-tree key comparison
|
|
routines (which have to compare Unicode strings), HFS Plus
|
|
defines that Unicode strings will be stored in fully
|
|
decomposed form, with composing characters stored in
|
|
canonical order. The other equivalent forms are illegal in
|
|
HFS Plus strings. An implementation must convert these
|
|
equivalent forms to the fully decomposed form before storing
|
|
the string on disk.</P>
|
|
|
|
<P>The <a HREF = "tn1150table.html">Unicode Decomposition</a>
|
|
table contains a list of characters that are illegal as part
|
|
of an HFS Plus string, and the equivalent character(s) that
|
|
must be used instead. Any character appearing in a column
|
|
titled "Illegal", must be replaced by the character(s) in
|
|
the column immediately to the right (titled "Replace With").
|
|
</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD>
|
|
<P><B>Note:</B><BR>
|
|
Mac OS versions 8.1 through 10.2.x used decompositions based on
|
|
Unicode 2.1. Mac OS X version 10.3 and later use decompositions
|
|
based on Unicode 3.2. Most of the characters whose decomposition
|
|
changed are not used by any Mac encoding, so they are unlikely to
|
|
occur on an HFS Plus volume. The MacGreek encoding had the largest
|
|
number of decomposition changes.</p>
|
|
|
|
<p>The Unicode decomposition table mentioned above indicates
|
|
which decompositions were added, removed, or changed between
|
|
Unicode 2.1 and Unicode 3.2.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>In addition, the Korean Hangul characters with codes in
|
|
the range u+AC00 through u+D7A3 are illegal and must be
|
|
replaced with the equivalent sequence of conjoining jamos,
|
|
as described in the Unicode 2.0 book, section 3.10.</P>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
The characters with codes in the range u+2000
|
|
through u+2FFF are punctuation, symbols, dingbats,
|
|
arrows, box drawing, etc. The u+24xx block, for
|
|
example, has single characters for things like
|
|
"(a)". The characters in this range are
|
|
<EM>not</EM> fully decomposed; they are left
|
|
unchanged in HFS Plus strings. This allows strings
|
|
in Mac OS encodings to be converted to Unicode and
|
|
back without loss of information. This is not
|
|
unnatural since a user would not necessarily expect
|
|
a dingbat "(a)" to be equivalent to the three
|
|
character sequence "(", "a", ")" in a file name.</p>
|
|
|
|
<P>The characters in the range u+F900 through u+FAFF
|
|
are CJK compatibility ideographs, and are <em>not</em>
|
|
decomposed in HFS Plus strings.</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>So, for the example given earlier, "<img src="images/tn1150_006.gif" width=7 height=11 align=bottom alt="1150.5.gif">"
|
|
must be stored as the two Unicode characters u+0065 and
|
|
u+0301 (in that order). The Unicode character u+00E9 may not
|
|
appear in a Unicode string used as part of an HFS Plus
|
|
B-tree key.</P>
|
|
|
|
<H3><a NAME="StringComparisonAlgorithm"></a>Case-Insensitive
|
|
String Comparison Algorithm</H3>
|
|
|
|
<P>In HFS Plus and case-insensitive <a href="#HFSX">HFSX</a>,
|
|
strings must be compared in a case-insensitive fashion. The
|
|
Unicode standard does not strictly define upper and lower
|
|
case equivalence, although it does suggest some equivalences.
|
|
The HFS Plus string comparison algorithm (defined below)
|
|
include a concrete case equivalence definition. An
|
|
implementation must use the equivalence expressed by this
|
|
algorithm.</P>
|
|
|
|
<P>Furthermore, Unicode requires that certain formatting
|
|
characters be ignored (skipped over) during string
|
|
comparisons. The algorithm and tables used for case
|
|
equivalence also arrange to ignore these characters. An
|
|
implementations must ignore the characters that are ignored
|
|
by this algorithm.</P>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
Case-sensitive <a href="#HFSX">HFSX</a> volumes do
|
|
<em>not</em> ignore the Unicode ignorable characters.
|
|
Those characters are significant for the purposes of
|
|
name comparion on case-sensitive HFSX.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>The HFS Plus case-insensitive string
|
|
comparison algorithm is defined by the <CODE>FastUnicodeCompare</CODE>
|
|
routine, shown below. This routine returns a value that
|
|
tells the caller how the strings are ordered relative to
|
|
each other: whether the first string is less than, equal to,
|
|
or greater than the second string. An HFS Plus implementation
|
|
may use this routine directly, or use another routine that
|
|
produces the same relative ordering.</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The <CODE>FastUnicodeCompare</CODE> routine does not handle
|
|
composed Unicode characters since they are illegal in HFS
|
|
Plus strings. As described in
|
|
<a HREF = "#CanonicalDecomposition">Canonical
|
|
Decomposition</a>, all HFS Plus strings must be fully
|
|
decomposed, with composing characters in canonical order.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>/*
|
|
FastUnicodeCompare - Compare two Unicode strings;
|
|
produce a relative ordering
|
|
|
|
IF RESULT
|
|
--------------------------
|
|
str1 < str2 => -1
|
|
str1 = str2 => 0
|
|
str1 > str2 => +1
|
|
|
|
The lower case table starts with 256 entries (one for
|
|
each of the upper bytes of the original Unicode char).
|
|
If that entry is zero, then all characters with that
|
|
upper byte are already case folded. If the entry is
|
|
non-zero, then it is the _index_ (not byte offset) of
|
|
the start of the sub-table for the characters with
|
|
that upper byte. All ignorable characters are folded
|
|
to the value zero.
|
|
|
|
In pseudocode:
|
|
|
|
Let c = source Unicode character
|
|
Let table[] = lower case table
|
|
|
|
lower = table[highbyte(c)]
|
|
if (lower == 0)
|
|
lower = c
|
|
else
|
|
lower = table[lower+lowbyte(c)]
|
|
|
|
if (lower == 0)
|
|
ignore this character
|
|
|
|
To handle ignorable characters, we now need a loop to
|
|
find the next valid character. Also, we can't pre-compute
|
|
the number of characters to compare; the string length
|
|
might be larger than the number of non-ignorable characters.
|
|
Further, we must be able to handle ignorable characters at
|
|
any point in the string, including as the first or last
|
|
characters. We use a zero value as a sentinel to detect
|
|
both end-of-string and ignorable characters. Since the
|
|
File Manager doesn't prevent the NULL character (value
|
|
zero) as part of a file name, the case mapping table is
|
|
assumed to map u+0000 to some non-zero value (like 0xFFFF,
|
|
which is an invalid Unicode character).
|
|
|
|
Pseudocode:
|
|
|
|
while (1) {
|
|
c1 = GetNextValidChar(str1) -- returns zero if
|
|
-- at end of string
|
|
c2 = GetNextValidChar(str2)
|
|
|
|
if (c1 != c2) break; -- found a difference
|
|
|
|
if (c1 == 0) -- reached end of string on
|
|
-- both strings at once?
|
|
return 0; -- yes, so strings are equal
|
|
}
|
|
|
|
-- When we get here, c1 != c2. So, we just
|
|
-- need to determine which one is less.
|
|
if (c1 < c2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
*/
|
|
|
|
SInt32 FastUnicodeCompare (
|
|
register ConstUniCharArrayPtr str1,
|
|
register ItemCount length1,
|
|
register ConstUniCharArrayPtr str2,
|
|
register ItemCount length2) {
|
|
register UInt16 c1,c2;
|
|
register UInt16 temp;
|
|
register UInt16* lowerCaseTable;
|
|
|
|
lowerCaseTable = gLowerCaseTable;
|
|
|
|
while (1) {
|
|
/* Set default values for c1, c2 in
|
|
case there are no more valid chars */
|
|
c1 = 0;
|
|
c2 = 0;
|
|
/* Find next non-ignorable char from
|
|
str1, or zero if no more */
|
|
while (length1 && c1 == 0) {
|
|
c1 = *(str1++);
|
|
--length1;
|
|
/* is there a subtable for
|
|
this upper byte? */
|
|
if ((temp = lowerCaseTable[c1>>8]) != 0)
|
|
/* yes, so fold the char */
|
|
c1 = lowerCaseTable[temp + (c1 & 0x00FF)];
|
|
}
|
|
/* Find next non-ignorable char
|
|
from str2, or zero if no more */
|
|
while (length2 && c2 == 0) {
|
|
c2 = *(str2++);
|
|
--length2;
|
|
/* is there a subtable
|
|
for this upper byte? */
|
|
if ((temp = lowerCaseTable[c2>>8]) != 0)
|
|
/* yes, so fold the char */
|
|
c2 = lowerCaseTable[temp + (c2 & 0x00FF)];
|
|
|
|
}
|
|
/* found a difference, so stop looping */
|
|
if (c1 != c2)
|
|
break;
|
|
/* did we reach the end of
|
|
both strings at the same time? */
|
|
if (c1 == 0)
|
|
/* yes, so strings are equal */
|
|
return 0;
|
|
}
|
|
if (c1 < c2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* The lower case table consists of a 256-entry high-byte
|
|
table followed by some number of 256-entry subtables. The
|
|
high-byte table contains either an offset to the subtable
|
|
for characters with that high byte or zero, which means
|
|
that there are no case mappings or ignored characters in
|
|
that block. Ignored characters are mapped to zero. */
|
|
|
|
|
|
UInt16 gLowerCaseTable[] = {
|
|
/* High-byte indices ( == 0 if no case mapping and
|
|
no ignorables ) Full data tables omitted for brevity.
|
|
See the Downloadables section for URL to download
|
|
the code. */
|
|
};</pre>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="HFSWrapper"></a>HFS Wrapper</H2>
|
|
|
|
<P>An HFS Plus volume may be contained within an HFS volume
|
|
in a way that makes the volume look like an HFS volume to
|
|
systems without HFS Plus support. This has a two important
|
|
advantages:</P>
|
|
|
|
<OL>
|
|
<li>It allows a computer with HFS (but no HFS Plus)
|
|
support in ROM to start up from an HFS Plus volume. When
|
|
creating the wrapper, Mac OS includes a System file
|
|
containing the minimum code to locate and mount the
|
|
embedded HFS Plus volume and continue booting from its
|
|
System file.</li>
|
|
|
|
<li>It improves the user experience when an HFS Plus
|
|
volume is inserted in a computer that has HFS support but
|
|
no HFS Plus support. On such a computer, the HFS wrapper
|
|
will be mounted as a volume, which prevents error dialogs
|
|
that might confuse the user into thinking the volume is
|
|
empty, damaged, or unreadable. The HFS wrapper may also
|
|
contain a Read Me document to explain the steps the user
|
|
should take to access their files.</li>
|
|
</OL>
|
|
|
|
<P>The rest of this section describes how the HFS wrapper is
|
|
laid out and how the HFS Plus volume is embedded within the
|
|
wrapper.</p>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
This section does not describe the HFS Plus volume format;
|
|
instead, it describes additions to the HFS volume format
|
|
that allow an HFS Plus volume (or some other volume) to be
|
|
embedded in an HFS volume. However, as all Mac OS volumes
|
|
are formatted with an HFS wrapper, all implementations
|
|
should be able to parse the wrapper to find the embedded HFS
|
|
Plus volume.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
An HFS Plus volume is not required to have an HFS wrapper.
|
|
In that case, the volume will start at the beginning of
|
|
the disk, and the volume header will be at offset 1024 bytes.
|
|
However, Apple software currently initializes all HFS Plus
|
|
volumes with an HFS wrapper.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
<H3>HFS Master Directory Block</H3>
|
|
|
|
<P>An HFS volume always contains a Master Directory Block
|
|
(MDB), at offset 1024 bytes. The MDB is similar to an HFS Plus
|
|
<a HREF = "#VolumeHeader">volume header</a>. In order to
|
|
support volumes embedded within an HFS volume, several
|
|
unused fields of the MDB have been changed, and are now used
|
|
to indicate the type, location, and size of the embedded
|
|
volume.</P>
|
|
|
|
<P>What was formerly the <CODE>drVCSize</CODE> field (at
|
|
offset 0x7C) is now named <CODE>drEmbedSigWord</CODE>. This
|
|
two-byte field contains a unique value that identifies the
|
|
type of embedded volume. When an HFS Plus volume is
|
|
embedded, <CODE>drEmbedSigWord</CODE> must be
|
|
<CODE>kHFSPlusSigWord</CODE> (<CODE>'H+'</CODE>), the same
|
|
value stored in the <CODE>signature</CODE> field of an HFS
|
|
Plus volume header.</P>
|
|
|
|
<P>What were formerly the <CODE>drVBMCSize</CODE> and
|
|
<CODE>drCtlCSize</CODE> fields (at offset <CODE>0x7E</CODE>)
|
|
have been combined into a single field occupying four bytes.
|
|
The new structure is named <CODE>drEmbedExtent</CODE> and is
|
|
of type <CODE>HFSExtentDescriptor</CODE>. It contains the
|
|
starting allocation block number (<CODE>startBlock</CODE>)
|
|
where the embedded volume begins and number of allocation
|
|
blocks (<CODE>blockCount</CODE> ) the embedded volume
|
|
occupies. The embedded volume must be contiguous. Both of
|
|
these values are in terms of the HFS wrapper's allocation
|
|
blocks, not HFS Plus allocation blocks.</p>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The description of the HFS volume format in
|
|
<a href="http://developer.apple.com/techpubs/mac/Files/Files-102.html">Inside
|
|
Macintosh: Files</a> describes these fields as being used to
|
|
store the size of various caches, and labels each one as
|
|
"used internally".</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>To actually find the embedded volume's location on disk,
|
|
an implementation must use the <CODE>drAlBlkSiz</CODE> and
|
|
<CODE>drAlBlSt</CODE> fields of the MDB. The
|
|
<CODE>drAlBlkSiz</CODE> field contains the size (in bytes)
|
|
of the HFS allocation blocks. The <CODE>drAlBlSt</CODE>
|
|
field contains the offset, in 512-byte blocks, of the
|
|
wrapper's allocation block 0 relative to the start of the
|
|
volume.</p>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
This embedding introduces a transform between HFS Plus
|
|
volume offsets and disk offsets. The HFS Plus volume exists
|
|
on a virtual disk embedded within the real disk. When
|
|
accessing an HFS Plus structure on an embedded disk, an
|
|
implementation must add the offset of the embedded disk to
|
|
the HFS Plus location. Listing 2 shows how one might do this,
|
|
assuming 512-byte sectors.
|
|
</P>
|
|
</TD></TR></table></CENTER>
|
|
|
|
|
|
|
|
<CENTER><table BORDER=0 CELLPADDING=5 WIDTH=550>
|
|
<TR>
|
|
<TD BGCOLOR="#E6E6E6">
|
|
<pre>static UInt32 HFSPlusSectorToDiskSector(UInt32 hfsPlusSector)
|
|
{
|
|
UInt32 embeddedDiskOffset;
|
|
|
|
embeddedDiskOffset = gMDB.drAlBlSt +
|
|
gMDB.drEmbedExtent.startBlock * (drAlBlkSiz / 512)
|
|
return embeddedDiskOffset + hfsPlusSector;
|
|
}</pre>
|
|
</TD>
|
|
</TR>
|
|
<TR>
|
|
<TD>
|
|
<P><b>Listing 2.</b> Sector transform for
|
|
embedded volumes.</p>
|
|
</TD>
|
|
</TR>
|
|
</table></CENTER>
|
|
|
|
|
|
<P>In order to prevent accidentally changing the files in
|
|
the HFS wrapper, the wrapper volume must be marked as
|
|
software-write-protected by setting
|
|
<CODE>kHFSVolumeSoftwareLockBit</CODE> in the
|
|
<CODE>drAtrb</CODE> (volume attributes) field of the MDB.
|
|
All correct HFS implementations will prevent any changes to
|
|
the wrapper volume.</P>
|
|
|
|
<P>To improve performance of HFS Plus volumes, the size of
|
|
the wrapper's allocation blocks should be a multiple of the
|
|
size of the HFS Plus volume's allocation blocks. In
|
|
addition, the wrapper's allocation block start
|
|
(<CODE>drAlBlSt</CODE>) should be a multiple of the HFS Plus
|
|
volume's allocation block size (or perhaps 4 KB, if the HFS
|
|
Plus allocation blocks are larger). If these recommendations
|
|
are followed, the HFS Plus allocation blocks will be
|
|
properly aligned on the disk. And, if the HFS Plus
|
|
allocation block size is a multiple of the sector size,
|
|
then blocking and deblocking at the device driver level
|
|
will be minimized.</P>
|
|
|
|
<H3>Allocating Space for the Embedded Volume</H3>
|
|
|
|
<P>The space occupied by the embedded volume must be marked
|
|
as allocated in the HFS wrapper's volume bitmap (similar to
|
|
the HFS Plus <a HREF = "#AllocationFile">allocation file</a>)
|
|
and placed in the HFS wrapper's bad block file (similar to
|
|
the HFS Plus <a HREF = "#BadBlockFile">bad block file</a>).
|
|
This doesn't mean the blocks are actually bad; it merely
|
|
prevents the HFS Plus volume from being overwritten by newly
|
|
created files in the HFS wrapper, being deleted
|
|
accidentally, or being marked as free, usable space by HFS
|
|
disk repair utilities.</P>
|
|
|
|
<P>The <CODE>kHFSVolumeSparedBlocksMask</CODE> bit of the
|
|
<CODE>drAtrb</CODE> (volume attributes) field of the MDB
|
|
must be set to indicate that the volume has a bad blocks
|
|
file.</p>
|
|
|
|
<H3>Read Me and System Files</H3>
|
|
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>IMPORTANT:</B><BR>
|
|
|
|
This section is not part of the HFS Plus volume format. It
|
|
describes how the existing Mac OS implementation of HFS Plus
|
|
creates HFS wrappers. It is provided for your information
|
|
only.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P>As initialized by the Mac OS Disk Initialization Package,
|
|
the HFS wrapper volume contains five files in the root
|
|
folder.</P>
|
|
|
|
<UL>
|
|
<li>Read Me -- The Read Me file, whose name is actually
|
|
"Where_have_all_my_files_gone?", contains text explaining
|
|
that this volume is really an HFS Plus volume but the
|
|
contents cannot be accessed because HFS Plus is not
|
|
currently installed on the computer. It also describes
|
|
the steps needed to install HFS Plus support. Localized
|
|
system software will also create a localized version of
|
|
the file with localized file name and text content.</li>
|
|
|
|
<li>System and Finder (invisible) -- The System file
|
|
contains the minimum code to locate and mount the
|
|
embedded HFS Plus volume, and to continue booting from
|
|
the System file in the embedded volume. The Finder file
|
|
is empty; it is there to prevent older versions of the
|
|
Finder from de-blessing the wrapper's root directory,
|
|
which would prevent booting from the volume.</li>
|
|
|
|
<li>Desktop DB and Desktop DF (invisible) -- The Desktop
|
|
DB and Desktop DF files are an artifact of the way the
|
|
files on the wrapper volume are created.</li>
|
|
</UL>
|
|
|
|
<P>In addition, the root folder is set as the blessed folder
|
|
by placing its folder ID in the first <CODE>SInt32</CODE> of
|
|
the <CODE>drFndrInfo</CODE> (Finder information) field of
|
|
the MDB.</p>
|
|
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
|
|
<H2><a NAME="VolumeConsistencyChecks"></a>Volume Consistency Checks</H2>
|
|
|
|
<P>An HFS Plus volume is a complex data structure,
|
|
consisting of many different inter-related data structures.
|
|
Inconsistencies between these data structures could cause
|
|
serious data loss. When an HFS Plus implementation mounts a
|
|
volume, it must perform basic consistency checks to ensure
|
|
that the volume is consistent. In addition, the
|
|
implementation may choose to implement other, more advanced,
|
|
consistency checks.</P>
|
|
|
|
<P>Many of these consistency checks take a significant
|
|
amount of time to run. While a safe implementation might run
|
|
these checks every time a volume is mounted, most
|
|
implementations will want to rely on the correctness of the
|
|
previous implementation that modified the disk. The
|
|
implementation may avoid unnecessary checking by determining
|
|
whether the volume was last unmounted cleanly. If it was,
|
|
the implementation may choose to skip a consistency check.
|
|
</P>
|
|
|
|
<P>An implementation can determine whether a volume was
|
|
unmounted cleanly by looking at various flag bits in the
|
|
volume header. See <a HREF = "#VolumeAttributes">Volume
|
|
Attributes</a> for details.</P>
|
|
|
|
<H3><a NAME="CNIDCheck"></a>Next Catalog Node ID Consistency Check</H3>
|
|
|
|
<P>For an HFS Plus volume to work correctly with
|
|
many implementations, it is vital that the <CODE>nextCatalogID</CODE>
|
|
field of the <a HREF = "#VolumeHeader">volume header</a> be greater than
|
|
all <a HREF = "#CNID">CNIDs</a> currently used in the
|
|
catalog file. The algorithm to ensure this is as follows.
|
|
</P>
|
|
|
|
<UL>
|
|
<li>The implementation must iterate through all the leaf
|
|
nodes of the catalog file, looking for file and folder
|
|
records, determining the maximum CNID of any file or
|
|
folder in the catalog.</li>
|
|
|
|
<li>Once it knows the maximum CNID value, the
|
|
implementation must set <CODE>nextCatalogID</CODE> to a
|
|
value greater than it.</li>
|
|
</UL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>Note:</B><BR>
|
|
The consistency check of <CODE>nextCatalogID</CODE> must be
|
|
skipped if <code>kHFSCatalogNodeIDsReusedBit</code> is set
|
|
in the <code>attributes</code> field of the
|
|
<a HREF = "#VolumeHeader">volume header</a>.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<H3><a NAME="AllocationFileConsistencyCheck"></a>Allocation File
|
|
Consistency Check</H3>
|
|
|
|
|
|
<P>For an HFS Plus volume to work correctly, it's vital that
|
|
any allocation block in use by file system structures be
|
|
marked as allocated in the allocation file. The algorithm to
|
|
ensure this is as follows:</P>
|
|
|
|
<UL>
|
|
<li>The implementation must first walk the allocation
|
|
file, marking every allocation block as free. (This step
|
|
can be skipped to improve the performance of the
|
|
consistency check. All that will happen is that some
|
|
allocation blocks may have been marked as in-use, though
|
|
they are not really in use by any extent.)</li>
|
|
|
|
<li>The implementation must then mark the allocation
|
|
blocks containing the first 1536 bytes and the last
|
|
1024 bytes as allocated. These areas are either
|
|
reserved or used by the <a HREF = "#VolumeHeader">volume
|
|
header</a>.</li>
|
|
|
|
<li>The implementation must then mark the allocation
|
|
blocks used by all extents in all special files (the
|
|
catalog file, the extents overflow file, the allocation
|
|
file, the attributes file, and the startup file) as
|
|
allocated. These extents are all described in the
|
|
<a HREF = "#VolumeHeader">volume header</a>.</li>
|
|
|
|
<li>The implementation must then walk the leaf nodes of
|
|
the catalog file, marking all allocation blocks used by
|
|
extents in file records (in the
|
|
<CODE>HFSPlusForkData</CODE> structures for the data and
|
|
resource forks).</li>
|
|
|
|
<li>The implementation must then walk the leaf nodes of
|
|
the <a HREF = "#ExtentsOverflowFile">extents overflow
|
|
file</a>, marking all allocation blocks used by all
|
|
extents in all extent records as allocated.</li>
|
|
|
|
<li>The implementation must then walk the leaf nodes of
|
|
the attributes file, marking all allocation blocks used
|
|
by all extents described in fork data attributes and
|
|
extension attributes as allocated.</li>
|
|
</UL>
|
|
|
|
<CENTER><table BORDER=0 WIDTH=550>
|
|
<TR><TD BGCOLOR="#E6E6E6">
|
|
<P><B>WARNING:</B><BR>
|
|
|
|
To prevent the loss of user data, an implementation must
|
|
perform this check every time it mounts a volume that wasn't
|
|
unmounted cleanly. It is most important that an allocation
|
|
block that is in use be marked in the allocation file. It is
|
|
less important that an allocation block that is not in use
|
|
be cleared in the allocation file. If an allocation block is
|
|
marked as in-use by the allocation file, but not actually in
|
|
use by any extent, then that allocation block is really just
|
|
wasting space; it isn't otherwise dangerous.</p>
|
|
</TD></TR></table></CENTER>
|
|
|
|
<P><a HREF = "#top">Back to top</a></p>
|
|
|
|
<H2><a NAME="Summary"></a>Summary</H2>
|
|
|
|
<P>Volume format specifications are <del>fun</del> <ins>exhausting</ins>.</p>
|
|
|
|
|
|
<P><a href="#top">Back to top</a></P>
|
|
|
|
<H2><a name="References"></a>References</H2>
|
|
|
|
<p><a href="http://developer.apple.com/techpubs/mac/Files/Files-2.html">Inside
|
|
Macintosh: Files</a>, especially the
|
|
<a href="http://developer.apple.com/techpubs/mac/Files/Files-99.html#HEADING99-0">Data
|
|
Organization on Volumes</a> section.</p>
|
|
|
|
<p><a href="http://developer.apple.com/documentation/Carbon/Reference/Finder_Interface/index.html">
|
|
Finder Interface Reference</a> section of the Carbon user experience documentation.</p>
|
|
|
|
<p>
|
|
<a href="http://developer.apple.com/technotes/tn/tn1189.html">
|
|
Technical Note 1189: The Monster Disk Driver Technote</a>,
|
|
especially the
|
|
<a href="http://developer.apple.com/technotes/tn/tn1189.html#SecretsOfThePartitionMap">
|
|
Secrets of the Partition Map</a> section.</p>
|
|
|
|
<p><CITE>Algorithms in C</CITE>, Robert Sedgewick,
|
|
Addison-Wesley, 1992, especially the section on B-trees.</p>
|
|
|
|
<H2><a NAME="Changes"></a>Change History</H2>
|
|
|
|
|
|
<table BORDER=0 CELLPADDING=3 WIDTH=544>
|
|
<TR>
|
|
<TD WIDTH=100>
|
|
<P ALIGN=center>01-February-1999</p>
|
|
</TD><TD>
|
|
<P>Originally published.</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=100>
|
|
<P ALIGN=center>01-March-1999</p>
|
|
</TD><TD>
|
|
<P>Updated to include a warning about initializing disks with a
|
|
<a HREF = "#SmallAllocationBlockWarning">small allocation block size</a>.</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=100>
|
|
<P ALIGN=CENTER>18-January-2000</p>
|
|
</TD><TD>
|
|
<P>Updated to clarify the allocation block usage and placement of the
|
|
alternate volume header on volumes where the disk size is
|
|
not an <a HREF = "#OddSizeVolumes">even multiple of the
|
|
allocation block size</a>.</p>
|
|
</TD></TR>
|
|
<TR>
|
|
<TD WIDTH=100 VALIGN=TOP>
|
|
<P ALIGN=CENTER>2-Mar-2004</p>
|
|
</TD><TD>
|
|
<P>Added information about the <a HREF = "#Journal">journal</a>.</p>
|
|
<P>Updated the <a HREF = "#HFSPlusPermissions">HFS Plus Permissions</a> section
|
|
to describe the way Mac OS X uses permissions on disk.</p>
|
|
<P>Added the <a HREF = "#HardLinks">Hard Links</a> and <a HREF = "#Symlinks">
|
|
Symbolic Links</a> sections to describe how Mac OS X implements hard and symbolic links.</p>
|
|
<P>Added information on how <a href="#CNID">catalog node IDs</a> can wrap
|
|
around and be reused, in which case the <a href="#CNIDCheck">CNID consistency
|
|
check</a> must be skipped.</p>
|
|
<P>Added details about the <a href="#VolumeFinderInfo">Finder information</a>
|
|
in the <a HREF = "#VolumeHeader">volume header</a>. Added a section on the
|
|
<a href="#FinderInfo">Finder information</a> for files and directories.</p>
|
|
<P>Added a section about <a href="#HFSX">HFSX</a>, an extension of HFS Plus
|
|
that allows for case-sensitive file and directory names.</P>
|
|
<P>Added a section about how Mac OS X version 10.3 uses a
|
|
<a href="#MetadataZone">Metadata Zone</a> and
|
|
<a href="#HotFile">adaptive hot file clustering</a>.</P>
|
|
|
|
</TD></TR>
|
|
</table>
|
|
|
|
<P><a href="#top">Back to top</a></P>
|
|
|
|
<H2><a NAME="Downloads"></a>Downloadables</H2>
|
|
|
|
<center><table BORDER=0 CELLPADDING=3 WIDTH="100%">
|
|
<TR>
|
|
<TD WIDTH=50>
|
|
<P ALIGN=CENTER><img src="images/bluebook.gif" width=22 height=23 align=middle alt="Bluebook.gif"></p>
|
|
</TD><TD>
|
|
<P>FastUnicodeCompare.c (43 KB).</p>
|
|
</TD><TD WIDTH=60>
|
|
<P><a HREF = "downloads/tn1150.1.hqx">Download</a></p>
|
|
</TD></TR>
|
|
</table></center>
|
|
|
|
<P><a href="#top">Back to top</a></P>
|
|
|
|
|
|
</TD></TR></table></center>
|
|
<!-- begin_footer_information --><div id="blackout">
|
|
<div id="preload"></div>
|
|
</div>
|
|
<div id="leave_feedback" class="button" role="button" tabindex="0">Feedback</div>
|
|
<div id="modal" aria-hidden="true">
|
|
<div id="closebox" tabindex="0" aria-label="Close feedback form" role="button"></div>
|
|
<div id="sending" class="hidden">
|
|
<h2 tabindex="0">Sending feedback…</h2>
|
|
<div id="sending_img"></div>
|
|
</div>
|
|
<div id="error" class="hidden">
|
|
<h2 tabindex="0">We’re sorry, an error has occurred.</h2>
|
|
<p>Please try submitting your feedback later.</p>
|
|
<div id="error_icon"></div>
|
|
</div>
|
|
<div id="success" class="hidden">
|
|
<h2 tabindex="0">Thank you for providing feedback!</h2>
|
|
<p>Your input helps improve our developer documentation.</p>
|
|
<div id="thank_you_icon"></div>
|
|
</div>
|
|
|
|
<form id="feedback" action="#" method="post">
|
|
<div class="left-leaf">
|
|
<h2 id="helpful_title" data-asterisk="a1" tabindex="0">How helpful is this document?</h2>
|
|
<sup id="a1" class="asterisk" aria-hidden="true">*</sup>
|
|
|
|
<div id="star_group" role="radiogroup" aria-required="true">
|
|
<label>
|
|
<input class="radio" type="radio" name="helped" value="1" />
|
|
Very helpful
|
|
</label>
|
|
<label>
|
|
<input class="radio" type="radio" name="helped" value="2" />
|
|
Somewhat helpful
|
|
</label>
|
|
<label>
|
|
<input class="radio" type="radio" name="helped" value="3" />
|
|
Not helpful
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="right-leaf">
|
|
<h2>How can we improve this document?</h2>
|
|
<div id="improve" class="checkboxes">
|
|
<label>
|
|
<input type="checkbox" name="typo" />
|
|
Fix typos or links
|
|
</label>
|
|
<label>
|
|
<input type="checkbox" name="infoIncorrect" />
|
|
Fix incorrect information
|
|
</label>
|
|
<label>
|
|
<input type="checkbox" name="needs_examples" />
|
|
Add or update code samples
|
|
</label>
|
|
<label>
|
|
<input type="checkbox" name="needs_art" />
|
|
Add or update illustrations
|
|
</label>
|
|
<label>
|
|
<input type="checkbox" name="missingInfo" />
|
|
Add information about...
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<textarea id="comment" name="problem" cols="70" rows="8" placeholder="Please tell us more about your experience with this document" data-asterisk="a2" required></textarea>
|
|
<sup id="a2" class="asterisk" aria-hidden="true">*</sup>
|
|
|
|
<input id="email" type="email" name="email" placeholder="Email (optional)" size="48">
|
|
|
|
<p class="fineprint">
|
|
<em aria-hidden="true"><span>*</span> Required information</em>
|
|
</p>
|
|
|
|
<input id="submit" type="button" value="Send" />
|
|
|
|
<section id="legal">
|
|
<p>
|
|
To submit a product bug or enhancement request, please visit the
|
|
<a href="https://developer.apple.com/bugreporter/" target="_blank">Bug Reporter</a>
|
|
page.
|
|
</p>
|
|
<p>
|
|
Please read <a href="http://www.apple.com/legal/policies/ideas.html" target="_blank">Apple's Unsolicited Idea Submission Policy</a>
|
|
before you send us your feedback.
|
|
</p>
|
|
</section>
|
|
</form>
|
|
</div>
|
|
<div id="globalfooter" class="hideInXcode hideOnPrint">
|
|
<div class="gf-sosumi">
|
|
<p>Copyright © 2013 Apple Inc. All rights reserved.</p>
|
|
<ul class="piped">
|
|
<li><a href="http://www.apple.com/legal/internet-services/terms/site.html" class="first" target="_blank">Terms of Use</a></li>
|
|
<li><a href="http://www.apple.com/privacy/" target="_blank">Privacy Policy</a></li>
|
|
</ul>
|
|
</div>
|
|
</div><!--/globalfooter-->
|
|
<!-- end_footer_information -->
|
|
|
|
|
|
</div>
|
|
</article>
|
|
</body>
|
|
<script type="text/javascript" src="../../Resources/893/JavaScript/lib/prototype.js"></script>
|
|
<script type="text/javascript" src="../../Resources/893/JavaScript/library.js"></script>
|
|
<script type="text/javascript" src="../../Resources/893/JavaScript/feedback.js"></script>
|
|
|
|
|
|
<script type="text/javascript" src="/library/webstats/pagetracker.js"></script>
|
|
<script type="text/javascript">
|
|
if(typeof PageTracker !== 'undefined') {
|
|
if(window.addEventListener) {
|
|
window.addEventListener("load", function(){PageTracker.logPageLoad()},false);
|
|
} else if(window.attachEvent) {
|
|
window.attachEvent("onload",function(){PageTracker.logPageLoad()});
|
|
}
|
|
}
|
|
</script>
|
|
</HTML>
|