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