Blame view

fs/hfs/mdb.c 10.1 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;
  		}
d61426732   Joe Perches   hfs/hfsplus: conv...
51
52
  		pr_err("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))) {
d61426732   Joe Perches   hfs/hfsplus: conv...
105
106
  		pr_err("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)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
123
124
  		pr_err("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) {
d61426732   Joe Perches   hfs/hfsplus: conv...
168
169
170
171
  		pr_warn("unable to locate alternate MDB
  ");
  		pr_warn("continuing without an alternate MDB
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  	}
80f8dccf9   Al Viro   HFS wants 8Kb per...
173
  	HFS_SB(sb)->bitmap = kmalloc(8192, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
180
181
182
183
184
  	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) {
d61426732   Joe Perches   hfs/hfsplus: conv...
185
186
  			pr_err("unable to read volume bitmap
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
193
194
195
196
197
198
199
  			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) {
d61426732   Joe Perches   hfs/hfsplus: conv...
200
201
  		pr_err("unable to open extent tree
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
  		goto out;
  	}
  	HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
  	if (!HFS_SB(sb)->cat_tree) {
d61426732   Joe Perches   hfs/hfsplus: conv...
206
207
  		pr_err("unable to open catalog tree
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
  		goto out;
  	}
  
  	attrib = mdb->drAtrb;
  	if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) {
d61426732   Joe Perches   hfs/hfsplus: conv...
213
214
  		pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended.  mounting read-only.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
  		sb->s_flags |= MS_RDONLY;
  	}
  	if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) {
d61426732   Joe Perches   hfs/hfsplus: conv...
218
219
  		pr_warn("filesystem is marked locked, mounting read-only.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
  		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*_...
227
  		be32_add_cpu(&mdb->drWrCnt, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
  		mdb->drLsMod = hfs_mtime();
  
  		mark_buffer_dirty(HFS_SB(sb)->mdb_bh);
3072b90c4   Christoph Hellwig   hfs: use sync_dir...
231
  		sync_dirty_buffer(HFS_SB(sb)->mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  	}
  
  	return 0;
  
  out_bh:
  	brelse(bh);
  out:
  	hfs_mdb_put(sb);
  	return -EIO;
  }
  
  /*
   * hfs_mdb_commit()
   *
   * Description:
50640bcc0   Artem Bityutskiy   hfs: nuke write_s...
247
   *   This updates the MDB on disk.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
   *   It does not check, if the superblock has been modified, or
   *   if the filesystem has been mounted read-only. It is mainly
50640bcc0   Artem Bityutskiy   hfs: nuke write_s...
250
   *   called by hfs_sync_fs() and flush_mdb().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
   * 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;
4527440d5   Artem Bityutskiy   hfs: simplify a b...
270
271
  	if (sb->s_flags & MS_RDONLY)
  		return;
b59352359   Artem Bityutskiy   hfs: get rid of l...
272
  	lock_buffer(HFS_SB(sb)->mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  	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);
b59352359   Artem Bityutskiy   hfs: get rid of l...
296
297
  
  		lock_buffer(HFS_SB(sb)->alt_mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
  		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);
b59352359   Artem Bityutskiy   hfs: get rid of l...
301
  		unlock_buffer(HFS_SB(sb)->alt_mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  		mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh);
3072b90c4   Christoph Hellwig   hfs: use sync_dir...
303
  		sync_dirty_buffer(HFS_SB(sb)->alt_mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  	}
  
  	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) {
d61426732   Joe Perches   hfs/hfsplus: conv...
320
321
  				pr_err("unable to read volume bitmap
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
  				break;
  			}
  			len = min((int)sb->s_blocksize - off, size);
b59352359   Artem Bityutskiy   hfs: get rid of l...
325
326
  
  			lock_buffer(bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  			memcpy(bh->b_data + off, ptr, len);
b59352359   Artem Bityutskiy   hfs: get rid of l...
328
  			unlock_buffer(bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
333
334
335
336
  			mark_buffer_dirty(bh);
  			brelse(bh);
  			block++;
  			off = 0;
  			ptr += len;
  			size -= len;
  		}
  	}
b59352359   Artem Bityutskiy   hfs: get rid of l...
337
  	unlock_buffer(HFS_SB(sb)->mdb_bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
  }
  
  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...
356
357
  	if (!HFS_SB(sb))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
364
  	/* 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...
365

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

80f8dccf9   Al Viro   HFS wants 8Kb per...
369
  	kfree(HFS_SB(sb)->bitmap);
945b09201   Colin Leroy   [PATCH] hfs, hfsp...
370
371
  	kfree(HFS_SB(sb));
  	sb->s_fs_info = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  }