| /* |
| * 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); |
| } |