diff options
Diffstat (limited to 'fs/hfs/mdb.c')
-rw-r--r-- | fs/hfs/mdb.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c new file mode 100644 index 000000000..45ad05022 --- /dev/null +++ b/fs/hfs/mdb.c @@ -0,0 +1,298 @@ +/* + * linux/fs/hfs/mdb.c + * + * Copyright (C) 1995-1997 Paul H. Hargrove + * This file may be distributed under the terms of the GNU 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 + */ +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 drVCSize; /* MacOS caching parameter */ + hfs_word_t drVCBMSize; /* MacOS caching parameter */ + hfs_word_t drCtlCSize; /* MacOS caching parameter */ + 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 */ +}; + +/*================ 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; + + /* 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"); + goto bail2; + } + raw = (struct raw_mdb *)hfs_buffer_data(buf); + if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) { + hfs_buffer_put(buf); + goto bail2; + } + mdb->buf = buf; + + bs = hfs_get_hl(raw->drAlBlkSiz); + if (!bs || bs > HFS_USHRT_MAX || (bs & (HFS_SECTOR_SIZE-1))) { + hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs); + goto bail1; + } + 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, 28); + + /* 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 */ + if (backup && hfs_buffer_ok(mdb->alt_buf)) { + 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); +} |