blob: b3f8477f58cbd0cb9b10e5726f7fb87a3a150881 [file] [log] [blame]
/*
* linux/fs/hfs/mdb.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 functions for reading/writing the MDB.
*
* "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.
*
* The code in this file initializes some structures which contain
* pointers by calling memset(&foo, 0, sizeof(foo)).
* This produces the desired behavior only due to the non-ANSI
* assumption that the machine representation of NULL is all zeros.
*/
#include "hfs.h"
/*================ File-local data types ================*/
/*
* The HFS Master Directory Block (MDB).
*
* Also known as the Volume Information Block (VIB), this structure is
* the HFS equivalent of a superblock.
*
* Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
*
* modified for HFS Extended
*/
struct raw_mdb {
hfs_word_t drSigWord; /* Signature word indicating fs type */
hfs_lword_t drCrDate; /* fs creation date/time */
hfs_lword_t drLsMod; /* fs modification date/time */
hfs_word_t drAtrb; /* fs attributes */
hfs_word_t drNmFls; /* number of files in root directory */
hfs_word_t drVBMSt; /* location (in 512-byte blocks)
of the volume bitmap */
hfs_word_t drAllocPtr; /* location (in allocation blocks)
to begin next allocation search */
hfs_word_t drNmAlBlks; /* number of allocation blocks */
hfs_lword_t drAlBlkSiz; /* bytes in an allocation block */
hfs_lword_t drClpSiz; /* clumpsize, the number of bytes to
allocate when extending a file */
hfs_word_t drAlBlSt; /* location (in 512-byte blocks)
of the first allocation block */
hfs_lword_t drNxtCNID; /* CNID to assign to the next
file or directory created */
hfs_word_t drFreeBks; /* number of free allocation blocks */
hfs_byte_t drVN[28]; /* the volume label */
hfs_lword_t drVolBkUp; /* fs backup date/time */
hfs_word_t drVSeqNum; /* backup sequence number */
hfs_lword_t drWrCnt; /* fs write count */
hfs_lword_t drXTClpSiz; /* clumpsize for the extents B-tree */
hfs_lword_t drCTClpSiz; /* clumpsize for the catalog B-tree */
hfs_word_t drNmRtDirs; /* number of directories in
the root directory */
hfs_lword_t drFilCnt; /* number of files in the fs */
hfs_lword_t drDirCnt; /* number of directories in the fs */
hfs_byte_t drFndrInfo[32]; /* data used by the Finder */
hfs_word_t drEmbedSigWord; /* embedded volume signature */
hfs_lword_t drEmbedExtent; /* starting block number (xdrStABN)
and number of allocation blocks
(xdrNumABlks) occupied by embedded
volume */
hfs_lword_t drXTFlSize; /* bytes in the extents B-tree */
hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */
hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */
hfs_byte_t drCTExtRec[12]; /* catalog B-tree's first 3 extents */
} __attribute__((packed));
/*================ Global functions ================*/
/*
* hfs_mdb_get()
*
* Build the in-core MDB for a filesystem, including
* the B-trees and the volume bitmap.
*/
struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
hfs_s32 part_start)
{
struct hfs_mdb *mdb;
hfs_buffer buf;
struct raw_mdb *raw;
unsigned int bs, block;
int lcv, limit;
hfs_buffer *bmbuf;
if (!HFS_NEW(mdb)) {
hfs_warn("hfs_fs: out of memory\n");
return NULL;
}
memset(mdb, 0, sizeof(*mdb));
mdb->magic = HFS_MDB_MAGIC;
mdb->sys_mdb = sys_mdb;
INIT_LIST_HEAD(&mdb->entry_dirty);
init_MUTEX(&mdb->bitmap_sem);
/* See if this is an HFS filesystem */
buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
if (!hfs_buffer_ok(buf)) {
hfs_warn("hfs_fs: Unable to read superblock\n");
HFS_DELETE(mdb);
goto bail2;
}
raw = (struct raw_mdb *)hfs_buffer_data(buf);
if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) {
hfs_buffer_put(buf);
HFS_DELETE(mdb);
goto bail2;
}
mdb->buf = buf;
bs = hfs_get_hl(raw->drAlBlkSiz);
if (!bs || (bs & (HFS_SECTOR_SIZE-1))) {
hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs);
hfs_buffer_put(buf);
HFS_DELETE(mdb);
goto bail2;
}
mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS;
/* These parameters are read from the MDB, and never written */
mdb->create_date = hfs_get_hl(raw->drCrDate);
mdb->fs_ablocks = hfs_get_hs(raw->drNmAlBlks);
mdb->fs_start = hfs_get_hs(raw->drAlBlSt) + part_start;
mdb->backup_date = hfs_get_hl(raw->drVolBkUp);
mdb->clumpablks = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz)
>> HFS_SECTOR_SIZE_BITS;
memcpy(mdb->vname, raw->drVN, sizeof(raw->drVN));
/* These parameters are read from and written to the MDB */
mdb->modify_date = hfs_get_nl(raw->drLsMod);
mdb->attrib = hfs_get_ns(raw->drAtrb);
mdb->free_ablocks = hfs_get_hs(raw->drFreeBks);
mdb->next_id = hfs_get_hl(raw->drNxtCNID);
mdb->write_count = hfs_get_hl(raw->drWrCnt);
mdb->root_files = hfs_get_hs(raw->drNmFls);
mdb->root_dirs = hfs_get_hs(raw->drNmRtDirs);
mdb->file_count = hfs_get_hl(raw->drFilCnt);
mdb->dir_count = hfs_get_hl(raw->drDirCnt);
/* TRY to get the alternate (backup) MDB. */
lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz;
limit = lcv + mdb->alloc_blksz;
for (; lcv < limit; ++lcv) {
buf = hfs_buffer_get(sys_mdb, lcv, 1);
if (hfs_buffer_ok(buf)) {
struct raw_mdb *tmp =
(struct raw_mdb *)hfs_buffer_data(buf);
if (hfs_get_ns(tmp->drSigWord) ==
htons(HFS_SUPER_MAGIC)) {
mdb->alt_buf = buf;
break;
}
}
hfs_buffer_put(buf);
}
if (mdb->alt_buf == NULL) {
hfs_warn("hfs_fs: unable to locate alternate MDB\n");
hfs_warn("hfs_fs: continuing without an alternate MDB\n");
}
/* read in the bitmap */
block = hfs_get_hs(raw->drVBMSt) + part_start;
bmbuf = mdb->bitmap;
lcv = (mdb->fs_ablocks + 4095) / 4096;
for ( ; lcv; --lcv, ++bmbuf, ++block) {
if (!hfs_buffer_ok(*bmbuf =
hfs_buffer_get(sys_mdb, block, 1))) {
hfs_warn("hfs_fs: unable to read volume bitmap\n");
goto bail1;
}
}
if (!(mdb->ext_tree = hfs_btree_init(mdb, htonl(HFS_EXT_CNID),
raw->drXTExtRec,
hfs_get_hl(raw->drXTFlSize),
hfs_get_hl(raw->drXTClpSiz))) ||
!(mdb->cat_tree = hfs_btree_init(mdb, htonl(HFS_CAT_CNID),
raw->drCTExtRec,
hfs_get_hl(raw->drCTFlSize),
hfs_get_hl(raw->drCTClpSiz)))) {
hfs_warn("hfs_fs: unable to initialize data structures\n");
goto bail1;
}
if (!(mdb->attrib & htons(HFS_SB_ATTRIB_CLEAN))) {
hfs_warn("hfs_fs: WARNING: mounting unclean filesystem.\n");
} else if (!readonly) {
/* Mark the volume uncleanly unmounted in case we crash */
hfs_put_ns(mdb->attrib & htons(~HFS_SB_ATTRIB_CLEAN),
raw->drAtrb);
hfs_buffer_dirty(mdb->buf);
hfs_buffer_sync(mdb->buf);
}
return mdb;
bail1:
hfs_mdb_put(mdb, readonly);
bail2:
return NULL;
}
/*
* hfs_mdb_commit()
*
* Description:
* This updates the MDB on disk (look also at hfs_write_super()).
* It does not check, if the superblock has been modified, or
* if the filesystem has been mounted read-only. It is mainly
* called by hfs_write_super() and hfs_btree_extend().
* Input Variable(s):
* struct hfs_mdb *mdb: Pointer to the hfs MDB
* int backup;
* Output Variable(s):
* NONE
* Returns:
* void
* Preconditions:
* 'mdb' points to a "valid" (struct hfs_mdb).
* Postconditions:
* The HFS MDB and on disk will be updated, by copying the possibly
* modified fields from the in memory MDB (in native byte order) to
* the disk block buffer.
* If 'backup' is non-zero then the alternate MDB is also written
* and the function doesn't return until it is actually on disk.
*/
void hfs_mdb_commit(struct hfs_mdb *mdb, int backup)
{
struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf);
/* Commit catalog entries to buffers */
hfs_cat_commit(mdb);
/* Commit B-tree data to buffers */
hfs_btree_commit(mdb->cat_tree, raw->drCTExtRec, raw->drCTFlSize);
hfs_btree_commit(mdb->ext_tree, raw->drXTExtRec, raw->drXTFlSize);
/* Update write_count and modify_date */
++mdb->write_count;
mdb->modify_date = hfs_time();
/* These parameters may have been modified, so write them back */
hfs_put_nl(mdb->modify_date, raw->drLsMod);
hfs_put_hs(mdb->free_ablocks, raw->drFreeBks);
hfs_put_hl(mdb->next_id, raw->drNxtCNID);
hfs_put_hl(mdb->write_count, raw->drWrCnt);
hfs_put_hs(mdb->root_files, raw->drNmFls);
hfs_put_hs(mdb->root_dirs, raw->drNmRtDirs);
hfs_put_hl(mdb->file_count, raw->drFilCnt);
hfs_put_hl(mdb->dir_count, raw->drDirCnt);
/* write MDB to disk */
hfs_buffer_dirty(mdb->buf);
/* write the backup MDB, not returning until it is written.
* we only do this when either the catalog or extents overflow
* files grow. */
if (backup && hfs_buffer_ok(mdb->alt_buf)) {
struct raw_mdb *tmp = (struct raw_mdb *)
hfs_buffer_data(mdb->alt_buf);
if ((hfs_get_hl(tmp->drCTFlSize) <
hfs_get_hl(raw->drCTFlSize)) ||
(hfs_get_hl(tmp->drXTFlSize) <
hfs_get_hl(raw->drXTFlSize))) {
memcpy(hfs_buffer_data(mdb->alt_buf),
hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
hfs_buffer_dirty(mdb->alt_buf);
hfs_buffer_sync(mdb->alt_buf);
}
}
}
/*
* hfs_mdb_put()
*
* Release the resources associated with the in-core MDB. */
void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) {
int lcv;
/* invalidate cached catalog entries */
hfs_cat_invalidate(mdb);
/* free the B-trees */
hfs_btree_free(mdb->ext_tree);
hfs_btree_free(mdb->cat_tree);
/* free the volume bitmap */
for (lcv = 0; lcv < HFS_BM_MAXBLOCKS; ++lcv) {
hfs_buffer_put(mdb->bitmap[lcv]);
}
/* update volume attributes */
if (!readonly) {
struct raw_mdb *raw =
(struct raw_mdb *)hfs_buffer_data(mdb->buf);
hfs_put_ns(mdb->attrib, raw->drAtrb);
hfs_buffer_dirty(mdb->buf);
}
/* free the buffers holding the primary and alternate MDBs */
hfs_buffer_put(mdb->buf);
hfs_buffer_put(mdb->alt_buf);
/* free the MDB */
HFS_DELETE(mdb);
}