18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  linux/fs/hfs/mdb.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 1995-1997  Paul H. Hargrove
58c2ecf20Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
68c2ecf20Sopenharmony_ci * This file may be distributed under the terms of the GNU General Public License.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file contains functions for reading/writing the MDB.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/cdrom.h>
128c2ecf20Sopenharmony_ci#include <linux/genhd.h>
138c2ecf20Sopenharmony_ci#include <linux/nls.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "hfs_fs.h"
178c2ecf20Sopenharmony_ci#include "btree.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*================ File-local data types ================*/
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * The HFS Master Directory Block (MDB).
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Also known as the Volume Information Block (VIB), this structure is
258c2ecf20Sopenharmony_ci * the HFS equivalent of a superblock.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * modified for HFS Extended
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int hfs_get_last_session(struct super_block *sb,
338c2ecf20Sopenharmony_ci				sector_t *start, sector_t *size)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* default values */
388c2ecf20Sopenharmony_ci	*start = 0;
398c2ecf20Sopenharmony_ci	*size = i_size_read(sb->s_bdev->bd_inode) >> 9;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (HFS_SB(sb)->session >= 0) {
428c2ecf20Sopenharmony_ci		struct cdrom_tocentry te;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci		if (!cdi)
458c2ecf20Sopenharmony_ci			return -EINVAL;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci		te.cdte_track = HFS_SB(sb)->session;
488c2ecf20Sopenharmony_ci		te.cdte_format = CDROM_LBA;
498c2ecf20Sopenharmony_ci		if (cdrom_read_tocentry(cdi, &te) ||
508c2ecf20Sopenharmony_ci		    (te.cdte_ctrl & CDROM_DATA_TRACK) != 4) {
518c2ecf20Sopenharmony_ci			pr_err("invalid session number or type of track\n");
528c2ecf20Sopenharmony_ci			return -EINVAL;
538c2ecf20Sopenharmony_ci		}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		*start = (sector_t)te.cdte_addr.lba << 2;
568c2ecf20Sopenharmony_ci	} else if (cdi) {
578c2ecf20Sopenharmony_ci		struct cdrom_multisession ms_info;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		ms_info.addr_format = CDROM_LBA;
608c2ecf20Sopenharmony_ci		if (cdrom_multisession(cdi, &ms_info) == 0 && ms_info.xa_flag)
618c2ecf20Sopenharmony_ci			*start = (sector_t)ms_info.addr.lba << 2;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*
688c2ecf20Sopenharmony_ci * hfs_mdb_get()
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci * Build the in-core MDB for a filesystem, including
718c2ecf20Sopenharmony_ci * the B-trees and the volume bitmap.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_ciint hfs_mdb_get(struct super_block *sb)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct buffer_head *bh;
768c2ecf20Sopenharmony_ci	struct hfs_mdb *mdb, *mdb2;
778c2ecf20Sopenharmony_ci	unsigned int block;
788c2ecf20Sopenharmony_ci	char *ptr;
798c2ecf20Sopenharmony_ci	int off2, len, size, sect;
808c2ecf20Sopenharmony_ci	sector_t part_start, part_size;
818c2ecf20Sopenharmony_ci	loff_t off;
828c2ecf20Sopenharmony_ci	__be16 attrib;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* set the device driver to 512-byte blocks */
858c2ecf20Sopenharmony_ci	size = sb_min_blocksize(sb, HFS_SECTOR_SIZE);
868c2ecf20Sopenharmony_ci	if (!size)
878c2ecf20Sopenharmony_ci		return -EINVAL;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (hfs_get_last_session(sb, &part_start, &part_size))
908c2ecf20Sopenharmony_ci		return -EINVAL;
918c2ecf20Sopenharmony_ci	while (1) {
928c2ecf20Sopenharmony_ci		/* See if this is an HFS filesystem */
938c2ecf20Sopenharmony_ci		bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
948c2ecf20Sopenharmony_ci		if (!bh)
958c2ecf20Sopenharmony_ci			goto out;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC))
988c2ecf20Sopenharmony_ci			break;
998c2ecf20Sopenharmony_ci		brelse(bh);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		/* check for a partition block
1028c2ecf20Sopenharmony_ci		 * (should do this only for cdrom/loop though)
1038c2ecf20Sopenharmony_ci		 */
1048c2ecf20Sopenharmony_ci		if (hfs_part_find(sb, &part_start, &part_size))
1058c2ecf20Sopenharmony_ci			goto out;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz);
1098c2ecf20Sopenharmony_ci	if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
1108c2ecf20Sopenharmony_ci		pr_err("bad allocation block size %d\n", size);
1118c2ecf20Sopenharmony_ci		goto out_bh;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE);
1158c2ecf20Sopenharmony_ci	/* size must be a multiple of 512 */
1168c2ecf20Sopenharmony_ci	while (size & (size - 1))
1178c2ecf20Sopenharmony_ci		size -= HFS_SECTOR_SIZE;
1188c2ecf20Sopenharmony_ci	sect = be16_to_cpu(mdb->drAlBlSt) + part_start;
1198c2ecf20Sopenharmony_ci	/* align block size to first sector */
1208c2ecf20Sopenharmony_ci	while (sect & ((size - 1) >> HFS_SECTOR_SIZE_BITS))
1218c2ecf20Sopenharmony_ci		size >>= 1;
1228c2ecf20Sopenharmony_ci	/* align block size to weird alloc size */
1238c2ecf20Sopenharmony_ci	while (HFS_SB(sb)->alloc_blksz & (size - 1))
1248c2ecf20Sopenharmony_ci		size >>= 1;
1258c2ecf20Sopenharmony_ci	brelse(bh);
1268c2ecf20Sopenharmony_ci	if (!sb_set_blocksize(sb, size)) {
1278c2ecf20Sopenharmony_ci		pr_err("unable to set blocksize to %u\n", size);
1288c2ecf20Sopenharmony_ci		goto out;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
1328c2ecf20Sopenharmony_ci	if (!bh)
1338c2ecf20Sopenharmony_ci		goto out;
1348c2ecf20Sopenharmony_ci	if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC))
1358c2ecf20Sopenharmony_ci		goto out_bh;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	HFS_SB(sb)->mdb_bh = bh;
1388c2ecf20Sopenharmony_ci	HFS_SB(sb)->mdb = mdb;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* These parameters are read from the MDB, and never written */
1418c2ecf20Sopenharmony_ci	HFS_SB(sb)->part_start = part_start;
1428c2ecf20Sopenharmony_ci	HFS_SB(sb)->fs_ablocks = be16_to_cpu(mdb->drNmAlBlks);
1438c2ecf20Sopenharmony_ci	HFS_SB(sb)->fs_div = HFS_SB(sb)->alloc_blksz >> sb->s_blocksize_bits;
1448c2ecf20Sopenharmony_ci	HFS_SB(sb)->clumpablks = be32_to_cpu(mdb->drClpSiz) /
1458c2ecf20Sopenharmony_ci				 HFS_SB(sb)->alloc_blksz;
1468c2ecf20Sopenharmony_ci	if (!HFS_SB(sb)->clumpablks)
1478c2ecf20Sopenharmony_ci		HFS_SB(sb)->clumpablks = 1;
1488c2ecf20Sopenharmony_ci	HFS_SB(sb)->fs_start = (be16_to_cpu(mdb->drAlBlSt) + part_start) >>
1498c2ecf20Sopenharmony_ci			       (sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* These parameters are read from and written to the MDB */
1528c2ecf20Sopenharmony_ci	HFS_SB(sb)->free_ablocks = be16_to_cpu(mdb->drFreeBks);
1538c2ecf20Sopenharmony_ci	HFS_SB(sb)->next_id = be32_to_cpu(mdb->drNxtCNID);
1548c2ecf20Sopenharmony_ci	HFS_SB(sb)->root_files = be16_to_cpu(mdb->drNmFls);
1558c2ecf20Sopenharmony_ci	HFS_SB(sb)->root_dirs = be16_to_cpu(mdb->drNmRtDirs);
1568c2ecf20Sopenharmony_ci	HFS_SB(sb)->file_count = be32_to_cpu(mdb->drFilCnt);
1578c2ecf20Sopenharmony_ci	HFS_SB(sb)->folder_count = be32_to_cpu(mdb->drDirCnt);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* TRY to get the alternate (backup) MDB. */
1608c2ecf20Sopenharmony_ci	sect = part_start + part_size - 2;
1618c2ecf20Sopenharmony_ci	bh = sb_bread512(sb, sect, mdb2);
1628c2ecf20Sopenharmony_ci	if (bh) {
1638c2ecf20Sopenharmony_ci		if (mdb2->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) {
1648c2ecf20Sopenharmony_ci			HFS_SB(sb)->alt_mdb_bh = bh;
1658c2ecf20Sopenharmony_ci			HFS_SB(sb)->alt_mdb = mdb2;
1668c2ecf20Sopenharmony_ci		} else
1678c2ecf20Sopenharmony_ci			brelse(bh);
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (!HFS_SB(sb)->alt_mdb) {
1718c2ecf20Sopenharmony_ci		pr_warn("unable to locate alternate MDB\n");
1728c2ecf20Sopenharmony_ci		pr_warn("continuing without an alternate MDB\n");
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	HFS_SB(sb)->bitmap = kmalloc(8192, GFP_KERNEL);
1768c2ecf20Sopenharmony_ci	if (!HFS_SB(sb)->bitmap)
1778c2ecf20Sopenharmony_ci		goto out;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* read in the bitmap */
1808c2ecf20Sopenharmony_ci	block = be16_to_cpu(mdb->drVBMSt) + part_start;
1818c2ecf20Sopenharmony_ci	off = (loff_t)block << HFS_SECTOR_SIZE_BITS;
1828c2ecf20Sopenharmony_ci	size = (HFS_SB(sb)->fs_ablocks + 8) / 8;
1838c2ecf20Sopenharmony_ci	ptr = (u8 *)HFS_SB(sb)->bitmap;
1848c2ecf20Sopenharmony_ci	while (size) {
1858c2ecf20Sopenharmony_ci		bh = sb_bread(sb, off >> sb->s_blocksize_bits);
1868c2ecf20Sopenharmony_ci		if (!bh) {
1878c2ecf20Sopenharmony_ci			pr_err("unable to read volume bitmap\n");
1888c2ecf20Sopenharmony_ci			goto out;
1898c2ecf20Sopenharmony_ci		}
1908c2ecf20Sopenharmony_ci		off2 = off & (sb->s_blocksize - 1);
1918c2ecf20Sopenharmony_ci		len = min((int)sb->s_blocksize - off2, size);
1928c2ecf20Sopenharmony_ci		memcpy(ptr, bh->b_data + off2, len);
1938c2ecf20Sopenharmony_ci		brelse(bh);
1948c2ecf20Sopenharmony_ci		ptr += len;
1958c2ecf20Sopenharmony_ci		off += len;
1968c2ecf20Sopenharmony_ci		size -= len;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp);
2008c2ecf20Sopenharmony_ci	if (!HFS_SB(sb)->ext_tree) {
2018c2ecf20Sopenharmony_ci		pr_err("unable to open extent tree\n");
2028c2ecf20Sopenharmony_ci		goto out;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci	HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
2058c2ecf20Sopenharmony_ci	if (!HFS_SB(sb)->cat_tree) {
2068c2ecf20Sopenharmony_ci		pr_err("unable to open catalog tree\n");
2078c2ecf20Sopenharmony_ci		goto out;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	attrib = mdb->drAtrb;
2118c2ecf20Sopenharmony_ci	if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) {
2128c2ecf20Sopenharmony_ci		pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended.  mounting read-only.\n");
2138c2ecf20Sopenharmony_ci		sb->s_flags |= SB_RDONLY;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) {
2168c2ecf20Sopenharmony_ci		pr_warn("filesystem is marked locked, mounting read-only.\n");
2178c2ecf20Sopenharmony_ci		sb->s_flags |= SB_RDONLY;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	if (!sb_rdonly(sb)) {
2208c2ecf20Sopenharmony_ci		/* Mark the volume uncleanly unmounted in case we crash */
2218c2ecf20Sopenharmony_ci		attrib &= cpu_to_be16(~HFS_SB_ATTRIB_UNMNT);
2228c2ecf20Sopenharmony_ci		attrib |= cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT);
2238c2ecf20Sopenharmony_ci		mdb->drAtrb = attrib;
2248c2ecf20Sopenharmony_ci		be32_add_cpu(&mdb->drWrCnt, 1);
2258c2ecf20Sopenharmony_ci		mdb->drLsMod = hfs_mtime();
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
2288c2ecf20Sopenharmony_ci		sync_dirty_buffer(HFS_SB(sb)->mdb_bh);
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ciout_bh:
2348c2ecf20Sopenharmony_ci	brelse(bh);
2358c2ecf20Sopenharmony_ciout:
2368c2ecf20Sopenharmony_ci	hfs_mdb_put(sb);
2378c2ecf20Sopenharmony_ci	return -EIO;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * hfs_mdb_commit()
2428c2ecf20Sopenharmony_ci *
2438c2ecf20Sopenharmony_ci * Description:
2448c2ecf20Sopenharmony_ci *   This updates the MDB on disk.
2458c2ecf20Sopenharmony_ci *   It does not check, if the superblock has been modified, or
2468c2ecf20Sopenharmony_ci *   if the filesystem has been mounted read-only. It is mainly
2478c2ecf20Sopenharmony_ci *   called by hfs_sync_fs() and flush_mdb().
2488c2ecf20Sopenharmony_ci * Input Variable(s):
2498c2ecf20Sopenharmony_ci *   struct hfs_mdb *mdb: Pointer to the hfs MDB
2508c2ecf20Sopenharmony_ci *   int backup;
2518c2ecf20Sopenharmony_ci * Output Variable(s):
2528c2ecf20Sopenharmony_ci *   NONE
2538c2ecf20Sopenharmony_ci * Returns:
2548c2ecf20Sopenharmony_ci *   void
2558c2ecf20Sopenharmony_ci * Preconditions:
2568c2ecf20Sopenharmony_ci *   'mdb' points to a "valid" (struct hfs_mdb).
2578c2ecf20Sopenharmony_ci * Postconditions:
2588c2ecf20Sopenharmony_ci *   The HFS MDB and on disk will be updated, by copying the possibly
2598c2ecf20Sopenharmony_ci *   modified fields from the in memory MDB (in native byte order) to
2608c2ecf20Sopenharmony_ci *   the disk block buffer.
2618c2ecf20Sopenharmony_ci *   If 'backup' is non-zero then the alternate MDB is also written
2628c2ecf20Sopenharmony_ci *   and the function doesn't return until it is actually on disk.
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_civoid hfs_mdb_commit(struct super_block *sb)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct hfs_mdb *mdb = HFS_SB(sb)->mdb;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (sb_rdonly(sb))
2698c2ecf20Sopenharmony_ci		return;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	lock_buffer(HFS_SB(sb)->mdb_bh);
2728c2ecf20Sopenharmony_ci	if (test_and_clear_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags)) {
2738c2ecf20Sopenharmony_ci		/* These parameters may have been modified, so write them back */
2748c2ecf20Sopenharmony_ci		mdb->drLsMod = hfs_mtime();
2758c2ecf20Sopenharmony_ci		mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks);
2768c2ecf20Sopenharmony_ci		mdb->drNxtCNID = cpu_to_be32(HFS_SB(sb)->next_id);
2778c2ecf20Sopenharmony_ci		mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files);
2788c2ecf20Sopenharmony_ci		mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs);
2798c2ecf20Sopenharmony_ci		mdb->drFilCnt = cpu_to_be32(HFS_SB(sb)->file_count);
2808c2ecf20Sopenharmony_ci		mdb->drDirCnt = cpu_to_be32(HFS_SB(sb)->folder_count);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		/* write MDB to disk */
2838c2ecf20Sopenharmony_ci		mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* write the backup MDB, not returning until it is written.
2878c2ecf20Sopenharmony_ci	 * we only do this when either the catalog or extents overflow
2888c2ecf20Sopenharmony_ci	 * files grow. */
2898c2ecf20Sopenharmony_ci	if (test_and_clear_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags) &&
2908c2ecf20Sopenharmony_ci	    HFS_SB(sb)->alt_mdb) {
2918c2ecf20Sopenharmony_ci		hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec,
2928c2ecf20Sopenharmony_ci				     &mdb->drXTFlSize, NULL);
2938c2ecf20Sopenharmony_ci		hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec,
2948c2ecf20Sopenharmony_ci				     &mdb->drCTFlSize, NULL);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		lock_buffer(HFS_SB(sb)->alt_mdb_bh);
2978c2ecf20Sopenharmony_ci		memcpy(HFS_SB(sb)->alt_mdb, HFS_SB(sb)->mdb, HFS_SECTOR_SIZE);
2988c2ecf20Sopenharmony_ci		HFS_SB(sb)->alt_mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT);
2998c2ecf20Sopenharmony_ci		HFS_SB(sb)->alt_mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT);
3008c2ecf20Sopenharmony_ci		unlock_buffer(HFS_SB(sb)->alt_mdb_bh);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh);
3038c2ecf20Sopenharmony_ci		sync_dirty_buffer(HFS_SB(sb)->alt_mdb_bh);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (test_and_clear_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags)) {
3078c2ecf20Sopenharmony_ci		struct buffer_head *bh;
3088c2ecf20Sopenharmony_ci		sector_t block;
3098c2ecf20Sopenharmony_ci		char *ptr;
3108c2ecf20Sopenharmony_ci		int off, size, len;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		block = be16_to_cpu(HFS_SB(sb)->mdb->drVBMSt) + HFS_SB(sb)->part_start;
3138c2ecf20Sopenharmony_ci		off = (block << HFS_SECTOR_SIZE_BITS) & (sb->s_blocksize - 1);
3148c2ecf20Sopenharmony_ci		block >>= sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS;
3158c2ecf20Sopenharmony_ci		size = (HFS_SB(sb)->fs_ablocks + 7) / 8;
3168c2ecf20Sopenharmony_ci		ptr = (u8 *)HFS_SB(sb)->bitmap;
3178c2ecf20Sopenharmony_ci		while (size) {
3188c2ecf20Sopenharmony_ci			bh = sb_bread(sb, block);
3198c2ecf20Sopenharmony_ci			if (!bh) {
3208c2ecf20Sopenharmony_ci				pr_err("unable to read volume bitmap\n");
3218c2ecf20Sopenharmony_ci				break;
3228c2ecf20Sopenharmony_ci			}
3238c2ecf20Sopenharmony_ci			len = min((int)sb->s_blocksize - off, size);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci			lock_buffer(bh);
3268c2ecf20Sopenharmony_ci			memcpy(bh->b_data + off, ptr, len);
3278c2ecf20Sopenharmony_ci			unlock_buffer(bh);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci			mark_buffer_dirty(bh);
3308c2ecf20Sopenharmony_ci			brelse(bh);
3318c2ecf20Sopenharmony_ci			block++;
3328c2ecf20Sopenharmony_ci			off = 0;
3338c2ecf20Sopenharmony_ci			ptr += len;
3348c2ecf20Sopenharmony_ci			size -= len;
3358c2ecf20Sopenharmony_ci		}
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	unlock_buffer(HFS_SB(sb)->mdb_bh);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_civoid hfs_mdb_close(struct super_block *sb)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	/* update volume attributes */
3438c2ecf20Sopenharmony_ci	if (sb_rdonly(sb))
3448c2ecf20Sopenharmony_ci		return;
3458c2ecf20Sopenharmony_ci	HFS_SB(sb)->mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT);
3468c2ecf20Sopenharmony_ci	HFS_SB(sb)->mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT);
3478c2ecf20Sopenharmony_ci	mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/*
3518c2ecf20Sopenharmony_ci * hfs_mdb_put()
3528c2ecf20Sopenharmony_ci *
3538c2ecf20Sopenharmony_ci * Release the resources associated with the in-core MDB.  */
3548c2ecf20Sopenharmony_civoid hfs_mdb_put(struct super_block *sb)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	if (!HFS_SB(sb))
3578c2ecf20Sopenharmony_ci		return;
3588c2ecf20Sopenharmony_ci	/* free the B-trees */
3598c2ecf20Sopenharmony_ci	hfs_btree_close(HFS_SB(sb)->ext_tree);
3608c2ecf20Sopenharmony_ci	hfs_btree_close(HFS_SB(sb)->cat_tree);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* free the buffers holding the primary and alternate MDBs */
3638c2ecf20Sopenharmony_ci	brelse(HFS_SB(sb)->mdb_bh);
3648c2ecf20Sopenharmony_ci	brelse(HFS_SB(sb)->alt_mdb_bh);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	unload_nls(HFS_SB(sb)->nls_io);
3678c2ecf20Sopenharmony_ci	unload_nls(HFS_SB(sb)->nls_disk);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	kfree(HFS_SB(sb)->bitmap);
3708c2ecf20Sopenharmony_ci	kfree(HFS_SB(sb));
3718c2ecf20Sopenharmony_ci	sb->s_fs_info = NULL;
3728c2ecf20Sopenharmony_ci}
373