\subsection{Vnode Interface Support}

% Ok, this needs a little organization

% subsubsection: memory management
%   macfsvnodes
%   lookup issues
% subsubsection: locking
%   mostly provided by the b-tree
%   lock modified files
%   lock a directory when listing all contents
% subsubsection: Unix semantics
%   handling the deleted case

  The implementation of the vnode interface presented several
problems.  Apart from translating vnode calls to the user-level
library, there are memory management and mutual exclusion problems.
Further, there are aspects of Unix semantics that are difficult to
emulate.  The DosFs code \cite{DOSFS} deals with the same problems,
and some of the general solutions described below originate there.

\subsubsection{Memory management}

  MacFS keeps a vnode structure with extra fields, notably for the
file's catalog record and a lock.  Because open vnodes are kept in
memory and not on disk, to mantain consistency there must be at most
one vnode per file, and at most one Unix process modifying a vnode at
any one time.

  The fundamental low level operations on a vnode are lookup, hold,
release, inactivate, lock, and unlock.  Lock and unlock provide mutual
exclusion at the level of vnode access; hold and release prevent and
allow a vnode to exit memory (be inactivated), respectively.  Lookup
takes a parent directory vnode and child name and returns a locked
held vnode.  Inactivation removes an unlocked unheld vnode from
memory.

  Lookup and inactivation are calls defined by the vnode interface.
There is also a special case of lookup for getting the vnode of the
root.  Hold and release are both defined and implemented by the VFS
layer.  To prevent vnode duplication, MacFS hashes vnodes by device
and file or directory id.  Lookup searches the hash table.  If the
vnode is not found, it searches the \B\ and adds a new vnode to the
hash table.  inactivate removes hash entries.  There is a possible
race condition between removing an entry from the hash table and
looking it up, which is solved by locking the vnode and rechecking its
contents.

\subsubsection{Handling Re-entrance}
  The consistency of the disk meta-data is mantained by the low-level
B-tree code, which provides mutual exclusion to all accesses to the
\C, \E, and allocation bitmap.  Operations that modify or examine a
file vnode require locking by the vnode interface layer -- these
include the file being moved in renaming or deleting operations.  None
of the directory-level operations require locking the directory vnode
while disk access takes place.

\subsubsection{Mantaining Unix Semantics}

  The vnode-level mount operation gives an entire filesystem one set of
protection bits and user and group owner id's.  Due to the lack of
per-file execution bits and the foreign origin of the data, we did not
implement execution of files from MacFs.  Hard links are inherently
not supported by the disk structures, and symbolic links aren't
supported, though a special Finder Info file type could easily be used
to support symbolic links.

  Deleting an open file is a special case.  Unix semantics allows a
file that is open to be deleted, with the result that existing file
descriptors can be used to read and write the file until it is closed;
the actual disk file is then deleted.  The inode mechanism makes this
easy and natural for Unix; on the Mac, the catalog record is moved to
a hidden unique name (built using the unique file-id) and the vnode is
flagged so that the underlying file is deleted when the vnode is
deactivated.  For a vnode to know how to delete itself on inactivation
requires storing the parent id and file name in the vnode.

  The disk structures make no provision for per-file access or mode
information.  Due to this there is only one access restriction per
filesystem, specified at mount time.  Due to time limitations various
specialized parts of the vnode interface are not supported, notably
the NFS-inspired calls involving fid's and the pagein and pageout
calls to support file execution.
