1 /*
2 * linux/fs/hfs/mdb.c
3 *
4 * Copyright (C) 1995-1997 Paul H. Hargrove
5 * This file may be distributed under the terms of the GNU General Public License.
6 *
7 * This file contains functions for reading/writing the MDB.
8 *
9 * "XXX" in a comment is a note to myself to consider changing something.
10 *
11 * In function preconditions the term "valid" applied to a pointer to
12 * a structure means that the pointer is non-NULL and the structure it
13 * points to has all fields initialized to consistent values.
14 *
15 * The code in this file initializes some structures which contain
16 * pointers by calling memset(&foo, 0, sizeof(foo)).
17 * This produces the desired behavior only due to the non-ANSI
18 * assumption that the machine representation of NULL is all zeros.
19 */
20
21 #include "hfs.h"
22
23 /*================ File-local data types ================*/
24
25 /*
26 * The HFS Master Directory Block (MDB).
27 *
28 * Also known as the Volume Information Block (VIB), this structure is
29 * the HFS equivalent of a superblock.
30 *
31 * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
32 *
33 * modified for HFS Extended
34 */
35 struct raw_mdb {
36 hfs_word_t drSigWord; /* Signature word indicating fs type */
37 hfs_lword_t drCrDate; /* fs creation date/time */
38 hfs_lword_t drLsMod; /* fs modification date/time */
39 hfs_word_t drAtrb; /* fs attributes */
40 hfs_word_t drNmFls; /* number of files in root directory */
41 hfs_word_t drVBMSt; /* location (in 512-byte blocks)
42 of the volume bitmap */
43 hfs_word_t drAllocPtr; /* location (in allocation blocks)
44 to begin next allocation search */
45 hfs_word_t drNmAlBlks; /* number of allocation blocks */
46 hfs_lword_t drAlBlkSiz; /* bytes in an allocation block */
47 hfs_lword_t drClpSiz; /* clumpsize, the number of bytes to
48 allocate when extending a file */
49 hfs_word_t drAlBlSt; /* location (in 512-byte blocks)
50 of the first allocation block */
51 hfs_lword_t drNxtCNID; /* CNID to assign to the next
52 file or directory created */
53 hfs_word_t drFreeBks; /* number of free allocation blocks */
54 hfs_byte_t drVN[28]; /* the volume label */
55 hfs_lword_t drVolBkUp; /* fs backup date/time */
56 hfs_word_t drVSeqNum; /* backup sequence number */
57 hfs_lword_t drWrCnt; /* fs write count */
58 hfs_lword_t drXTClpSiz; /* clumpsize for the extents B-tree */
59 hfs_lword_t drCTClpSiz; /* clumpsize for the catalog B-tree */
60 hfs_word_t drNmRtDirs; /* number of directories in
61 the root directory */
62 hfs_lword_t drFilCnt; /* number of files in the fs */
63 hfs_lword_t drDirCnt; /* number of directories in the fs */
64 hfs_byte_t drFndrInfo[32]; /* data used by the Finder */
65 hfs_word_t drEmbedSigWord; /* embedded volume signature */
66 hfs_lword_t drEmbedExtent; /* starting block number (xdrStABN)
67 and number of allocation blocks
68 (xdrNumABlks) occupied by embedded
69 volume */
70 hfs_lword_t drXTFlSize; /* bytes in the extents B-tree */
71 hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */
72 hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */
73 hfs_byte_t drCTExtRec[12]; /* catalog B-tree's first 3 extents */
74 } __attribute__((packed));
75
76 /*================ Global functions ================*/
77
78 /*
79 * hfs_mdb_get()
80 *
81 * Build the in-core MDB for a filesystem, including
82 * the B-trees and the volume bitmap.
83 */
hfs_mdb_get(hfs_sysmdb sys_mdb,int readonly,hfs_s32 part_start)84 struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
85 hfs_s32 part_start)
86 {
87 struct hfs_mdb *mdb;
88 hfs_buffer buf;
89 struct raw_mdb *raw;
90 unsigned int bs, block;
91 int lcv, limit;
92 hfs_buffer *bmbuf;
93
94 if (!HFS_NEW(mdb)) {
95 hfs_warn("hfs_fs: out of memory\n");
96 return NULL;
97 }
98
99 memset(mdb, 0, sizeof(*mdb));
100 mdb->magic = HFS_MDB_MAGIC;
101 mdb->sys_mdb = sys_mdb;
102 INIT_LIST_HEAD(&mdb->entry_dirty);
103 hfs_init_waitqueue(&mdb->rename_wait);
104 hfs_init_waitqueue(&mdb->bitmap_wait);
105
106 /* See if this is an HFS filesystem */
107 buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
108 if (!hfs_buffer_ok(buf)) {
109 hfs_warn("hfs_fs: Unable to read superblock\n");
110 HFS_DELETE(mdb);
111 goto bail2;
112 }
113
114 raw = (struct raw_mdb *)hfs_buffer_data(buf);
115 if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) {
116 hfs_buffer_put(buf);
117 HFS_DELETE(mdb);
118 goto bail2;
119 }
120 mdb->buf = buf;
121
122 bs = hfs_get_hl(raw->drAlBlkSiz);
123 if (!bs || (bs & (HFS_SECTOR_SIZE-1))) {
124 hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs);
125 hfs_buffer_put(buf);
126 HFS_DELETE(mdb);
127 goto bail2;
128 }
129 mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS;
130
131 /* These parameters are read from the MDB, and never written */
132 mdb->create_date = hfs_get_hl(raw->drCrDate);
133 mdb->fs_ablocks = hfs_get_hs(raw->drNmAlBlks);
134 mdb->fs_start = hfs_get_hs(raw->drAlBlSt) + part_start;
135 mdb->backup_date = hfs_get_hl(raw->drVolBkUp);
136 mdb->clumpablks = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz)
137 >> HFS_SECTOR_SIZE_BITS;
138 memcpy(mdb->vname, raw->drVN, sizeof(raw->drVN));
139
140 /* These parameters are read from and written to the MDB */
141 mdb->modify_date = hfs_get_nl(raw->drLsMod);
142 mdb->attrib = hfs_get_ns(raw->drAtrb);
143 mdb->free_ablocks = hfs_get_hs(raw->drFreeBks);
144 mdb->next_id = hfs_get_hl(raw->drNxtCNID);
145 mdb->write_count = hfs_get_hl(raw->drWrCnt);
146 mdb->root_files = hfs_get_hs(raw->drNmFls);
147 mdb->root_dirs = hfs_get_hs(raw->drNmRtDirs);
148 mdb->file_count = hfs_get_hl(raw->drFilCnt);
149 mdb->dir_count = hfs_get_hl(raw->drDirCnt);
150
151 /* TRY to get the alternate (backup) MDB. */
152 lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz;
153 limit = lcv + mdb->alloc_blksz;
154 for (; lcv < limit; ++lcv) {
155 buf = hfs_buffer_get(sys_mdb, lcv, 1);
156 if (hfs_buffer_ok(buf)) {
157 struct raw_mdb *tmp =
158 (struct raw_mdb *)hfs_buffer_data(buf);
159
160 if (hfs_get_ns(tmp->drSigWord) ==
161 htons(HFS_SUPER_MAGIC)) {
162 mdb->alt_buf = buf;
163 break;
164 }
165 }
166 hfs_buffer_put(buf);
167 }
168
169 if (mdb->alt_buf == NULL) {
170 hfs_warn("hfs_fs: unable to locate alternate MDB\n");
171 hfs_warn("hfs_fs: continuing without an alternate MDB\n");
172 }
173
174 /* read in the bitmap */
175 block = hfs_get_hs(raw->drVBMSt) + part_start;
176 bmbuf = mdb->bitmap;
177 lcv = (mdb->fs_ablocks + 4095) / 4096;
178 for ( ; lcv; --lcv, ++bmbuf, ++block) {
179 if (!hfs_buffer_ok(*bmbuf =
180 hfs_buffer_get(sys_mdb, block, 1))) {
181 hfs_warn("hfs_fs: unable to read volume bitmap\n");
182 goto bail1;
183 }
184 }
185
186 if (!(mdb->ext_tree = hfs_btree_init(mdb, htonl(HFS_EXT_CNID),
187 raw->drXTExtRec,
188 hfs_get_hl(raw->drXTFlSize),
189 hfs_get_hl(raw->drXTClpSiz))) ||
190 !(mdb->cat_tree = hfs_btree_init(mdb, htonl(HFS_CAT_CNID),
191 raw->drCTExtRec,
192 hfs_get_hl(raw->drCTFlSize),
193 hfs_get_hl(raw->drCTClpSiz)))) {
194 hfs_warn("hfs_fs: unable to initialize data structures\n");
195 goto bail1;
196 }
197
198 if (!(mdb->attrib & htons(HFS_SB_ATTRIB_CLEAN))) {
199 hfs_warn("hfs_fs: WARNING: mounting unclean filesystem.\n");
200 } else if (!readonly && !(mdb->attrib & (HFS_SB_ATTRIB_HLOCK | HFS_SB_ATTRIB_SLOCK))) {
201 /* Mark the volume uncleanly unmounted in case we crash */
202 hfs_put_ns(mdb->attrib & htons(~HFS_SB_ATTRIB_CLEAN),
203 raw->drAtrb);
204 hfs_buffer_dirty(mdb->buf);
205 hfs_buffer_sync(mdb->buf);
206 }
207
208 return mdb;
209
210 bail1:
211 hfs_mdb_put(mdb, readonly);
212 bail2:
213 return NULL;
214 }
215
216 /*
217 * hfs_mdb_commit()
218 *
219 * Description:
220 * This updates the MDB on disk (look also at hfs_write_super()).
221 * It does not check, if the superblock has been modified, or
222 * if the filesystem has been mounted read-only. It is mainly
223 * called by hfs_write_super() and hfs_btree_extend().
224 * Input Variable(s):
225 * struct hfs_mdb *mdb: Pointer to the hfs MDB
226 * int backup;
227 * Output Variable(s):
228 * NONE
229 * Returns:
230 * void
231 * Preconditions:
232 * 'mdb' points to a "valid" (struct hfs_mdb).
233 * Postconditions:
234 * The HFS MDB and on disk will be updated, by copying the possibly
235 * modified fields from the in memory MDB (in native byte order) to
236 * the disk block buffer.
237 * If 'backup' is non-zero then the alternate MDB is also written
238 * and the function doesn't return until it is actually on disk.
239 */
hfs_mdb_commit(struct hfs_mdb * mdb,int backup)240 void hfs_mdb_commit(struct hfs_mdb *mdb, int backup)
241 {
242 struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf);
243
244 /* Commit catalog entries to buffers */
245 hfs_cat_commit(mdb);
246
247 /* Commit B-tree data to buffers */
248 hfs_btree_commit(mdb->cat_tree, raw->drCTExtRec, raw->drCTFlSize);
249 hfs_btree_commit(mdb->ext_tree, raw->drXTExtRec, raw->drXTFlSize);
250
251 /* Update write_count and modify_date */
252 ++mdb->write_count;
253 mdb->modify_date = hfs_time();
254
255 /* These parameters may have been modified, so write them back */
256 hfs_put_nl(mdb->modify_date, raw->drLsMod);
257 hfs_put_hs(mdb->free_ablocks, raw->drFreeBks);
258 hfs_put_hl(mdb->next_id, raw->drNxtCNID);
259 hfs_put_hl(mdb->write_count, raw->drWrCnt);
260 hfs_put_hs(mdb->root_files, raw->drNmFls);
261 hfs_put_hs(mdb->root_dirs, raw->drNmRtDirs);
262 hfs_put_hl(mdb->file_count, raw->drFilCnt);
263 hfs_put_hl(mdb->dir_count, raw->drDirCnt);
264
265 /* write MDB to disk */
266 hfs_buffer_dirty(mdb->buf);
267
268 /* write the backup MDB, not returning until it is written.
269 * we only do this when either the catalog or extents overflow
270 * files grow. */
271 if (backup && hfs_buffer_ok(mdb->alt_buf)) {
272 struct raw_mdb *tmp = (struct raw_mdb *)
273 hfs_buffer_data(mdb->alt_buf);
274
275 if ((hfs_get_hl(tmp->drCTFlSize) <
276 hfs_get_hl(raw->drCTFlSize)) ||
277 (hfs_get_hl(tmp->drXTFlSize) <
278 hfs_get_hl(raw->drXTFlSize))) {
279 memcpy(hfs_buffer_data(mdb->alt_buf),
280 hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
281 hfs_buffer_dirty(mdb->alt_buf);
282 hfs_buffer_sync(mdb->alt_buf);
283 }
284 }
285 }
286
287 /*
288 * hfs_mdb_put()
289 *
290 * Release the resources associated with the in-core MDB. */
hfs_mdb_put(struct hfs_mdb * mdb,int readonly)291 void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) {
292 int lcv;
293
294 /* invalidate cached catalog entries */
295 hfs_cat_invalidate(mdb);
296
297 /* free the B-trees */
298 hfs_btree_free(mdb->ext_tree);
299 hfs_btree_free(mdb->cat_tree);
300
301 /* free the volume bitmap */
302 for (lcv = 0; lcv < HFS_BM_MAXBLOCKS; ++lcv) {
303 hfs_buffer_put(mdb->bitmap[lcv]);
304 }
305
306 /* update volume attributes */
307 if (!readonly) {
308 struct raw_mdb *raw =
309 (struct raw_mdb *)hfs_buffer_data(mdb->buf);
310 hfs_put_ns(mdb->attrib, raw->drAtrb);
311 hfs_buffer_dirty(mdb->buf);
312 }
313
314 /* free the buffers holding the primary and alternate MDBs */
315 hfs_buffer_put(mdb->buf);
316 hfs_buffer_put(mdb->alt_buf);
317
318 /* free the MDB */
319 HFS_DELETE(mdb);
320 }
321