162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/fs/hfs/mdb.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 1995-1997  Paul H. Hargrove
562306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
662306a36Sopenharmony_ci * This file may be distributed under the terms of the GNU General Public License.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file contains functions for reading/writing the MDB.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/cdrom.h>
1262306a36Sopenharmony_ci#include <linux/blkdev.h>
1362306a36Sopenharmony_ci#include <linux/nls.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "hfs_fs.h"
1762306a36Sopenharmony_ci#include "btree.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*================ File-local data types ================*/
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * The HFS Master Directory Block (MDB).
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Also known as the Volume Information Block (VIB), this structure is
2562306a36Sopenharmony_ci * the HFS equivalent of a superblock.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * modified for HFS Extended
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int hfs_get_last_session(struct super_block *sb,
3362306a36Sopenharmony_ci				sector_t *start, sector_t *size)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* default values */
3862306a36Sopenharmony_ci	*start = 0;
3962306a36Sopenharmony_ci	*size = bdev_nr_sectors(sb->s_bdev);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (HFS_SB(sb)->session >= 0) {
4262306a36Sopenharmony_ci		struct cdrom_tocentry te;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		if (!cdi)
4562306a36Sopenharmony_ci			return -EINVAL;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		te.cdte_track = HFS_SB(sb)->session;
4862306a36Sopenharmony_ci		te.cdte_format = CDROM_LBA;
4962306a36Sopenharmony_ci		if (cdrom_read_tocentry(cdi, &te) ||
5062306a36Sopenharmony_ci		    (te.cdte_ctrl & CDROM_DATA_TRACK) != 4) {
5162306a36Sopenharmony_ci			pr_err("invalid session number or type of track\n");
5262306a36Sopenharmony_ci			return -EINVAL;
5362306a36Sopenharmony_ci		}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		*start = (sector_t)te.cdte_addr.lba << 2;
5662306a36Sopenharmony_ci	} else if (cdi) {
5762306a36Sopenharmony_ci		struct cdrom_multisession ms_info;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		ms_info.addr_format = CDROM_LBA;
6062306a36Sopenharmony_ci		if (cdrom_multisession(cdi, &ms_info) == 0 && ms_info.xa_flag)
6162306a36Sopenharmony_ci			*start = (sector_t)ms_info.addr.lba << 2;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * hfs_mdb_get()
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * Build the in-core MDB for a filesystem, including
7162306a36Sopenharmony_ci * the B-trees and the volume bitmap.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ciint hfs_mdb_get(struct super_block *sb)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct buffer_head *bh;
7662306a36Sopenharmony_ci	struct hfs_mdb *mdb, *mdb2;
7762306a36Sopenharmony_ci	unsigned int block;
7862306a36Sopenharmony_ci	char *ptr;
7962306a36Sopenharmony_ci	int off2, len, size, sect;
8062306a36Sopenharmony_ci	sector_t part_start, part_size;
8162306a36Sopenharmony_ci	loff_t off;
8262306a36Sopenharmony_ci	__be16 attrib;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* set the device driver to 512-byte blocks */
8562306a36Sopenharmony_ci	size = sb_min_blocksize(sb, HFS_SECTOR_SIZE);
8662306a36Sopenharmony_ci	if (!size)
8762306a36Sopenharmony_ci		return -EINVAL;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (hfs_get_last_session(sb, &part_start, &part_size))
9062306a36Sopenharmony_ci		return -EINVAL;
9162306a36Sopenharmony_ci	while (1) {
9262306a36Sopenharmony_ci		/* See if this is an HFS filesystem */
9362306a36Sopenharmony_ci		bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
9462306a36Sopenharmony_ci		if (!bh)
9562306a36Sopenharmony_ci			goto out;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC))
9862306a36Sopenharmony_ci			break;
9962306a36Sopenharmony_ci		brelse(bh);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		/* check for a partition block
10262306a36Sopenharmony_ci		 * (should do this only for cdrom/loop though)
10362306a36Sopenharmony_ci		 */
10462306a36Sopenharmony_ci		if (hfs_part_find(sb, &part_start, &part_size))
10562306a36Sopenharmony_ci			goto out;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz);
10962306a36Sopenharmony_ci	if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
11062306a36Sopenharmony_ci		pr_err("bad allocation block size %d\n", size);
11162306a36Sopenharmony_ci		goto out_bh;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE);
11562306a36Sopenharmony_ci	/* size must be a multiple of 512 */
11662306a36Sopenharmony_ci	while (size & (size - 1))
11762306a36Sopenharmony_ci		size -= HFS_SECTOR_SIZE;
11862306a36Sopenharmony_ci	sect = be16_to_cpu(mdb->drAlBlSt) + part_start;
11962306a36Sopenharmony_ci	/* align block size to first sector */
12062306a36Sopenharmony_ci	while (sect & ((size - 1) >> HFS_SECTOR_SIZE_BITS))
12162306a36Sopenharmony_ci		size >>= 1;
12262306a36Sopenharmony_ci	/* align block size to weird alloc size */
12362306a36Sopenharmony_ci	while (HFS_SB(sb)->alloc_blksz & (size - 1))
12462306a36Sopenharmony_ci		size >>= 1;
12562306a36Sopenharmony_ci	brelse(bh);
12662306a36Sopenharmony_ci	if (!sb_set_blocksize(sb, size)) {
12762306a36Sopenharmony_ci		pr_err("unable to set blocksize to %u\n", size);
12862306a36Sopenharmony_ci		goto out;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
13262306a36Sopenharmony_ci	if (!bh)
13362306a36Sopenharmony_ci		goto out;
13462306a36Sopenharmony_ci	if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC))
13562306a36Sopenharmony_ci		goto out_bh;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	HFS_SB(sb)->mdb_bh = bh;
13862306a36Sopenharmony_ci	HFS_SB(sb)->mdb = mdb;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* These parameters are read from the MDB, and never written */
14162306a36Sopenharmony_ci	HFS_SB(sb)->part_start = part_start;
14262306a36Sopenharmony_ci	HFS_SB(sb)->fs_ablocks = be16_to_cpu(mdb->drNmAlBlks);
14362306a36Sopenharmony_ci	HFS_SB(sb)->fs_div = HFS_SB(sb)->alloc_blksz >> sb->s_blocksize_bits;
14462306a36Sopenharmony_ci	HFS_SB(sb)->clumpablks = be32_to_cpu(mdb->drClpSiz) /
14562306a36Sopenharmony_ci				 HFS_SB(sb)->alloc_blksz;
14662306a36Sopenharmony_ci	if (!HFS_SB(sb)->clumpablks)
14762306a36Sopenharmony_ci		HFS_SB(sb)->clumpablks = 1;
14862306a36Sopenharmony_ci	HFS_SB(sb)->fs_start = (be16_to_cpu(mdb->drAlBlSt) + part_start) >>
14962306a36Sopenharmony_ci			       (sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* These parameters are read from and written to the MDB */
15262306a36Sopenharmony_ci	HFS_SB(sb)->free_ablocks = be16_to_cpu(mdb->drFreeBks);
15362306a36Sopenharmony_ci	HFS_SB(sb)->next_id = be32_to_cpu(mdb->drNxtCNID);
15462306a36Sopenharmony_ci	HFS_SB(sb)->root_files = be16_to_cpu(mdb->drNmFls);
15562306a36Sopenharmony_ci	HFS_SB(sb)->root_dirs = be16_to_cpu(mdb->drNmRtDirs);
15662306a36Sopenharmony_ci	HFS_SB(sb)->file_count = be32_to_cpu(mdb->drFilCnt);
15762306a36Sopenharmony_ci	HFS_SB(sb)->folder_count = be32_to_cpu(mdb->drDirCnt);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* TRY to get the alternate (backup) MDB. */
16062306a36Sopenharmony_ci	sect = part_start + part_size - 2;
16162306a36Sopenharmony_ci	bh = sb_bread512(sb, sect, mdb2);
16262306a36Sopenharmony_ci	if (bh) {
16362306a36Sopenharmony_ci		if (mdb2->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) {
16462306a36Sopenharmony_ci			HFS_SB(sb)->alt_mdb_bh = bh;
16562306a36Sopenharmony_ci			HFS_SB(sb)->alt_mdb = mdb2;
16662306a36Sopenharmony_ci		} else
16762306a36Sopenharmony_ci			brelse(bh);
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!HFS_SB(sb)->alt_mdb) {
17162306a36Sopenharmony_ci		pr_warn("unable to locate alternate MDB\n");
17262306a36Sopenharmony_ci		pr_warn("continuing without an alternate MDB\n");
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	HFS_SB(sb)->bitmap = kmalloc(8192, GFP_KERNEL);
17662306a36Sopenharmony_ci	if (!HFS_SB(sb)->bitmap)
17762306a36Sopenharmony_ci		goto out;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* read in the bitmap */
18062306a36Sopenharmony_ci	block = be16_to_cpu(mdb->drVBMSt) + part_start;
18162306a36Sopenharmony_ci	off = (loff_t)block << HFS_SECTOR_SIZE_BITS;
18262306a36Sopenharmony_ci	size = (HFS_SB(sb)->fs_ablocks + 8) / 8;
18362306a36Sopenharmony_ci	ptr = (u8 *)HFS_SB(sb)->bitmap;
18462306a36Sopenharmony_ci	while (size) {
18562306a36Sopenharmony_ci		bh = sb_bread(sb, off >> sb->s_blocksize_bits);
18662306a36Sopenharmony_ci		if (!bh) {
18762306a36Sopenharmony_ci			pr_err("unable to read volume bitmap\n");
18862306a36Sopenharmony_ci			goto out;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci		off2 = off & (sb->s_blocksize - 1);
19162306a36Sopenharmony_ci		len = min((int)sb->s_blocksize - off2, size);
19262306a36Sopenharmony_ci		memcpy(ptr, bh->b_data + off2, len);
19362306a36Sopenharmony_ci		brelse(bh);
19462306a36Sopenharmony_ci		ptr += len;
19562306a36Sopenharmony_ci		off += len;
19662306a36Sopenharmony_ci		size -= len;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp);
20062306a36Sopenharmony_ci	if (!HFS_SB(sb)->ext_tree) {
20162306a36Sopenharmony_ci		pr_err("unable to open extent tree\n");
20262306a36Sopenharmony_ci		goto out;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
20562306a36Sopenharmony_ci	if (!HFS_SB(sb)->cat_tree) {
20662306a36Sopenharmony_ci		pr_err("unable to open catalog tree\n");
20762306a36Sopenharmony_ci		goto out;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	attrib = mdb->drAtrb;
21162306a36Sopenharmony_ci	if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) {
21262306a36Sopenharmony_ci		pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended.  mounting read-only.\n");
21362306a36Sopenharmony_ci		sb->s_flags |= SB_RDONLY;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) {
21662306a36Sopenharmony_ci		pr_warn("filesystem is marked locked, mounting read-only.\n");
21762306a36Sopenharmony_ci		sb->s_flags |= SB_RDONLY;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	if (!sb_rdonly(sb)) {
22062306a36Sopenharmony_ci		/* Mark the volume uncleanly unmounted in case we crash */
22162306a36Sopenharmony_ci		attrib &= cpu_to_be16(~HFS_SB_ATTRIB_UNMNT);
22262306a36Sopenharmony_ci		attrib |= cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT);
22362306a36Sopenharmony_ci		mdb->drAtrb = attrib;
22462306a36Sopenharmony_ci		be32_add_cpu(&mdb->drWrCnt, 1);
22562306a36Sopenharmony_ci		mdb->drLsMod = hfs_mtime();
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
22862306a36Sopenharmony_ci		sync_dirty_buffer(HFS_SB(sb)->mdb_bh);
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciout_bh:
23462306a36Sopenharmony_ci	brelse(bh);
23562306a36Sopenharmony_ciout:
23662306a36Sopenharmony_ci	hfs_mdb_put(sb);
23762306a36Sopenharmony_ci	return -EIO;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * hfs_mdb_commit()
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci * Description:
24462306a36Sopenharmony_ci *   This updates the MDB on disk.
24562306a36Sopenharmony_ci *   It does not check, if the superblock has been modified, or
24662306a36Sopenharmony_ci *   if the filesystem has been mounted read-only. It is mainly
24762306a36Sopenharmony_ci *   called by hfs_sync_fs() and flush_mdb().
24862306a36Sopenharmony_ci * Input Variable(s):
24962306a36Sopenharmony_ci *   struct hfs_mdb *mdb: Pointer to the hfs MDB
25062306a36Sopenharmony_ci *   int backup;
25162306a36Sopenharmony_ci * Output Variable(s):
25262306a36Sopenharmony_ci *   NONE
25362306a36Sopenharmony_ci * Returns:
25462306a36Sopenharmony_ci *   void
25562306a36Sopenharmony_ci * Preconditions:
25662306a36Sopenharmony_ci *   'mdb' points to a "valid" (struct hfs_mdb).
25762306a36Sopenharmony_ci * Postconditions:
25862306a36Sopenharmony_ci *   The HFS MDB and on disk will be updated, by copying the possibly
25962306a36Sopenharmony_ci *   modified fields from the in memory MDB (in native byte order) to
26062306a36Sopenharmony_ci *   the disk block buffer.
26162306a36Sopenharmony_ci *   If 'backup' is non-zero then the alternate MDB is also written
26262306a36Sopenharmony_ci *   and the function doesn't return until it is actually on disk.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_civoid hfs_mdb_commit(struct super_block *sb)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct hfs_mdb *mdb = HFS_SB(sb)->mdb;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (sb_rdonly(sb))
26962306a36Sopenharmony_ci		return;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	lock_buffer(HFS_SB(sb)->mdb_bh);
27262306a36Sopenharmony_ci	if (test_and_clear_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags)) {
27362306a36Sopenharmony_ci		/* These parameters may have been modified, so write them back */
27462306a36Sopenharmony_ci		mdb->drLsMod = hfs_mtime();
27562306a36Sopenharmony_ci		mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks);
27662306a36Sopenharmony_ci		mdb->drNxtCNID = cpu_to_be32(HFS_SB(sb)->next_id);
27762306a36Sopenharmony_ci		mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files);
27862306a36Sopenharmony_ci		mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs);
27962306a36Sopenharmony_ci		mdb->drFilCnt = cpu_to_be32(HFS_SB(sb)->file_count);
28062306a36Sopenharmony_ci		mdb->drDirCnt = cpu_to_be32(HFS_SB(sb)->folder_count);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		/* write MDB to disk */
28362306a36Sopenharmony_ci		mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* write the backup MDB, not returning until it is written.
28762306a36Sopenharmony_ci	 * we only do this when either the catalog or extents overflow
28862306a36Sopenharmony_ci	 * files grow. */
28962306a36Sopenharmony_ci	if (test_and_clear_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags) &&
29062306a36Sopenharmony_ci	    HFS_SB(sb)->alt_mdb) {
29162306a36Sopenharmony_ci		hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec,
29262306a36Sopenharmony_ci				     &mdb->drXTFlSize, NULL);
29362306a36Sopenharmony_ci		hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec,
29462306a36Sopenharmony_ci				     &mdb->drCTFlSize, NULL);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		lock_buffer(HFS_SB(sb)->alt_mdb_bh);
29762306a36Sopenharmony_ci		memcpy(HFS_SB(sb)->alt_mdb, HFS_SB(sb)->mdb, HFS_SECTOR_SIZE);
29862306a36Sopenharmony_ci		HFS_SB(sb)->alt_mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT);
29962306a36Sopenharmony_ci		HFS_SB(sb)->alt_mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT);
30062306a36Sopenharmony_ci		unlock_buffer(HFS_SB(sb)->alt_mdb_bh);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh);
30362306a36Sopenharmony_ci		sync_dirty_buffer(HFS_SB(sb)->alt_mdb_bh);
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (test_and_clear_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags)) {
30762306a36Sopenharmony_ci		struct buffer_head *bh;
30862306a36Sopenharmony_ci		sector_t block;
30962306a36Sopenharmony_ci		char *ptr;
31062306a36Sopenharmony_ci		int off, size, len;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		block = be16_to_cpu(HFS_SB(sb)->mdb->drVBMSt) + HFS_SB(sb)->part_start;
31362306a36Sopenharmony_ci		off = (block << HFS_SECTOR_SIZE_BITS) & (sb->s_blocksize - 1);
31462306a36Sopenharmony_ci		block >>= sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS;
31562306a36Sopenharmony_ci		size = (HFS_SB(sb)->fs_ablocks + 7) / 8;
31662306a36Sopenharmony_ci		ptr = (u8 *)HFS_SB(sb)->bitmap;
31762306a36Sopenharmony_ci		while (size) {
31862306a36Sopenharmony_ci			bh = sb_bread(sb, block);
31962306a36Sopenharmony_ci			if (!bh) {
32062306a36Sopenharmony_ci				pr_err("unable to read volume bitmap\n");
32162306a36Sopenharmony_ci				break;
32262306a36Sopenharmony_ci			}
32362306a36Sopenharmony_ci			len = min((int)sb->s_blocksize - off, size);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci			lock_buffer(bh);
32662306a36Sopenharmony_ci			memcpy(bh->b_data + off, ptr, len);
32762306a36Sopenharmony_ci			unlock_buffer(bh);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci			mark_buffer_dirty(bh);
33062306a36Sopenharmony_ci			brelse(bh);
33162306a36Sopenharmony_ci			block++;
33262306a36Sopenharmony_ci			off = 0;
33362306a36Sopenharmony_ci			ptr += len;
33462306a36Sopenharmony_ci			size -= len;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	unlock_buffer(HFS_SB(sb)->mdb_bh);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_civoid hfs_mdb_close(struct super_block *sb)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	/* update volume attributes */
34362306a36Sopenharmony_ci	if (sb_rdonly(sb))
34462306a36Sopenharmony_ci		return;
34562306a36Sopenharmony_ci	HFS_SB(sb)->mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT);
34662306a36Sopenharmony_ci	HFS_SB(sb)->mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT);
34762306a36Sopenharmony_ci	mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/*
35162306a36Sopenharmony_ci * hfs_mdb_put()
35262306a36Sopenharmony_ci *
35362306a36Sopenharmony_ci * Release the resources associated with the in-core MDB.  */
35462306a36Sopenharmony_civoid hfs_mdb_put(struct super_block *sb)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	if (!HFS_SB(sb))
35762306a36Sopenharmony_ci		return;
35862306a36Sopenharmony_ci	/* free the B-trees */
35962306a36Sopenharmony_ci	hfs_btree_close(HFS_SB(sb)->ext_tree);
36062306a36Sopenharmony_ci	hfs_btree_close(HFS_SB(sb)->cat_tree);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* free the buffers holding the primary and alternate MDBs */
36362306a36Sopenharmony_ci	brelse(HFS_SB(sb)->mdb_bh);
36462306a36Sopenharmony_ci	brelse(HFS_SB(sb)->alt_mdb_bh);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	unload_nls(HFS_SB(sb)->nls_io);
36762306a36Sopenharmony_ci	unload_nls(HFS_SB(sb)->nls_disk);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	kfree(HFS_SB(sb)->bitmap);
37062306a36Sopenharmony_ci	kfree(HFS_SB(sb));
37162306a36Sopenharmony_ci	sb->s_fs_info = NULL;
37262306a36Sopenharmony_ci}
373