Commit d1081202 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] HFS rewrite

From: Roman Zippel <zippel@linux-m68k.org>

This is a complete rewrite of the HFS driver, it gets rid of a all the
special conversion options, which belong in user space.  The driver uses now
a btree support very similiar to HFS+, so that both could be merged at some
point.

Thanks to Ethan Benson <erbenson@alaska.net> for a number of patches to make
the driver more compliant with the spec and Christoph Hellwig <hch@lst.de>
for fixing up the documentation.
parent abd63124
Macintosh HFS Filesystem for Linux
==================================
HFS stands for ``Hierarchical File System'' and is the filesystem used
by the Mac Plus and all later Macintosh models. Earlier Macintosh
models used MFS (``Macintosh File System''), which is not supported,
MacOS 8.1 and newer support a filesystem called HFS+ that's similar to
HFS but is extended in various areas. Use the hfsplus filesystem driver
to access such filesystems from Linux.
Mount options
=============
When mounting an HFS filesystem, the following options are accepted:
creator=cccc, type=cccc
Specifies the creator/type values as shown by the MacOS finder
used for creating new files. Default values: '????'.
uid=n, gid=n
Specifies the user/group that owns all files on the filesystems.
Default: user/group id of the mounting process.
dir_umask=n, file_umask=n, umask=n
Specifies the umask used for all files , all directories or all
files and directories. Defaults to the umask of the mounting process.
session=n
Select the CDROM session to mount as HFS filesystem. Defaults to
leaving that decision to the CDROM driver. This option will fail
with anything but a CDROM as underlying devices.
part=n
Select partition number n from the devices. Does only makes
sense for CDROMS because they can't be partitioned under Linux.
For disk devices the generic partition parsing code does this
for us. Defaults to not parsing the partition table at all.
quiet
Ignore invalid mount options instead of complaining.
Writing to HFS Filesystems
==========================
HFS is not a UNIX filesystem, thus it does not have the usual features you'd
expect:
o You can't modify the set-uid, set-gid, sticky or executable bits or the uid
and gid of files.
o You can't create hard- or symlinks, device files, sockets or FIFOs.
HFS does on the other have the concepts of multiple forks per file. These
non-standard forks are represented as hidden additional files in the normal
filesystems namespace which is kind of a cludge and makes the semantics for
the a little strange:
o You can't create, delete or rename resource forks of files or the
Finder's metadata.
o They are however created (with default values), deleted and renamed
along with the corresponding data fork or directory.
o Copying files to a different filesystem will loose those attributes
that are essential for MacOS to work.
Creating HFS filesystems
===================================
The hfsutils package from Robert Leslie contains a program called
hformat that can be used to create HFS filesystem. See
<http://www.mars.org/home/rob/proj/hfs/> for details.
Credits
=======
The HFS drivers was written by Paul H. Hargrovea (hargrove@sccm.Stanford.EDU)
and is now maintained by Roman Zippel (roman@ardistech.com) at Ardis
Technologies.
Roman rewrote large parts of the code and brought in btree routines derived
from Brad Boyer's hfsplus driver (also maintained by Roman now).
......@@ -862,8 +862,8 @@ W: http://www.nyx.net/~arobinso
S: Maintained
HFS FILESYSTEM
P: Oliver Neukum
M: oliver@neukum.org
P: Roman Zippel
M: zippel@linux-m68k.org
L: linux-kernel@vger.kernel.org
S: Maintained
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Installation instructions for the HFS Filesystem for Linux
Paul H. Hargrove, hargrove@sccm.Stanford.EDU
version 0.95 28 Apr 1997
This document explains how to compile and install version 0.95 of
hfs_fs, the HFS filesystem for Linux.
11.. SSyysstteemm RReeqquuiirreemmeennttss
You will need the following to compile and use this release of hfs_fs:
+o Kernel version 2.0.1 or newer compiled with modules enabled
(CONFIG_MODULES).
+o The kernel sources (or at least the header files) available online.
+o The module utilities package current for your kernel version and an
understanding of how to use it.
22.. IInnssttaallllaattiioonn
This release of the HFS filesystem is not part of the official kernel
distribution. Therefore, it is compiled as a module and then loaded
into the kernel using the module utilities. Therefore, your kernel
must be compiled with CONFIG_MODULES enabled.
22..11.. CCoommppiilliinngg tthhee llooaaddaabbllee mmoodduullee
To compile hfs.o you should only need to execute ``make'' in the
hfs_fs source directory.
If gcc complains about not finding a large number of header files with
names beginning with ``linux/'' then you probably don't have the
kernel header files installed correctly. Either /usr/include/linux,
/usr/include/asm and /usr/include/scsi should be symbolic links to
include/linux, include/asm and include/scsi in the kernel source tree
for the kernel you wish to use hfs_fs with, or else they should be
directories containing the header files for the kernel you wish to use
hfs_fs with.
If gcc complains about not finding linux/version.h, then you will need
to run ``make dep'' in the kernel source directory to build it. Under
MkLinux, run ``make include/linux/version.h'' instead.
If gcc complains about not finding the files linux/config.h or
linux/autoconf.h, then you will need to run ``make config'' and ``make
dep'' in the kernel source directory to build these two files.
If you are compiling on a DEC Alpha and receive messages saying
assignment from incompatible pointer type when compiling files dir_*.c
and file_*.c, then you need to change a single line in the file
linux/hfs_fs.h. Remove the text ``&& !defined(__alpha__)'' from the
end of line 217.
22..22.. IInnssttaalllliinngg tthhee mmoodduullee iinn tthhee mmoodduulleess ddiirreeccttoorryy ((ooppttiioonnaall))
If you plan to use kerneld to automatically load the module or if you
wish to use modprobe or insmod without supplying a complete path to
hfs.o, then you will need to copy hfs.o into a directory where the
module utilities expect to find it.
The proper directory may depend slightly on your configuration.
However, /lib/modules/default/fs/ is a common one for filesystem
modules. Once hfs.o is in the proper directory you should run depmod
-a to update the dependency list used by kerneld and modprobe.
22..33.. LLooaaddiinngg tthhee mmoodduullee iinnttoo tthhee rruunnnniinngg kkeerrnneell
There are three ways to accomplish this:
1. If you are running kerneld and have installed hfs.o in the modules
directory then you don't need to issue any commands; the module
will be loaded when you attempt to mount an HFS filesystem.
2. If you are _n_o_t running kerneld then you can load hfs.o manually by
running modprobe hfs.o. If you have not installed hfs.o in one of
the standard module directories, then you will need provide a full
path to the file hfs.o.
3. If you have been experiencing kernel crashes with hfs_fs, then you
should file a bug report including the names of the functions which
the EIP and Stack Trace point into. To help with this you can ask
for relocation map for the module when you load it. To do this
load the module with ``insmod -m hfs.o >loadmap''. Again, you may
need a full path to the file hfs.o if you have not placed it in one
of the standard module directories.
22..44.. UUssiinngg tthhee mmoodduullee wwiitthh vveerrssiioonneedd ssyymmbboollss
All the interface between the module and the kernel take place through
very stable (since the mid-1.3.x kernels) parts of the kernel. If you
enabled versioned symbols (CONFIG_MODVERSIONS) when you compiled your
kernel you should often be able to compile this module once and then
use it with many kernels newer than the one you compiled it for.
In any case, it is unlikely that this module will need changes with
each new kernel patch; simple recompilation should usually suffice.
33.. LLeeggaall NNoottiicceess
33..11.. TThhiiss DDooccuummeenntt
This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
Permission is granted to make and distribute verbatim copies of this
document provided the copyright notice and this permission notice are
preserved on all copies.
Permission is granted to copy and distribute modified versions of this
document under the conditions for verbatim copies above, provided a
notice clearly stating that the document is a modified version is also
included in the modified document.
Permission is granted to copy and distribute translations of this
document into another language, under the conditions specified above
for modified versions.
Permission is granted to convert this document into another media
under the conditions specified above for modified versions provided
the requirement to acknowledge the source document is fulfilled by
inclusion of an obvious reference to the source document in the new
media. Where there is any doubt as to what defines ``obvious'' the
copyright owner reserves the right to decide.
......@@ -4,7 +4,7 @@
obj-$(CONFIG_HFS_FS) += hfs.o
hfs-objs := balloc.o bdelete.o bfind.o bins_del.o binsert.o bitmap.o bitops.o \
bnode.o brec.o btree.o catalog.o dir.o dir_cap.o dir_dbl.o \
dir_nat.o extent.o file.o file_cap.o file_hdr.o inode.o mdb.o \
part_tbl.o string.o super.o sysdep.o trans.o version.o
hfs-objs := bitmap.o bfind.o bnode.o brec.o btree.o \
catalog.o dir.o extent.o inode.o mdb.o \
part_tbl.o string.o super.o sysdep.o trans.o
The hfs_fs "to do" list.
------------------------
Items are broken down into groups and the groups are listed in order
from most important to least important. The items within each group
are not placed in any particular order. The order in which items are
listed probably doesn't correlate well with the order they will be
addressed.
Genuine bugs:
1. Header files have compiled-in limit (currently 10) on descriptors.
Missing features:
1. 1k block support is needed for some devices.
2. An ioctl()-based interface is needed to provide a consistent way
to do things under all of the representations of forked files.
Possible additional "fork" mount options:
1. AppleSingle.
2. The scheme MacOS uses on FAT disks (PC Exchange).
3. "Flat" (no resource forks or metadata).
Performance issues:
1. Use drAllocPtr to speed block allocations.
2. Keep a real cache of bnodes, rather than just a hash table of
the ones that are currently in use.
3. Keep a real cache of extent records, rather than just a linked
list of the ones that are currently in use and the one most
recently used. This is particularly needed to get acceptable
performance with multiple readers on a file. Perhaps simply
keep them in memory once they've been read until the file is
closed.
Implementation details:
1. Allocation scheme could/should be closer to that used by Apple.
2. B*-tree insertion could/should be closer to that used by Apple.
3. Magic-number checks on data structures are rarely done.
4. Error recovery is needed for failed binsert(), bdelete() and rename().
5. Deadlock detection is needed to make insert_empty_bnode() and
bdelete() less likely to hang on a corrupted B-tree.
6. Metadata for covered directories shouldn't appear in the filesystem.
Under CAP and AppleDouble it currently does. However, the obvious
solution is a real performance killer and is not worth implementing.
Fantasy features:
1. Access Desktop file/database for comment and icon.
2. Implement mmap() for AppleDouble header files and CAP info files.
3. Implement AppleShare client support.
Suggestions/comments/questions are welcome.
Code addressing any of the issues listed above is especially welcome.
Paul H. Hargrove
hargrove@sccm.Stanford.EDU
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* linux/fs/hfs/bins_del.c
*
* Copyright (C) 1995-1997 Paul H. Hargrove
* This file may be distributed under the terms of the GNU General Public License.
*
* This file contains the code common to inserting and deleting records
* in a B-tree.
*
* "XXX" in a comment is a note to myself to consider changing something.
*
* In function preconditions the term "valid" applied to a pointer to
* a structure means that the pointer is non-NULL and the structure it
* points to has all fields initialized to consistent values.
*/
#include "hfs_btree.h"
/*================ File-local functions ================*/
/*
* hfs_bnode_update_key()
*
* Description:
* Updates the key for a bnode in its parent.
* The key change is propagated up the tree as necessary.
* Input Variable(s):
* struct hfs_brec *brec: the search path to update keys in
* struct hfs_belem *belem: the search path element with the changed key
* struct hfs_bnode *bnode: the bnode with the changed key
* int offset: the "distance" from 'belem->bn' to 'bnode':
* 0 if the change is in 'belem->bn',
* 1 if the change is in its right sibling, etc.
* Output Variable(s):
* NONE
* Returns:
* void
* Preconditions:
* 'brec' points to a valid (struct hfs_brec)
* 'belem' points to a valid (struct hfs_belem) in 'brec'.
* 'bnode' points to a valid (struct hfs_bnode) which is non-empty
* and is 'belem->bn' or one of its siblings.
* 'offset' is as described above.
* Postconditions:
* The key change is propagated up the tree as necessary.
*/
void hfs_bnode_update_key(struct hfs_brec *brec, struct hfs_belem *belem,
struct hfs_bnode *bnode, int offset)
{
int record = (--belem)->record + offset;
void *key = bnode_datastart(bnode) + 1;
int keysize = brec->tree->bthKeyLen;
struct hfs_belem *limit;
memcpy(1+bnode_key(belem->bnr.bn, record), key, keysize);
/* don't trash the header */
if (brec->top > &brec->elem[1]) {
limit = brec->top;
} else {
limit = &brec->elem[1];
}
while ((belem > limit) && (record == 1)) {
record = (--belem)->record;
memcpy(1+belem_key(belem), key, keysize);
}
}
/*
* hfs_bnode_shift_right()
*
* Description:
* Shifts some records from a node to its right neighbor.
* Input Variable(s):
* struct hfs_bnode* left: the node to shift records from
* struct hfs_bnode* right: the node to shift records to
* hfs_u16 first: the number of the first record in 'left' to move to 'right'
* Output Variable(s):
* NONE
* Returns:
* void
* Preconditions:
* 'left' and 'right' point to valid (struct hfs_bnode)s.
* 'left' contains at least 'first' records.
* 'right' has enough free space to hold the records to be moved from 'left'
* Postconditions:
* The record numbered 'first' and all records after it in 'left' are
* placed at the beginning of 'right'.
* The key corresponding to 'right' in its parent is NOT updated.
*/
void hfs_bnode_shift_right(struct hfs_bnode *left, struct hfs_bnode *right,
int first)
{
int i, adjust, nrecs;
unsigned size;
hfs_u16 *to, *from;
if ((first <= 0) || (first > left->ndNRecs)) {
hfs_warn("bad argument to shift_right: first=%d, nrecs=%d\n",
first, left->ndNRecs);
return;
}
/* initialize variables */
nrecs = left->ndNRecs + 1 - first;
size = bnode_end(left) - bnode_offset(left, first);
/* move (possibly empty) contents of right node forward */
memmove(bnode_datastart(right) + size,
bnode_datastart(right),
bnode_end(right) - sizeof(struct NodeDescriptor));
/* copy in new records */
memcpy(bnode_datastart(right), bnode_key(left,first), size);
/* fix up offsets in right node */
i = right->ndNRecs + 1;
from = RECTBL(right, i);
to = from - nrecs;
while (i--) {
hfs_put_hs(hfs_get_hs(from++) + size, to++);
}
adjust = sizeof(struct NodeDescriptor) - bnode_offset(left, first);
i = nrecs-1;
from = RECTBL(left, first+i);
while (i--) {
hfs_put_hs(hfs_get_hs(from++) + adjust, to++);
}
/* fix record counts */
left->ndNRecs -= nrecs;
right->ndNRecs += nrecs;
}
/*
* hfs_bnode_shift_left()
*
* Description:
* Shifts some records from a node to its left neighbor.
* Input Variable(s):
* struct hfs_bnode* left: the node to shift records to
* struct hfs_bnode* right: the node to shift records from
* hfs_u16 last: the number of the last record in 'right' to move to 'left'
* Output Variable(s):
* NONE
* Returns:
* void
* Preconditions:
* 'left' and 'right' point to valid (struct hfs_bnode)s.
* 'right' contains at least 'last' records.
* 'left' has enough free space to hold the records to be moved from 'right'
* Postconditions:
* The record numbered 'last' and all records before it in 'right' are
* placed at the end of 'left'.
* The key corresponding to 'right' in its parent is NOT updated.
*/
void hfs_bnode_shift_left(struct hfs_bnode *left, struct hfs_bnode *right,
int last)
{
int i, adjust, nrecs;
unsigned size;
hfs_u16 *to, *from;
if ((last <= 0) || (last > right->ndNRecs)) {
hfs_warn("bad argument to shift_left: last=%d, nrecs=%d\n",
last, right->ndNRecs);
return;
}
/* initialize variables */
size = bnode_offset(right, last + 1) - sizeof(struct NodeDescriptor);
/* copy records to left node */
memcpy(bnode_dataend(left), bnode_datastart(right), size);
/* move (possibly empty) remainder of right node backward */
memmove(bnode_datastart(right), bnode_datastart(right) + size,
bnode_end(right) - bnode_offset(right, last + 1));
/* fix up offsets */
nrecs = left->ndNRecs;
i = last;
from = RECTBL(right, 2);
to = RECTBL(left, nrecs + 2);
adjust = bnode_offset(left, nrecs + 1) - sizeof(struct NodeDescriptor);
while (i--) {
hfs_put_hs(hfs_get_hs(from--) + adjust, to--);
}
i = right->ndNRecs + 1 - last;
++from;
to = RECTBL(right, 1);
while (i--) {
hfs_put_hs(hfs_get_hs(from--) - size, to--);
}
/* fix record counts */
left->ndNRecs += last;
right->ndNRecs -= last;
}
/*
* hfs_bnode_in_brec()
*
* Description:
* Determines whethet a given bnode is part of a given brec.
* This is used to avoid deadlock in the case of a corrupted b-tree.
* Input Variable(s):
* hfs_u32 node: the number of the node to check for.
* struct hfs_brec* brec: the brec to check in.
* Output Variable(s):
* NONE
* Returns:
* int: 1 it found, 0 if not
* Preconditions:
* 'brec' points to a valid struct hfs_brec.
* Postconditions:
* 'brec' is unchanged.
*/
int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec)
{
const struct hfs_belem *belem = brec->bottom;
while (belem && (belem >= brec->top)) {
if (belem->bnr.bn && (belem->bnr.bn->node == node)) {
return 1;
}
--belem;
}
return 0;
}
This diff is collapsed.
This diff is collapsed.
/*
* linux/fs/hfs/bitops.c
*
* Copyright (C) 1996 Paul H. Hargrove
* This file may be distributed under the terms of the GNU General Public License.
*
* This file contains functions to handle bitmaps in "left-to-right"
* bit-order such that the MSB of a 32-bit big-endian word is bit 0.
* (This corresponds to bit 7 of a 32-bit little-endian word.)
*
* I have tested and confirmed that the results are identical on the
* Intel x86, PowerPC and DEC Alpha processors.
*
* "XXX" in a comment is a note to myself to consider changing something.
*/
#include "hfs.h"
/*================ Global functions ================*/
/*
* hfs_find_zero_bit()
*
* Description:
* Given a block of memory, its length in bits, and a starting bit number,
* determine the number of the first zero bits (in left-to-right ordering)
* in that range.
*
* Returns >= 'size' if no zero bits are found in the range.
*
* Accesses memory in 32-bit aligned chunks of 32-bits and thus
* may read beyond the 'size'th bit.
*/
hfs_u32 hfs_find_zero_bit(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
{
const hfs_u32 *end = start + ((size + 31) >> 5);
const hfs_u32 *curr = start + (offset >> 5);
int bit = offset % 32;
if (offset < size) {
/* scan the first partial hfs_u32 for zero bits */
if (bit != 0) {
do {
if (!hfs_test_bit(bit, curr)) {
goto done;
}
++bit;
} while (bit < 32);
bit = 0;
++curr;
}
/* scan complete hfs_u32s for the first zero bit */
while (curr < end) {
if (*curr == ~((hfs_u32)0)) {
++curr;
} else {
while (hfs_test_bit(bit, curr)) {
++bit;
}
break;
}
}
done:
bit |= (curr - start) << 5;
return bit;
} else {
return size;
}
}
/*
* hfs_count_zero_bits()
*
* Description:
* Given a block of memory, its length in bits, and a starting bit number,
* determine the number of consecutive zero bits (in left-to-right ordering)
* in that range.
*
* Accesses memory in 32-bit aligned chunks of 32-bits and thus
* may read beyond the 'size'th bit.
*/
hfs_u32 hfs_count_zero_bits(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
{
const hfs_u32 *end = start + ((size + 31) >> 5);
const hfs_u32 *curr = start + (offset >> 5);
int bit = offset % 32;
if (offset < size) {
/* scan the first partial hfs_u32 for one bits */
if (bit != 0) {
do {
if (hfs_test_bit(bit, curr)) {
goto done;
}
++bit;
} while (bit < 32);
bit = 0;
++curr;
}
/* scan complete hfs_u32s for the first one bit */
while (curr < end) {
if (*curr == ((hfs_u32)0)) {
++curr;
} else {
while (!hfs_test_bit(bit, curr)) {
++bit;
}
break;
}
}
done:
bit |= (curr - start) << 5;
if (bit > size) {
bit = size;
}
return bit - offset;
} else {
return 0;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* linux/fs/hfs/version.c
*
* Copyright (C) 1995-1997 Paul H. Hargrove
* This file may be distributed under the terms of the GNU General Public License.
*
* This file contains the version string for this release.
*/
const char hfs_version[]="0.96";
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment