Blame view

fs/hfs/mdb.c 10.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   *  linux/fs/hfs/mdb.c
   *
   * Copyright (C) 1995-1997  Paul H. Hargrove
   * (C) 2003 Ardis Technologies <roman@ardistech.com>
   * This file may be distributed under the terms of the GNU General Public License.
   *
   * This file contains functions for reading/writing the MDB.
   */
  
  #include <linux/cdrom.h>
  #include <linux/genhd.h>
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
13
  #include <linux/nls.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  
  #include "hfs_fs.h"
  #include "btree.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
   */
  
  static int hfs_get_last_session(struct super_block *sb,
  				sector_t *start, sector_t *size)
  {
  	struct cdrom_multisession ms_info;
  	struct cdrom_tocentry te;
  	int res;
  
  	/* default values */
  	*start = 0;
  	*size = sb->s_bdev->bd_inode->i_size >> 9;
  
  	if (HFS_SB(sb)->session >= 0) {
  		te.cdte_track = HFS_SB(sb)->session;
  		te.cdte_format = CDROM_LBA;
  		res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te);
  		if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) {
  			*start = (sector_t)te.cdte_addr.lba << 2;
  			return 0;
  		}
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
51
52
  		printk(KERN_ERR "hfs: invalid session number or type of track
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  		return -EINVAL;
  	}
  	ms_info.addr_format = CDROM_LBA;
  	res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, (unsigned long)&ms_info);
  	if (!res && ms_info.xa_flag)
  		*start = (sector_t)ms_info.addr.lba << 2;
  	return 0;
  }
  
  /*
   * hfs_mdb_get()
   *
   * Build the in-core MDB for a filesystem, including
   * the B-trees and the volume bitmap.
   */
  int hfs_mdb_get(struct super_block *sb)
  {
  	struct buffer_head *bh;
  	struct hfs_mdb *mdb, *mdb2;
  	unsigned int block;
  	char *ptr;
  	int off2, len, size, sect;
  	sector_t part_start, part_size;
  	loff_t off;
  	__be16 attrib;
  
  	/* set the device driver to 512-byte blocks */
  	size = sb_min_blocksize(sb, HFS_SECTOR_SIZE);
  	if (!size)
  		return -EINVAL;
  
  	if (hfs_get_last_session(sb, &part_start, &part_size))
  		return -EINVAL;
  	while (1) {
  		/* See if this is an HFS filesystem */
  		bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
  		if (!bh)
  			goto out;
  
  		if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC))
  			break;
  		brelse(bh);
  
  		/* check for a partition block
  		 * (should do this only for cdrom/loop though)
  		 */
  		if (hfs_part_find(sb, &part_start, &part_size))
  			goto out;
  	}
  
  	HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz);
  	if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
105
106
  		printk(KERN_ERR "hfs: bad allocation block size %d
  ", size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  		goto out_bh;
  	}
  
  	size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE);
  	/* size must be a multiple of 512 */
  	while (size & (size - 1))
  		size -= HFS_SECTOR_SIZE;
  	sect = be16_to_cpu(mdb->drAlBlSt) + part_start;
  	/* align block size to first sector */
  	while (sect & ((size - 1) >> HFS_SECTOR_SIZE_BITS))
  		size >>= 1;
  	/* align block size to weird alloc size */
  	while (HFS_SB(sb)->alloc_blksz & (size - 1))
  		size >>= 1;
  	brelse(bh);
  	if (!sb_set_blocksize(sb, size)) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
123
124
  		printk(KERN_ERR "hfs: unable to set blocksize to %u
  ", size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  		goto out;
  	}
  
  	bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
  	if (!bh)
  		goto out;
  	if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC))
  		goto out_bh;
  
  	HFS_SB(sb)->mdb_bh = bh;
  	HFS_SB(sb)->mdb = mdb;
  
  	/* These parameters are read from the MDB, and never written */
  	HFS_SB(sb)->part_start = part_start;
  	HFS_SB(sb)->fs_ablocks = be16_to_cpu(mdb->drNmAlBlks);
  	HFS_SB(sb)->fs_div = HFS_SB(sb)->alloc_blksz >> sb->s_blocksize_bits;
  	HFS_SB(sb)->clumpablks = be32_to_cpu(mdb->drClpSiz) /
  				 HFS_SB(sb)->alloc_blksz;
  	if (!HFS_SB(sb)->clumpablks)
  		HFS_SB(sb)->clumpablks = 1;
  	HFS_SB(sb)->fs_start = (be16_to_cpu(mdb->drAlBlSt) + part_start) >>
  			       (sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS);
  
  	/* These parameters are read from and written to the MDB */
  	HFS_SB(sb)->free_ablocks = be16_to_cpu(mdb->drFreeBks);
  	HFS_SB(sb)->next_id = be32_to_cpu(mdb->drNxtCNID);
  	HFS_SB(sb)->root_files = be16_to_cpu(mdb->drNmFls);
  	HFS_SB(sb)->root_dirs = be16_to_cpu(mdb->drNmRtDirs);
  	HFS_SB(sb)->file_count = be32_to_cpu(mdb->drFilCnt);
  	HFS_SB(sb)->folder_count = be32_to_cpu(mdb->drDirCnt);
  
  	/* TRY to get the alternate (backup) MDB. */
  	sect = part_start + part_size - 2;
  	bh = sb_bread512(sb, sect, mdb2);
  	if (bh) {
  		if (mdb2->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) {
  			HFS_SB(sb)->alt_mdb_bh = bh;
  			HFS_SB(sb)->alt_mdb = mdb2;
  		} else
  			brelse(bh);
  	}
  
  	if (!HFS_SB(sb)->alt_mdb) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
168
169
170
171
  		printk(KERN_WARNING "hfs: unable to locate alternate MDB
  ");
  		printk(KERN_WARNING "hfs: continuing without an alternate MDB
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  	}
  
  	HFS_SB(sb)->bitmap = (__be32 *)__get_free_pages(GFP_KERNEL, PAGE_SIZE < 8192 ? 1 : 0);
  	if (!HFS_SB(sb)->bitmap)
  		goto out;
  
  	/* read in the bitmap */
  	block = be16_to_cpu(mdb->drVBMSt) + part_start;
  	off = (loff_t)block << HFS_SECTOR_SIZE_BITS;
  	size = (HFS_SB(sb)->fs_ablocks + 8) / 8;
  	ptr = (u8 *)HFS_SB(sb)->bitmap;
  	while (size) {
  		bh = sb_bread(sb, off >> sb->s_blocksize_bits);
  		if (!bh) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
186
187
  			printk(KERN_ERR "hfs: unable to read volume bitmap
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
195
196
197
198
199
200
  			goto out;
  		}
  		off2 = off & (sb->s_blocksize - 1);
  		len = min((int)sb->s_blocksize - off2, size);
  		memcpy(ptr, bh->b_data + off2, len);
  		brelse(bh);
  		ptr += len;
  		off += len;
  		size -= len;
  	}
  
  	HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp);
  	if (!HFS_SB(sb)->ext_tree) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
201
202
  		printk(KERN_ERR "hfs: unable to open extent tree
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
  		goto out;
  	}
  	HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
  	if (!HFS_SB(sb)->cat_tree) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
207
208
  		printk(KERN_ERR "hfs: unable to open catalog tree
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
213
  		goto out;
  	}
  
  	attrib = mdb->drAtrb;
  	if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
214
  		printk(KERN_WARNING "hfs: filesystem was not cleanly unmounted, "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
  			 "running fsck.hfs is recommended.  mounting read-only.
  ");
  		sb->s_flags |= MS_RDONLY;
  	}
  	if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
220
221
  		printk(KERN_WARNING "hfs: filesystem is marked locked, mounting read-only.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
227
228
  		sb->s_flags |= MS_RDONLY;
  	}
  	if (!(sb->s_flags & MS_RDONLY)) {
  		/* Mark the volume uncleanly unmounted in case we crash */
  		attrib &= cpu_to_be16(~HFS_SB_ATTRIB_UNMNT);
  		attrib |= cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT);
  		mdb->drAtrb = attrib;
20c79e785   Marcin Slusarz   hfs/hfsplus: be*_...
229
  		be32_add_cpu(&mdb->drWrCnt, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
  		mdb->drLsMod = hfs_mtime();
  
  		mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
3072b90c4   Christoph Hellwig   hfs: use sync_dir...
233
  		sync_dirty_buffer(HFS_SB(sb)->mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  	}
  
  	return 0;
  
  out_bh:
  	brelse(bh);
  out:
  	hfs_mdb_put(sb);
  	return -EIO;
  }
  
  /*
   * 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 super_block *sb)
  {
  	struct hfs_mdb *mdb = HFS_SB(sb)->mdb;
  
  	if (test_and_clear_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags)) {
  		/* These parameters may have been modified, so write them back */
  		mdb->drLsMod = hfs_mtime();
  		mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks);
  		mdb->drNxtCNID = cpu_to_be32(HFS_SB(sb)->next_id);
  		mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files);
  		mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs);
  		mdb->drFilCnt = cpu_to_be32(HFS_SB(sb)->file_count);
  		mdb->drDirCnt = cpu_to_be32(HFS_SB(sb)->folder_count);
  
  		/* write MDB to disk */
  		mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
  	}
  
  	/* write the backup MDB, not returning until it is written.
  	 * we only do this when either the catalog or extents overflow
  	 * files grow. */
  	if (test_and_clear_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags) &&
  	    HFS_SB(sb)->alt_mdb) {
  		hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec,
  				     &mdb->drXTFlSize, NULL);
  		hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec,
  				     &mdb->drCTFlSize, NULL);
  		memcpy(HFS_SB(sb)->alt_mdb, HFS_SB(sb)->mdb, HFS_SECTOR_SIZE);
  		HFS_SB(sb)->alt_mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT);
  		HFS_SB(sb)->alt_mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT);
  		mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh);
3072b90c4   Christoph Hellwig   hfs: use sync_dir...
300
  		sync_dirty_buffer(HFS_SB(sb)->alt_mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  	}
  
  	if (test_and_clear_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags)) {
  		struct buffer_head *bh;
  		sector_t block;
  		char *ptr;
  		int off, size, len;
  
  		block = be16_to_cpu(HFS_SB(sb)->mdb->drVBMSt) + HFS_SB(sb)->part_start;
  		off = (block << HFS_SECTOR_SIZE_BITS) & (sb->s_blocksize - 1);
  		block >>= sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS;
  		size = (HFS_SB(sb)->fs_ablocks + 7) / 8;
  		ptr = (u8 *)HFS_SB(sb)->bitmap;
  		while (size) {
  			bh = sb_bread(sb, block);
  			if (!bh) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
317
318
  				printk(KERN_ERR "hfs: unable to read volume bitmap
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  				break;
  			}
  			len = min((int)sb->s_blocksize - off, size);
  			memcpy(bh->b_data + off, ptr, len);
  			mark_buffer_dirty(bh);
  			brelse(bh);
  			block++;
  			off = 0;
  			ptr += len;
  			size -= len;
  		}
  	}
  }
  
  void hfs_mdb_close(struct super_block *sb)
  {
  	/* update volume attributes */
  	if (sb->s_flags & MS_RDONLY)
  		return;
  	HFS_SB(sb)->mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT);
  	HFS_SB(sb)->mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT);
  	mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
  }
  
  /*
   * hfs_mdb_put()
   *
   * Release the resources associated with the in-core MDB.  */
  void hfs_mdb_put(struct super_block *sb)
  {
945b09201   Colin Leroy   [PATCH] hfs, hfsp...
349
350
  	if (!HFS_SB(sb))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
356
357
  	/* free the B-trees */
  	hfs_btree_close(HFS_SB(sb)->ext_tree);
  	hfs_btree_close(HFS_SB(sb)->cat_tree);
  
  	/* free the buffers holding the primary and alternate MDBs */
  	brelse(HFS_SB(sb)->mdb_bh);
  	brelse(HFS_SB(sb)->alt_mdb_bh);
945b09201   Colin Leroy   [PATCH] hfs, hfsp...
358

6d729e44a   Thomas Gleixner   fs: Make unload_n...
359
360
  	unload_nls(HFS_SB(sb)->nls_io);
  	unload_nls(HFS_SB(sb)->nls_disk);
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
361

eb2e5f452   Dave Anderson   hfs: fix memory l...
362
  	free_pages((unsigned long)HFS_SB(sb)->bitmap, PAGE_SIZE < 8192 ? 1 : 0);
945b09201   Colin Leroy   [PATCH] hfs, hfsp...
363
364
  	kfree(HFS_SB(sb));
  	sb->s_fs_info = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  }