]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* @(#)hfs_vfsutils.c 4.0 | |
23 | * | |
24 | * (c) 1997-2002 Apple Computer, Inc. All Rights Reserved | |
25 | * | |
26 | * hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS. | |
27 | * | |
28 | */ | |
29 | #include <sys/param.h> | |
30 | #include <sys/systm.h> | |
31 | #include <sys/kernel.h> | |
32 | #include <sys/malloc.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/mount.h> | |
35 | #include <sys/buf.h> | |
36 | #include <sys/ubc.h> | |
37 | #include <sys/unistd.h> | |
38 | #include <sys/utfconv.h> | |
39 | #include <sys/kauth.h> | |
40 | ||
41 | #include "hfs.h" | |
42 | #include "hfs_catalog.h" | |
43 | #include "hfs_dbg.h" | |
44 | #include "hfs_mount.h" | |
45 | #include "hfs_endian.h" | |
46 | #include "hfs_cnode.h" | |
47 | ||
48 | #include "hfscommon/headers/FileMgrInternal.h" | |
49 | #include "hfscommon/headers/BTreesInternal.h" | |
50 | #include "hfscommon/headers/HFSUnicodeWrappers.h" | |
51 | ||
52 | ||
53 | extern int count_lock_queue(void); | |
54 | ||
55 | ||
56 | static void ReleaseMetaFileVNode(struct vnode *vp); | |
57 | static int hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args); | |
58 | ||
59 | static void hfs_metadatazone_init(struct hfsmount *); | |
60 | static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *); | |
61 | ||
62 | ||
63 | ||
64 | u_int32_t GetLogicalBlockSize(struct vnode *vp); | |
65 | ||
66 | extern int hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey); | |
67 | ||
68 | ||
69 | //******************************************************************************* | |
70 | // Note: Finder information in the HFS/HFS+ metadata are considered opaque and | |
71 | // hence are not in the right byte order on little endian machines. It is | |
72 | // the responsibility of the finder and other clients to swap the data. | |
73 | //******************************************************************************* | |
74 | ||
75 | //******************************************************************************* | |
76 | // Routine: hfs_MountHFSVolume | |
77 | // | |
78 | // | |
79 | //******************************************************************************* | |
80 | char hfs_catname[] = "Catalog B-tree"; | |
81 | char hfs_extname[] = "Extents B-tree"; | |
82 | char hfs_vbmname[] = "Volume Bitmap"; | |
83 | char hfs_attrname[] = "Attribute B-tree"; | |
84 | ||
85 | char hfs_privdirname[] = | |
86 | "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data"; | |
87 | ||
88 | __private_extern__ | |
89 | OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb, | |
90 | struct proc *p) | |
91 | { | |
92 | ExtendedVCB *vcb = HFSTOVCB(hfsmp); | |
93 | int error; | |
94 | ByteCount utf8chars; | |
95 | struct cat_desc cndesc; | |
96 | struct cat_attr cnattr; | |
97 | struct cat_fork fork; | |
98 | ||
99 | /* Block size must be a multiple of 512 */ | |
100 | if (SWAP_BE32(mdb->drAlBlkSiz) == 0 || | |
101 | (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0) | |
102 | return (EINVAL); | |
103 | ||
104 | /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */ | |
105 | if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) && | |
106 | ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) { | |
107 | return (EINVAL); | |
108 | } | |
109 | hfsmp->hfs_flags |= HFS_STANDARD; | |
110 | /* | |
111 | * The MDB seems OK: transfer info from it into VCB | |
112 | * Note - the VCB starts out clear (all zeros) | |
113 | * | |
114 | */ | |
115 | vcb->vcbSigWord = SWAP_BE16 (mdb->drSigWord); | |
116 | vcb->vcbCrDate = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate))); | |
117 | vcb->localCreateDate = SWAP_BE32 (mdb->drCrDate); | |
118 | vcb->vcbLsMod = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod))); | |
119 | vcb->vcbAtrb = SWAP_BE16 (mdb->drAtrb); | |
120 | vcb->vcbNmFls = SWAP_BE16 (mdb->drNmFls); | |
121 | vcb->vcbVBMSt = SWAP_BE16 (mdb->drVBMSt); | |
122 | vcb->nextAllocation = SWAP_BE16 (mdb->drAllocPtr); | |
123 | vcb->totalBlocks = SWAP_BE16 (mdb->drNmAlBlks); | |
124 | vcb->blockSize = SWAP_BE32 (mdb->drAlBlkSiz); | |
125 | vcb->vcbClpSiz = SWAP_BE32 (mdb->drClpSiz); | |
126 | vcb->vcbAlBlSt = SWAP_BE16 (mdb->drAlBlSt); | |
127 | vcb->vcbNxtCNID = SWAP_BE32 (mdb->drNxtCNID); | |
128 | vcb->freeBlocks = SWAP_BE16 (mdb->drFreeBks); | |
129 | vcb->vcbVolBkUp = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drVolBkUp))); | |
130 | vcb->vcbWrCnt = SWAP_BE32 (mdb->drWrCnt); | |
131 | vcb->vcbNmRtDirs = SWAP_BE16 (mdb->drNmRtDirs); | |
132 | vcb->vcbFilCnt = SWAP_BE32 (mdb->drFilCnt); | |
133 | vcb->vcbDirCnt = SWAP_BE32 (mdb->drDirCnt); | |
134 | bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo)); | |
135 | if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) | |
136 | vcb->vcbWrCnt++; /* Compensate for write of MDB on last flush */ | |
137 | ||
138 | /* convert hfs encoded name into UTF-8 string */ | |
139 | error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN); | |
140 | /* | |
141 | * When an HFS name cannot be encoded with the current | |
142 | * volume encoding we use MacRoman as a fallback. | |
143 | */ | |
144 | if (error || (utf8chars == 0)) | |
145 | (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN); | |
146 | ||
147 | hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size); | |
148 | vcb->vcbVBMIOSize = kHFSBlockSize; | |
149 | ||
150 | hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_phys_block_size, | |
151 | hfsmp->hfs_phys_block_count); | |
152 | ||
153 | bzero(&cndesc, sizeof(cndesc)); | |
154 | cndesc.cd_parentcnid = kHFSRootParentID; | |
155 | cndesc.cd_flags |= CD_ISMETA; | |
156 | bzero(&cnattr, sizeof(cnattr)); | |
157 | cnattr.ca_nlink = 1; | |
158 | cnattr.ca_mode = S_IFREG; | |
159 | bzero(&fork, sizeof(fork)); | |
160 | ||
161 | /* | |
162 | * Set up Extents B-tree vnode | |
163 | */ | |
164 | cndesc.cd_nameptr = hfs_extname; | |
165 | cndesc.cd_namelen = strlen(hfs_extname); | |
166 | cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID; | |
167 | fork.cf_size = SWAP_BE32(mdb->drXTFlSize); | |
168 | fork.cf_blocks = fork.cf_size / vcb->blockSize; | |
169 | fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz); | |
170 | fork.cf_vblocks = 0; | |
171 | fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock); | |
172 | fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount); | |
173 | fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock); | |
174 | fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount); | |
175 | fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock); | |
176 | fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount); | |
177 | cnattr.ca_blocks = fork.cf_blocks; | |
178 | ||
179 | error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork, | |
180 | &hfsmp->hfs_extents_vp); | |
181 | if (error) goto MtVolErr; | |
182 | error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp), | |
183 | (KeyCompareProcPtr)CompareExtentKeys)); | |
184 | if (error) { | |
185 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
186 | goto MtVolErr; | |
187 | } | |
188 | ||
189 | /* | |
190 | * Set up Catalog B-tree vnode... | |
191 | */ | |
192 | cndesc.cd_nameptr = hfs_catname; | |
193 | cndesc.cd_namelen = strlen(hfs_catname); | |
194 | cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID; | |
195 | fork.cf_size = SWAP_BE32(mdb->drCTFlSize); | |
196 | fork.cf_blocks = fork.cf_size / vcb->blockSize; | |
197 | fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz); | |
198 | fork.cf_vblocks = 0; | |
199 | fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock); | |
200 | fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount); | |
201 | fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock); | |
202 | fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount); | |
203 | fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock); | |
204 | fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount); | |
205 | cnattr.ca_blocks = fork.cf_blocks; | |
206 | ||
207 | error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork, | |
208 | &hfsmp->hfs_catalog_vp); | |
209 | if (error) { | |
210 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
211 | goto MtVolErr; | |
212 | } | |
213 | error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp), | |
214 | (KeyCompareProcPtr)CompareCatalogKeys)); | |
215 | if (error) { | |
216 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
217 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
218 | goto MtVolErr; | |
219 | } | |
220 | ||
221 | /* | |
222 | * Set up dummy Allocation file vnode (used only for locking bitmap) | |
223 | */ | |
224 | cndesc.cd_nameptr = hfs_vbmname; | |
225 | cndesc.cd_namelen = strlen(hfs_vbmname); | |
226 | cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID; | |
227 | bzero(&fork, sizeof(fork)); | |
228 | cnattr.ca_blocks = 0; | |
229 | ||
230 | error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork, | |
231 | &hfsmp->hfs_allocation_vp); | |
232 | if (error) { | |
233 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
234 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
235 | goto MtVolErr; | |
236 | } | |
237 | ||
238 | /* mark the volume dirty (clear clean unmount bit) */ | |
239 | vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask; | |
240 | ||
241 | /* | |
242 | * all done with system files so we can unlock now... | |
243 | */ | |
244 | hfs_unlock(VTOC(hfsmp->hfs_allocation_vp)); | |
245 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
246 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
247 | ||
248 | if ( error == noErr ) | |
249 | { | |
250 | if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected | |
251 | { | |
252 | MarkVCBDirty( vcb ); // mark VCB dirty so it will be written | |
253 | } | |
254 | } | |
255 | goto CmdDone; | |
256 | ||
257 | //-- Release any resources allocated so far before exiting with an error: | |
258 | MtVolErr: | |
259 | ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp); | |
260 | ReleaseMetaFileVNode(hfsmp->hfs_extents_vp); | |
261 | ||
262 | CmdDone: | |
263 | return (error); | |
264 | } | |
265 | ||
266 | //******************************************************************************* | |
267 | // Routine: hfs_MountHFSPlusVolume | |
268 | // | |
269 | // | |
270 | //******************************************************************************* | |
271 | ||
272 | __private_extern__ | |
273 | OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, | |
274 | off_t embeddedOffset, u_int64_t disksize, struct proc *p, void *args, kauth_cred_t cred) | |
275 | { | |
276 | register ExtendedVCB *vcb; | |
277 | struct cat_desc cndesc; | |
278 | struct cat_attr cnattr; | |
279 | struct cat_fork cfork; | |
280 | UInt32 blockSize; | |
281 | daddr64_t spare_sectors; | |
282 | struct BTreeInfoRec btinfo; | |
283 | u_int16_t signature; | |
284 | u_int16_t version; | |
285 | int i; | |
286 | OSErr retval; | |
287 | ||
288 | signature = SWAP_BE16(vhp->signature); | |
289 | version = SWAP_BE16(vhp->version); | |
290 | ||
291 | if (signature == kHFSPlusSigWord) { | |
292 | if (version != kHFSPlusVersion) { | |
293 | printf("hfs_mount: invalid HFS+ version: %d\n", version); | |
294 | return (EINVAL); | |
295 | } | |
296 | } else if (signature == kHFSXSigWord) { | |
297 | if (version != kHFSXVersion) { | |
298 | printf("hfs_mount: invalid HFSX version: %d\n", version); | |
299 | return (EINVAL); | |
300 | } | |
301 | /* The in-memory signature is always 'H+'. */ | |
302 | signature = kHFSPlusSigWord; | |
303 | hfsmp->hfs_flags |= HFS_X; | |
304 | } else { | |
305 | /* Removed printf for invalid HFS+ signature because it gives | |
306 | * false error for UFS root volume | |
307 | */ | |
308 | return (EINVAL); | |
309 | } | |
310 | ||
311 | /* Block size must be at least 512 and a power of 2 */ | |
312 | blockSize = SWAP_BE32(vhp->blockSize); | |
313 | if (blockSize < 512 || !powerof2(blockSize)) | |
314 | return (EINVAL); | |
315 | ||
316 | /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */ | |
317 | if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL && | |
318 | (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0) | |
319 | return (EINVAL); | |
320 | ||
321 | /* Make sure we can live with the physical block size. */ | |
322 | if ((disksize & (hfsmp->hfs_phys_block_size - 1)) || | |
323 | (embeddedOffset & (hfsmp->hfs_phys_block_size - 1)) || | |
324 | (blockSize < hfsmp->hfs_phys_block_size)) { | |
325 | return (ENXIO); | |
326 | } | |
327 | /* | |
328 | * The VolumeHeader seems OK: transfer info from it into VCB | |
329 | * Note - the VCB starts out clear (all zeros) | |
330 | */ | |
331 | vcb = HFSTOVCB(hfsmp); | |
332 | ||
333 | vcb->vcbSigWord = signature; | |
334 | vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock); | |
335 | vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate)); | |
336 | vcb->vcbAtrb = SWAP_BE32(vhp->attributes); | |
337 | vcb->vcbClpSiz = SWAP_BE32(vhp->rsrcClumpSize); | |
338 | vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID); | |
339 | vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate)); | |
340 | vcb->vcbWrCnt = SWAP_BE32(vhp->writeCount); | |
341 | vcb->vcbFilCnt = SWAP_BE32(vhp->fileCount); | |
342 | vcb->vcbDirCnt = SWAP_BE32(vhp->folderCount); | |
343 | ||
344 | /* copy 32 bytes of Finder info */ | |
345 | bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo)); | |
346 | ||
347 | vcb->vcbAlBlSt = 0; /* hfs+ allocation blocks start at first block of volume */ | |
348 | if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) | |
349 | vcb->vcbWrCnt++; /* compensate for write of Volume Header on last flush */ | |
350 | ||
351 | /* Now fill in the Extended VCB info */ | |
352 | vcb->nextAllocation = SWAP_BE32(vhp->nextAllocation); | |
353 | vcb->totalBlocks = SWAP_BE32(vhp->totalBlocks); | |
354 | vcb->freeBlocks = SWAP_BE32(vhp->freeBlocks); | |
355 | vcb->blockSize = blockSize; | |
356 | vcb->encodingsBitmap = SWAP_BE64(vhp->encodingsBitmap); | |
357 | vcb->localCreateDate = SWAP_BE32(vhp->createDate); | |
358 | ||
359 | vcb->hfsPlusIOPosOffset = embeddedOffset; | |
360 | ||
361 | /* Default to no free block reserve */ | |
362 | vcb->reserveBlocks = 0; | |
363 | ||
364 | /* | |
365 | * Update the logical block size in the mount struct | |
366 | * (currently set up from the wrapper MDB) using the | |
367 | * new blocksize value: | |
368 | */ | |
369 | hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size); | |
370 | vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO); | |
371 | ||
372 | /* | |
373 | * Validate and initialize the location of the alternate volume header. | |
374 | */ | |
375 | spare_sectors = hfsmp->hfs_phys_block_count - | |
376 | (((daddr64_t)vcb->totalBlocks * blockSize) / | |
377 | hfsmp->hfs_phys_block_size); | |
378 | ||
379 | if (spare_sectors > (blockSize / hfsmp->hfs_phys_block_size)) { | |
380 | hfsmp->hfs_alt_id_sector = 0; /* partition has grown! */ | |
381 | } else { | |
382 | hfsmp->hfs_alt_id_sector = (hfsmp->hfsPlusIOPosOffset / hfsmp->hfs_phys_block_size) + | |
383 | HFS_ALT_SECTOR(hfsmp->hfs_phys_block_size, | |
384 | hfsmp->hfs_phys_block_count); | |
385 | } | |
386 | ||
387 | bzero(&cndesc, sizeof(cndesc)); | |
388 | cndesc.cd_parentcnid = kHFSRootParentID; | |
389 | cndesc.cd_flags |= CD_ISMETA; | |
390 | bzero(&cnattr, sizeof(cnattr)); | |
391 | cnattr.ca_nlink = 1; | |
392 | cnattr.ca_mode = S_IFREG; | |
393 | ||
394 | /* | |
395 | * Set up Extents B-tree vnode | |
396 | */ | |
397 | cndesc.cd_nameptr = hfs_extname; | |
398 | cndesc.cd_namelen = strlen(hfs_extname); | |
399 | cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID; | |
400 | ||
401 | cfork.cf_size = SWAP_BE64 (vhp->extentsFile.logicalSize); | |
402 | cfork.cf_clump = SWAP_BE32 (vhp->extentsFile.clumpSize); | |
403 | cfork.cf_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks); | |
404 | cfork.cf_vblocks = 0; | |
405 | cnattr.ca_blocks = cfork.cf_blocks; | |
406 | for (i = 0; i < kHFSPlusExtentDensity; i++) { | |
407 | cfork.cf_extents[i].startBlock = | |
408 | SWAP_BE32 (vhp->extentsFile.extents[i].startBlock); | |
409 | cfork.cf_extents[i].blockCount = | |
410 | SWAP_BE32 (vhp->extentsFile.extents[i].blockCount); | |
411 | } | |
412 | retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork, | |
413 | &hfsmp->hfs_extents_vp); | |
414 | ||
415 | if (retval) goto ErrorExit; | |
416 | retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp), | |
417 | (KeyCompareProcPtr) CompareExtentKeysPlus)); | |
418 | if (retval) { | |
419 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
420 | goto ErrorExit; | |
421 | } | |
422 | ||
423 | /* | |
424 | * Set up Catalog B-tree vnode | |
425 | */ | |
426 | cndesc.cd_nameptr = hfs_catname; | |
427 | cndesc.cd_namelen = strlen(hfs_catname); | |
428 | cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID; | |
429 | ||
430 | cfork.cf_size = SWAP_BE64 (vhp->catalogFile.logicalSize); | |
431 | cfork.cf_clump = SWAP_BE32 (vhp->catalogFile.clumpSize); | |
432 | cfork.cf_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks); | |
433 | cfork.cf_vblocks = 0; | |
434 | cnattr.ca_blocks = cfork.cf_blocks; | |
435 | for (i = 0; i < kHFSPlusExtentDensity; i++) { | |
436 | cfork.cf_extents[i].startBlock = | |
437 | SWAP_BE32 (vhp->catalogFile.extents[i].startBlock); | |
438 | cfork.cf_extents[i].blockCount = | |
439 | SWAP_BE32 (vhp->catalogFile.extents[i].blockCount); | |
440 | } | |
441 | retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork, | |
442 | &hfsmp->hfs_catalog_vp); | |
443 | if (retval) { | |
444 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
445 | goto ErrorExit; | |
446 | } | |
447 | retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp), | |
448 | (KeyCompareProcPtr) CompareExtendedCatalogKeys)); | |
449 | if (retval) { | |
450 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
451 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
452 | goto ErrorExit; | |
453 | } | |
454 | if ((hfsmp->hfs_flags & HFS_X) && | |
455 | BTGetInformation(VTOF(hfsmp->hfs_catalog_vp), 0, &btinfo) == 0) { | |
456 | if (btinfo.keyCompareType == kHFSBinaryCompare) { | |
457 | hfsmp->hfs_flags |= HFS_CASE_SENSITIVE; | |
458 | /* Install a case-sensitive key compare */ | |
459 | (void) BTOpenPath(VTOF(hfsmp->hfs_catalog_vp), | |
460 | (KeyCompareProcPtr)cat_binarykeycompare); | |
461 | } | |
462 | } | |
463 | ||
464 | /* | |
465 | * Set up Allocation file vnode | |
466 | */ | |
467 | cndesc.cd_nameptr = hfs_vbmname; | |
468 | cndesc.cd_namelen = strlen(hfs_vbmname); | |
469 | cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID; | |
470 | ||
471 | cfork.cf_size = SWAP_BE64 (vhp->allocationFile.logicalSize); | |
472 | cfork.cf_clump = SWAP_BE32 (vhp->allocationFile.clumpSize); | |
473 | cfork.cf_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks); | |
474 | cfork.cf_vblocks = 0; | |
475 | cnattr.ca_blocks = cfork.cf_blocks; | |
476 | for (i = 0; i < kHFSPlusExtentDensity; i++) { | |
477 | cfork.cf_extents[i].startBlock = | |
478 | SWAP_BE32 (vhp->allocationFile.extents[i].startBlock); | |
479 | cfork.cf_extents[i].blockCount = | |
480 | SWAP_BE32 (vhp->allocationFile.extents[i].blockCount); | |
481 | } | |
482 | retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork, | |
483 | &hfsmp->hfs_allocation_vp); | |
484 | if (retval) { | |
485 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
486 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
487 | goto ErrorExit; | |
488 | } | |
489 | ||
490 | /* | |
491 | * Set up Attribute B-tree vnode | |
492 | */ | |
493 | if (vhp->attributesFile.totalBlocks != 0) { | |
494 | cndesc.cd_nameptr = hfs_attrname; | |
495 | cndesc.cd_namelen = strlen(hfs_attrname); | |
496 | cndesc.cd_cnid = cnattr.ca_fileid = kHFSAttributesFileID; | |
497 | ||
498 | cfork.cf_size = SWAP_BE64 (vhp->attributesFile.logicalSize); | |
499 | cfork.cf_clump = SWAP_BE32 (vhp->attributesFile.clumpSize); | |
500 | cfork.cf_blocks = SWAP_BE32 (vhp->attributesFile.totalBlocks); | |
501 | cfork.cf_vblocks = 0; | |
502 | cnattr.ca_blocks = cfork.cf_blocks; | |
503 | for (i = 0; i < kHFSPlusExtentDensity; i++) { | |
504 | cfork.cf_extents[i].startBlock = | |
505 | SWAP_BE32 (vhp->attributesFile.extents[i].startBlock); | |
506 | cfork.cf_extents[i].blockCount = | |
507 | SWAP_BE32 (vhp->attributesFile.extents[i].blockCount); | |
508 | } | |
509 | retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork, | |
510 | &hfsmp->hfs_attribute_vp); | |
511 | if (retval) { | |
512 | hfs_unlock(VTOC(hfsmp->hfs_allocation_vp)); | |
513 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
514 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
515 | goto ErrorExit; | |
516 | } | |
517 | retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_attribute_vp), | |
518 | (KeyCompareProcPtr) hfs_attrkeycompare)); | |
519 | if (retval) { | |
520 | hfs_unlock(VTOC(hfsmp->hfs_attribute_vp)); | |
521 | hfs_unlock(VTOC(hfsmp->hfs_allocation_vp)); | |
522 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
523 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
524 | goto ErrorExit; | |
525 | } | |
526 | } | |
527 | ||
528 | ||
529 | /* Pick up volume name and create date */ | |
530 | retval = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, &cnattr, NULL); | |
531 | if (retval) { | |
532 | hfs_unlock(VTOC(hfsmp->hfs_allocation_vp)); | |
533 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
534 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
535 | goto ErrorExit; | |
536 | } | |
537 | vcb->vcbCrDate = cnattr.ca_itime; | |
538 | vcb->volumeNameEncodingHint = cndesc.cd_encoding; | |
539 | bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen)); | |
540 | cat_releasedesc(&cndesc); | |
541 | ||
542 | /* mark the volume dirty (clear clean unmount bit) */ | |
543 | vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask; | |
544 | if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) { | |
545 | hfs_flushvolumeheader(hfsmp, TRUE, 0); | |
546 | } | |
547 | ||
548 | /* | |
549 | * all done with metadata files so we can unlock now... | |
550 | */ | |
551 | if (hfsmp->hfs_attribute_vp) | |
552 | hfs_unlock(VTOC(hfsmp->hfs_attribute_vp)); | |
553 | hfs_unlock(VTOC(hfsmp->hfs_allocation_vp)); | |
554 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
555 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
556 | ||
557 | // | |
558 | // Check if we need to do late journal initialization. This only | |
559 | // happens if a previous version of MacOS X (or 9) touched the disk. | |
560 | // In that case hfs_late_journal_init() will go re-locate the journal | |
561 | // and journal_info_block files and validate that they're still kosher. | |
562 | // | |
563 | if ( (vcb->vcbAtrb & kHFSVolumeJournaledMask) | |
564 | && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion) | |
565 | && (hfsmp->jnl == NULL)) { | |
566 | ||
567 | retval = hfs_late_journal_init(hfsmp, vhp, args); | |
568 | if (retval != 0) { | |
569 | hfsmp->jnl = NULL; | |
570 | ||
571 | // if the journal failed to open, then set the lastMountedVersion | |
572 | // to be "FSK!" which fsck_hfs will see and force the fsck instead | |
573 | // of just bailing out because the volume is journaled. | |
574 | if (!(hfsmp->hfs_flags & HFS_READ_ONLY)) { | |
575 | HFSPlusVolumeHeader *jvhp; | |
576 | daddr64_t mdb_offset; | |
577 | struct buf *bp = NULL; | |
578 | ||
579 | hfsmp->hfs_flags |= HFS_NEED_JNL_RESET; | |
580 | ||
581 | mdb_offset = (daddr64_t)((embeddedOffset / blockSize) + HFS_PRI_SECTOR(blockSize)); | |
582 | ||
583 | retval = (int)buf_meta_bread(hfsmp->hfs_devvp, mdb_offset, blockSize, cred, &bp); | |
584 | if (retval == 0) { | |
585 | jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(blockSize)); | |
586 | ||
587 | if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) { | |
588 | printf ("hfs(3): Journal replay fail. Writing lastMountVersion as FSK!\n"); | |
589 | jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion); | |
590 | buf_bwrite(bp); | |
591 | } else { | |
592 | buf_brelse(bp); | |
593 | } | |
594 | bp = NULL; | |
595 | } else if (bp) { | |
596 | buf_brelse(bp); | |
597 | // clear this so the error exit path won't try to use it | |
598 | bp = NULL; | |
599 | } | |
600 | } | |
601 | ||
602 | retval = EINVAL; | |
603 | goto ErrorExit; | |
604 | } else if (hfsmp->jnl) { | |
605 | vfs_setflags(hfsmp->hfs_mp, (uint64_t)((unsigned int)MNT_JOURNALED)); | |
606 | } | |
607 | } else if (hfsmp->jnl) { | |
608 | struct cat_attr jinfo_attr, jnl_attr; | |
609 | ||
610 | // if we're here we need to fill in the fileid's for the | |
611 | // journal and journal_info_block. | |
612 | hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL); | |
613 | hfsmp->hfs_jnlfileid = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL); | |
614 | if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) { | |
615 | printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n"); | |
616 | printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid); | |
617 | } | |
618 | } | |
619 | ||
620 | /* | |
621 | * Establish a metadata allocation zone. | |
622 | */ | |
623 | hfs_metadatazone_init(hfsmp); | |
624 | ||
625 | /* | |
626 | * Make any metadata zone adjustments. | |
627 | */ | |
628 | if (hfsmp->hfs_flags & HFS_METADATA_ZONE) { | |
629 | /* Keep the roving allocator out of the metadata zone. */ | |
630 | if (vcb->nextAllocation >= hfsmp->hfs_metazone_start && | |
631 | vcb->nextAllocation <= hfsmp->hfs_metazone_end) { | |
632 | vcb->nextAllocation = hfsmp->hfs_metazone_end + 1; | |
633 | } | |
634 | } | |
635 | ||
636 | /* setup private/hidden directory for unlinked files */ | |
637 | FindMetaDataDirectory(vcb); | |
638 | if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) | |
639 | hfs_remove_orphans(hfsmp); | |
640 | ||
641 | if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) ) // if the disk is not write protected | |
642 | { | |
643 | MarkVCBDirty( vcb ); // mark VCB dirty so it will be written | |
644 | } | |
645 | ||
646 | /* | |
647 | * Allow hot file clustering if conditions allow. | |
648 | */ | |
649 | if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) && | |
650 | ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)) { | |
651 | (void) hfs_recording_init(hfsmp); | |
652 | } | |
653 | ||
654 | hfs_checkextendedsecurity(hfsmp); | |
655 | ||
656 | return (0); | |
657 | ||
658 | ErrorExit: | |
659 | /* | |
660 | * A fatal error occurred and the volume cannot be mounted | |
661 | * release any resources that we aquired... | |
662 | */ | |
663 | if (hfsmp->hfs_attribute_vp) | |
664 | ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp); | |
665 | ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp); | |
666 | ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp); | |
667 | ReleaseMetaFileVNode(hfsmp->hfs_extents_vp); | |
668 | ||
669 | return (retval); | |
670 | } | |
671 | ||
672 | ||
673 | /* | |
674 | * ReleaseMetaFileVNode | |
675 | * | |
676 | * vp L - - | |
677 | */ | |
678 | static void ReleaseMetaFileVNode(struct vnode *vp) | |
679 | { | |
680 | struct filefork *fp; | |
681 | ||
682 | if (vp && (fp = VTOF(vp))) { | |
683 | if (fp->fcbBTCBPtr != NULL) { | |
684 | (void)hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK); | |
685 | (void) BTClosePath(fp); | |
686 | hfs_unlock(VTOC(vp)); | |
687 | } | |
688 | ||
689 | /* release the node even if BTClosePath fails */ | |
690 | vnode_recycle(vp); | |
691 | vnode_put(vp); | |
692 | } | |
693 | } | |
694 | ||
695 | ||
696 | /************************************************************* | |
697 | * | |
698 | * Unmounts a hfs volume. | |
699 | * At this point vflush() has been called (to dump all non-metadata files) | |
700 | * | |
701 | *************************************************************/ | |
702 | ||
703 | __private_extern__ | |
704 | int | |
705 | hfsUnmount( register struct hfsmount *hfsmp, struct proc *p) | |
706 | { | |
707 | if (hfsmp->hfs_allocation_vp) | |
708 | ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp); | |
709 | ||
710 | if (hfsmp->hfs_attribute_vp) | |
711 | ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp); | |
712 | ||
713 | ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp); | |
714 | ReleaseMetaFileVNode(hfsmp->hfs_extents_vp); | |
715 | ||
716 | return (0); | |
717 | } | |
718 | ||
719 | ||
720 | /* | |
721 | * Test if fork has overflow extents. | |
722 | */ | |
723 | __private_extern__ | |
724 | int | |
725 | overflow_extents(struct filefork *fp) | |
726 | { | |
727 | u_long blocks; | |
728 | ||
729 | if (VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) { | |
730 | if (fp->ff_extents[7].blockCount == 0) | |
731 | return (0); | |
732 | ||
733 | blocks = fp->ff_extents[0].blockCount + | |
734 | fp->ff_extents[1].blockCount + | |
735 | fp->ff_extents[2].blockCount + | |
736 | fp->ff_extents[3].blockCount + | |
737 | fp->ff_extents[4].blockCount + | |
738 | fp->ff_extents[5].blockCount + | |
739 | fp->ff_extents[6].blockCount + | |
740 | fp->ff_extents[7].blockCount; | |
741 | } else { | |
742 | if (fp->ff_extents[2].blockCount == 0) | |
743 | return false; | |
744 | ||
745 | blocks = fp->ff_extents[0].blockCount + | |
746 | fp->ff_extents[1].blockCount + | |
747 | fp->ff_extents[2].blockCount; | |
748 | } | |
749 | ||
750 | return (fp->ff_blocks > blocks); | |
751 | } | |
752 | ||
753 | ||
754 | /* | |
755 | * Lock HFS system file(s). | |
756 | */ | |
757 | __private_extern__ | |
758 | int | |
759 | hfs_systemfile_lock(struct hfsmount *hfsmp, int flags, enum hfslocktype locktype) | |
760 | { | |
761 | if (flags & ~SFL_VALIDMASK) | |
762 | panic("hfs_systemfile_lock: invalid lock request (0x%x)", (unsigned long) flags); | |
763 | /* | |
764 | * Locking order is Catalog file, Attributes file, Bitmap file, Extents file | |
765 | */ | |
766 | if (flags & SFL_CATALOG) { | |
767 | (void) hfs_lock(VTOC(hfsmp->hfs_catalog_vp), locktype); | |
768 | /* | |
769 | * When the catalog file has overflow extents then | |
770 | * also acquire the extents b-tree lock if its not | |
771 | * already requested. | |
772 | */ | |
773 | if ((flags & SFL_EXTENTS) == 0 && | |
774 | overflow_extents(VTOF(hfsmp->hfs_catalog_vp))) { | |
775 | flags |= SFL_EXTENTS; | |
776 | } | |
777 | } | |
778 | if (flags & SFL_ATTRIBUTE) { | |
779 | if (hfsmp->hfs_attribute_vp) { | |
780 | (void) hfs_lock(VTOC(hfsmp->hfs_attribute_vp), locktype); | |
781 | /* | |
782 | * When the attribute file has overflow extents then | |
783 | * also acquire the extents b-tree lock if its not | |
784 | * already requested. | |
785 | */ | |
786 | if ((flags & SFL_EXTENTS) == 0 && | |
787 | overflow_extents(VTOF(hfsmp->hfs_attribute_vp))) { | |
788 | flags |= SFL_EXTENTS; | |
789 | } | |
790 | } else { | |
791 | flags &= ~SFL_ATTRIBUTE; | |
792 | } | |
793 | } | |
794 | if (flags & SFL_BITMAP) { | |
795 | /* | |
796 | * Since the only bitmap operations are clearing and | |
797 | * setting bits we always need exclusive access. And | |
798 | * when we have a journal, we can "hide" behind that | |
799 | * lock since we can only change the bitmap from | |
800 | * within a transaction. | |
801 | */ | |
802 | if (hfsmp->jnl) { | |
803 | flags &= ~SFL_BITMAP; | |
804 | } else { | |
805 | (void) hfs_lock(VTOC(hfsmp->hfs_allocation_vp), HFS_EXCLUSIVE_LOCK); | |
806 | } | |
807 | } | |
808 | if (flags & SFL_EXTENTS) { | |
809 | /* | |
810 | * Since the extents btree lock is recursive we always | |
811 | * need exclusive access. | |
812 | */ | |
813 | (void) hfs_lock(VTOC(hfsmp->hfs_extents_vp), HFS_EXCLUSIVE_LOCK); | |
814 | } | |
815 | return (flags); | |
816 | } | |
817 | ||
818 | /* | |
819 | * unlock HFS system file(s). | |
820 | */ | |
821 | __private_extern__ | |
822 | void | |
823 | hfs_systemfile_unlock(struct hfsmount *hfsmp, int flags) | |
824 | { | |
825 | struct timeval tv; | |
826 | u_int32_t lastfsync; | |
827 | int numOfLockedBuffs; | |
828 | ||
829 | microuptime(&tv); | |
830 | lastfsync = tv.tv_sec; | |
831 | ||
832 | if (flags & ~SFL_VALIDMASK) | |
833 | panic("hfs_systemfile_unlock: invalid lock request (0x%x)", (unsigned long) flags); | |
834 | ||
835 | if (flags & SFL_ATTRIBUTE && hfsmp->hfs_attribute_vp) { | |
836 | if (hfsmp->jnl == NULL) { | |
837 | BTGetLastSync((FCB*)VTOF(hfsmp->hfs_attribute_vp), &lastfsync); | |
838 | numOfLockedBuffs = count_lock_queue(); | |
839 | if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || | |
840 | ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) > | |
841 | kMaxSecsForFsync))) { | |
842 | hfs_btsync(hfsmp->hfs_attribute_vp, HFS_SYNCTRANS); | |
843 | } | |
844 | } | |
845 | hfs_unlock(VTOC(hfsmp->hfs_attribute_vp)); | |
846 | } | |
847 | if (flags & SFL_CATALOG) { | |
848 | if (hfsmp->jnl == NULL) { | |
849 | BTGetLastSync((FCB*)VTOF(hfsmp->hfs_catalog_vp), &lastfsync); | |
850 | numOfLockedBuffs = count_lock_queue(); | |
851 | if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || | |
852 | ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) > | |
853 | kMaxSecsForFsync))) { | |
854 | hfs_btsync(hfsmp->hfs_catalog_vp, HFS_SYNCTRANS); | |
855 | } | |
856 | } | |
857 | hfs_unlock(VTOC(hfsmp->hfs_catalog_vp)); | |
858 | } | |
859 | if (flags & SFL_BITMAP) { | |
860 | hfs_unlock(VTOC(hfsmp->hfs_allocation_vp)); | |
861 | } | |
862 | if (flags & SFL_EXTENTS) { | |
863 | if (hfsmp->jnl == NULL) { | |
864 | BTGetLastSync((FCB*)VTOF(hfsmp->hfs_extents_vp), &lastfsync); | |
865 | numOfLockedBuffs = count_lock_queue(); | |
866 | if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || | |
867 | ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) > | |
868 | kMaxSecsForFsync))) { | |
869 | hfs_btsync(hfsmp->hfs_extents_vp, HFS_SYNCTRANS); | |
870 | } | |
871 | } | |
872 | hfs_unlock(VTOC(hfsmp->hfs_extents_vp)); | |
873 | } | |
874 | } | |
875 | ||
876 | ||
877 | /* | |
878 | * RequireFileLock | |
879 | * | |
880 | * Check to see if a vnode is locked in the current context | |
881 | * This is to be used for debugging purposes only!! | |
882 | */ | |
883 | #if HFS_DIAGNOSTIC | |
884 | void RequireFileLock(FileReference vp, int shareable) | |
885 | { | |
886 | int locked; | |
887 | ||
888 | /* The extents btree and allocation bitmap are always exclusive. */ | |
889 | if (VTOC(vp)->c_fileid == kHFSExtentsFileID || | |
890 | VTOC(vp)->c_fileid == kHFSAllocationFileID) { | |
891 | shareable = 0; | |
892 | } | |
893 | ||
894 | locked = VTOC(vp)->c_lockowner == (void *)current_thread(); | |
895 | ||
896 | if (!locked && !shareable) { | |
897 | switch (VTOC(vp)->c_fileid) { | |
898 | case kHFSExtentsFileID: | |
899 | panic("extents btree not locked! v: 0x%08X\n #\n", (u_int)vp); | |
900 | break; | |
901 | case kHFSCatalogFileID: | |
902 | panic("catalog btree not locked! v: 0x%08X\n #\n", (u_int)vp); | |
903 | break; | |
904 | case kHFSAllocationFileID: | |
905 | /* The allocation file can hide behind the jornal lock. */ | |
906 | if (VTOHFS(vp)->jnl == NULL) | |
907 | panic("allocation file not locked! v: 0x%08X\n #\n", (u_int)vp); | |
908 | break; | |
909 | case kHFSAttributesFileID: | |
910 | panic("attributes btree not locked! v: 0x%08X\n #\n", (u_int)vp); | |
911 | break; | |
912 | } | |
913 | } | |
914 | } | |
915 | #endif | |
916 | ||
917 | ||
918 | /* | |
919 | * There are three ways to qualify for ownership rights on an object: | |
920 | * | |
921 | * 1. (a) Your UID matches the cnode's UID. | |
922 | * (b) The object in question is owned by "unknown" | |
923 | * 2. (a) Permissions on the filesystem are being ignored and | |
924 | * your UID matches the replacement UID. | |
925 | * (b) Permissions on the filesystem are being ignored and | |
926 | * the replacement UID is "unknown". | |
927 | * 3. You are root. | |
928 | * | |
929 | */ | |
930 | int | |
931 | hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, kauth_cred_t cred, | |
932 | struct proc *p, int invokesuperuserstatus) | |
933 | { | |
934 | if ((kauth_cred_getuid(cred) == cnode_uid) || /* [1a] */ | |
935 | (cnode_uid == UNKNOWNUID) || /* [1b] */ | |
936 | ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) && /* [2] */ | |
937 | ((kauth_cred_getuid(cred) == hfsmp->hfs_uid) || /* [2a] */ | |
938 | (hfsmp->hfs_uid == UNKNOWNUID))) || /* [2b] */ | |
939 | (invokesuperuserstatus && (suser(cred, 0) == 0))) { /* [3] */ | |
940 | return (0); | |
941 | } else { | |
942 | return (EPERM); | |
943 | } | |
944 | } | |
945 | ||
946 | ||
947 | unsigned long BestBlockSizeFit(unsigned long allocationBlockSize, | |
948 | unsigned long blockSizeLimit, | |
949 | unsigned long baseMultiple) { | |
950 | /* | |
951 | Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the | |
952 | specified limit but still an even multiple of the baseMultiple. | |
953 | */ | |
954 | int baseBlockCount, blockCount; | |
955 | unsigned long trialBlockSize; | |
956 | ||
957 | if (allocationBlockSize % baseMultiple != 0) { | |
958 | /* | |
959 | Whoops: the allocation blocks aren't even multiples of the specified base: | |
960 | no amount of dividing them into even parts will be a multiple, either then! | |
961 | */ | |
962 | return 512; /* Hope for the best */ | |
963 | }; | |
964 | ||
965 | /* Try the obvious winner first, to prevent 12K allocation blocks, for instance, | |
966 | from being handled as two 6K logical blocks instead of 3 4K logical blocks. | |
967 | Even though the former (the result of the loop below) is the larger allocation | |
968 | block size, the latter is more efficient: */ | |
969 | if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE; | |
970 | ||
971 | /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */ | |
972 | baseBlockCount = allocationBlockSize / baseMultiple; /* Now guaranteed to be an even multiple */ | |
973 | ||
974 | for (blockCount = baseBlockCount; blockCount > 0; --blockCount) { | |
975 | trialBlockSize = blockCount * baseMultiple; | |
976 | if (allocationBlockSize % trialBlockSize == 0) { /* An even multiple? */ | |
977 | if ((trialBlockSize <= blockSizeLimit) && | |
978 | (trialBlockSize % baseMultiple == 0)) { | |
979 | return trialBlockSize; | |
980 | }; | |
981 | }; | |
982 | }; | |
983 | ||
984 | /* Note: we should never get here, since blockCount = 1 should always work, | |
985 | but this is nice and safe and makes the compiler happy, too ... */ | |
986 | return 512; | |
987 | } | |
988 | ||
989 | ||
990 | /* | |
991 | * To make the HFS Plus filesystem follow UFS unlink semantics, a remove | |
992 | * of an active vnode is translated to a move/rename so the file appears | |
993 | * deleted. The destination folder for these move/renames is setup here | |
994 | * and a reference to it is place in hfsmp->hfs_privdir_desc. | |
995 | */ | |
996 | __private_extern__ | |
997 | u_long | |
998 | FindMetaDataDirectory(ExtendedVCB *vcb) | |
999 | { | |
1000 | struct hfsmount * hfsmp; | |
1001 | struct vnode * dvp = NULL; | |
1002 | struct cnode * dcp = NULL; | |
1003 | struct FndrDirInfo * fndrinfo; | |
1004 | struct cat_desc out_desc = {0}; | |
1005 | struct proc *p = current_proc(); | |
1006 | struct timeval tv; | |
1007 | cat_cookie_t cookie; | |
1008 | int lockflags; | |
1009 | int error; | |
1010 | ||
1011 | if (vcb->vcbSigWord != kHFSPlusSigWord) | |
1012 | return (0); | |
1013 | ||
1014 | hfsmp = VCBTOHFS(vcb); | |
1015 | ||
1016 | if (hfsmp->hfs_privdir_desc.cd_parentcnid == 0) { | |
1017 | hfsmp->hfs_privdir_desc.cd_parentcnid = kRootDirID; | |
1018 | hfsmp->hfs_privdir_desc.cd_nameptr = hfs_privdirname; | |
1019 | hfsmp->hfs_privdir_desc.cd_namelen = strlen(hfs_privdirname); | |
1020 | hfsmp->hfs_privdir_desc.cd_flags = CD_ISDIR; | |
1021 | } | |
1022 | ||
1023 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
1024 | ||
1025 | error = cat_lookup(hfsmp, &hfsmp->hfs_privdir_desc, 0, NULL, | |
1026 | &hfsmp->hfs_privdir_attr, NULL, NULL); | |
1027 | ||
1028 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1029 | ||
1030 | if (error == 0) { | |
1031 | hfsmp->hfs_metadata_createdate = hfsmp->hfs_privdir_attr.ca_itime; | |
1032 | hfsmp->hfs_privdir_desc.cd_cnid = hfsmp->hfs_privdir_attr.ca_fileid; | |
1033 | /* | |
1034 | * Clear the system immutable flag if set... | |
1035 | */ | |
1036 | if ((hfsmp->hfs_privdir_attr.ca_flags & SF_IMMUTABLE) && | |
1037 | (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) { | |
1038 | hfsmp->hfs_privdir_attr.ca_flags &= ~SF_IMMUTABLE; | |
1039 | ||
1040 | if ((error = hfs_start_transaction(hfsmp)) != 0) { | |
1041 | return (hfsmp->hfs_privdir_attr.ca_fileid); | |
1042 | } | |
1043 | ||
1044 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
1045 | (void) cat_update(hfsmp, &hfsmp->hfs_privdir_desc, | |
1046 | &hfsmp->hfs_privdir_attr, NULL, NULL); | |
1047 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1048 | ||
1049 | hfs_end_transaction(hfsmp); | |
1050 | } | |
1051 | return (hfsmp->hfs_privdir_attr.ca_fileid); | |
1052 | ||
1053 | } else if (hfsmp->hfs_flags & HFS_READ_ONLY) { | |
1054 | ||
1055 | return (0); | |
1056 | } | |
1057 | ||
1058 | /* Setup the default attributes */ | |
1059 | bzero(&hfsmp->hfs_privdir_attr, sizeof(struct cat_attr)); | |
1060 | hfsmp->hfs_privdir_attr.ca_mode = S_IFDIR; | |
1061 | hfsmp->hfs_privdir_attr.ca_nlink = 2; | |
1062 | hfsmp->hfs_privdir_attr.ca_itime = vcb->vcbCrDate; | |
1063 | microtime(&tv); | |
1064 | hfsmp->hfs_privdir_attr.ca_mtime = tv.tv_sec; | |
1065 | ||
1066 | /* hidden and off the desktop view */ | |
1067 | fndrinfo = (struct FndrDirInfo *)&hfsmp->hfs_privdir_attr.ca_finderinfo; | |
1068 | fndrinfo->frLocation.v = SWAP_BE16 (22460); | |
1069 | fndrinfo->frLocation.h = SWAP_BE16 (22460); | |
1070 | fndrinfo->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked); | |
1071 | ||
1072 | if ((error = hfs_start_transaction(hfsmp)) != 0) { | |
1073 | return (0); | |
1074 | } | |
1075 | /* Reserve some space in the Catalog file. */ | |
1076 | if (cat_preflight(hfsmp, CAT_CREATE, &cookie, p) != 0) { | |
1077 | hfs_end_transaction(hfsmp); | |
1078 | ||
1079 | return (0); | |
1080 | } | |
1081 | ||
1082 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); | |
1083 | ||
1084 | error = cat_create(hfsmp, &hfsmp->hfs_privdir_desc, | |
1085 | &hfsmp->hfs_privdir_attr, &out_desc); | |
1086 | ||
1087 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1088 | ||
1089 | cat_postflight(hfsmp, &cookie, p); | |
1090 | ||
1091 | if (error) { | |
1092 | hfs_volupdate(hfsmp, VOL_UPDATE, 0); | |
1093 | ||
1094 | hfs_end_transaction(hfsmp); | |
1095 | ||
1096 | return (0); | |
1097 | } | |
1098 | ||
1099 | hfsmp->hfs_privdir_desc.cd_hint = out_desc.cd_hint; | |
1100 | hfsmp->hfs_privdir_desc.cd_cnid = out_desc.cd_cnid; | |
1101 | hfsmp->hfs_privdir_attr.ca_fileid = out_desc.cd_cnid; | |
1102 | hfsmp->hfs_metadata_createdate = vcb->vcbCrDate; | |
1103 | ||
1104 | if (hfs_vget(hfsmp, kRootDirID, &dvp, 0) == 0) { | |
1105 | dcp = VTOC(dvp); | |
1106 | dcp->c_childhint = out_desc.cd_hint; | |
1107 | dcp->c_nlink++; | |
1108 | dcp->c_entries++; | |
1109 | dcp->c_touch_chgtime = TRUE; | |
1110 | dcp->c_touch_modtime = TRUE; | |
1111 | (void) hfs_update(dvp, 0); | |
1112 | hfs_unlock(dcp); | |
1113 | vnode_put(dvp); | |
1114 | } | |
1115 | hfs_volupdate(hfsmp, VOL_MKDIR, 1); | |
1116 | hfs_end_transaction(hfsmp); | |
1117 | ||
1118 | cat_releasedesc(&out_desc); | |
1119 | ||
1120 | return (out_desc.cd_cnid); | |
1121 | } | |
1122 | ||
1123 | __private_extern__ | |
1124 | u_long | |
1125 | GetFileInfo(ExtendedVCB *vcb, u_int32_t dirid, const char *name, | |
1126 | struct cat_attr *fattr, struct cat_fork *forkinfo) | |
1127 | { | |
1128 | struct hfsmount * hfsmp; | |
1129 | struct vnode * dvp = NULL; | |
1130 | struct cnode * dcp = NULL; | |
1131 | struct FndrDirInfo * fndrinfo; | |
1132 | struct cat_desc jdesc; | |
1133 | int lockflags; | |
1134 | int error; | |
1135 | ||
1136 | if (vcb->vcbSigWord != kHFSPlusSigWord) | |
1137 | return (0); | |
1138 | ||
1139 | hfsmp = VCBTOHFS(vcb); | |
1140 | ||
1141 | memset(&jdesc, 0, sizeof(struct cat_desc)); | |
1142 | jdesc.cd_parentcnid = kRootDirID; | |
1143 | jdesc.cd_nameptr = name; | |
1144 | jdesc.cd_namelen = strlen(name); | |
1145 | ||
1146 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
1147 | error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo, NULL); | |
1148 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1149 | ||
1150 | if (error == 0) { | |
1151 | return (fattr->ca_fileid); | |
1152 | } else if (hfsmp->hfs_flags & HFS_READ_ONLY) { | |
1153 | return (0); | |
1154 | } | |
1155 | ||
1156 | return (0); /* XXX what callers expect on an error */ | |
1157 | } | |
1158 | ||
1159 | ||
1160 | /* | |
1161 | * On HFS Plus Volume, there can be orphaned files. These | |
1162 | * are files that were unlinked while busy. If the volume | |
1163 | * was not cleanly unmounted then some of these files may | |
1164 | * have persisted and need to be removed. | |
1165 | */ | |
1166 | __private_extern__ | |
1167 | void | |
1168 | hfs_remove_orphans(struct hfsmount * hfsmp) | |
1169 | { | |
1170 | struct BTreeIterator * iterator = NULL; | |
1171 | struct FSBufferDescriptor btdata; | |
1172 | struct HFSPlusCatalogFile filerec; | |
1173 | struct HFSPlusCatalogKey * keyp; | |
1174 | struct proc *p = current_proc(); | |
1175 | FCB *fcb; | |
1176 | ExtendedVCB *vcb; | |
1177 | char filename[32]; | |
1178 | char tempname[32]; | |
1179 | size_t namelen; | |
1180 | cat_cookie_t cookie; | |
1181 | int catlock = 0; | |
1182 | int catreserve = 0; | |
1183 | int started_tr = 0; | |
1184 | int lockflags; | |
1185 | int result; | |
1186 | int orphanedlinks = 0; | |
1187 | ||
1188 | bzero(&cookie, sizeof(cookie)); | |
1189 | ||
1190 | if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS) | |
1191 | return; | |
1192 | ||
1193 | vcb = HFSTOVCB(hfsmp); | |
1194 | fcb = VTOF(hfsmp->hfs_catalog_vp); | |
1195 | ||
1196 | btdata.bufferAddress = &filerec; | |
1197 | btdata.itemSize = sizeof(filerec); | |
1198 | btdata.itemCount = 1; | |
1199 | ||
1200 | MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); | |
1201 | bzero(iterator, sizeof(*iterator)); | |
1202 | ||
1203 | /* Build a key to "temp" */ | |
1204 | keyp = (HFSPlusCatalogKey*)&iterator->key; | |
1205 | keyp->parentID = hfsmp->hfs_privdir_desc.cd_cnid; | |
1206 | keyp->nodeName.length = 4; /* "temp" */ | |
1207 | keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + keyp->nodeName.length * 2; | |
1208 | keyp->nodeName.unicode[0] = 't'; | |
1209 | keyp->nodeName.unicode[1] = 'e'; | |
1210 | keyp->nodeName.unicode[2] = 'm'; | |
1211 | keyp->nodeName.unicode[3] = 'p'; | |
1212 | ||
1213 | /* | |
1214 | * Position the iterator just before the first real temp file. | |
1215 | */ | |
1216 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); | |
1217 | (void) BTSearchRecord(fcb, iterator, NULL, NULL, iterator); | |
1218 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1219 | ||
1220 | /* Visit all the temp files in the HFS+ private directory. */ | |
1221 | for (;;) { | |
1222 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); | |
1223 | result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL); | |
1224 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1225 | if (result) | |
1226 | break; | |
1227 | if (keyp->parentID != hfsmp->hfs_privdir_desc.cd_cnid) | |
1228 | break; | |
1229 | if (filerec.recordType != kHFSPlusFileRecord) | |
1230 | continue; | |
1231 | ||
1232 | (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2, | |
1233 | filename, &namelen, sizeof(filename), 0, 0); | |
1234 | ||
1235 | (void) sprintf(tempname, "%s%d", HFS_DELETE_PREFIX, filerec.fileID); | |
1236 | ||
1237 | /* | |
1238 | * Delete all files named "tempxxx", where | |
1239 | * xxx is the file's cnid in decimal. | |
1240 | * | |
1241 | */ | |
1242 | if (bcmp(tempname, filename, namelen) == 0) { | |
1243 | struct filefork dfork; | |
1244 | struct filefork rfork; | |
1245 | struct cnode cnode; | |
1246 | ||
1247 | bzero(&dfork, sizeof(dfork)); | |
1248 | bzero(&rfork, sizeof(rfork)); | |
1249 | bzero(&cnode, sizeof(cnode)); | |
1250 | ||
1251 | if (hfs_start_transaction(hfsmp) != 0) { | |
1252 | printf("hfs_remove_orphans: failed to start transaction\n"); | |
1253 | goto exit; | |
1254 | } | |
1255 | started_tr = 1; | |
1256 | ||
1257 | /* | |
1258 | * Reserve some space in the Catalog file. | |
1259 | */ | |
1260 | if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) { | |
1261 | printf("hfs_remove_orphans: cat_preflight failed\n"); | |
1262 | goto exit; | |
1263 | } | |
1264 | catreserve = 1; | |
1265 | ||
1266 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK); | |
1267 | catlock = 1; | |
1268 | ||
1269 | /* Build a fake cnode */ | |
1270 | cat_convertattr(hfsmp, (CatalogRecord *)&filerec, &cnode.c_attr, | |
1271 | &dfork.ff_data, &rfork.ff_data); | |
1272 | cnode.c_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid; | |
1273 | cnode.c_desc.cd_nameptr = filename; | |
1274 | cnode.c_desc.cd_namelen = namelen; | |
1275 | cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid; | |
1276 | cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks; | |
1277 | ||
1278 | /* Position iterator at previous entry */ | |
1279 | if (BTIterateRecord(fcb, kBTreePrevRecord, iterator, | |
1280 | NULL, NULL) != 0) { | |
1281 | break; | |
1282 | } | |
1283 | ||
1284 | /* Truncate the file to zero (both forks) */ | |
1285 | if (dfork.ff_blocks > 0) { | |
1286 | u_int64_t fsize; | |
1287 | ||
1288 | dfork.ff_cp = &cnode; | |
1289 | cnode.c_datafork = &dfork; | |
1290 | cnode.c_rsrcfork = NULL; | |
1291 | fsize = (u_int64_t)dfork.ff_blocks * (u_int64_t)HFSTOVCB(hfsmp)->blockSize; | |
1292 | while (fsize > 0) { | |
1293 | if (fsize > HFS_BIGFILE_SIZE) { | |
1294 | fsize -= HFS_BIGFILE_SIZE; | |
1295 | } else { | |
1296 | fsize = 0; | |
1297 | } | |
1298 | ||
1299 | if (TruncateFileC(vcb, (FCB*)&dfork, fsize, false) != 0) { | |
1300 | printf("error truncting data fork!\n"); | |
1301 | break; | |
1302 | } | |
1303 | ||
1304 | // | |
1305 | // if we're iteratively truncating this file down, | |
1306 | // then end the transaction and start a new one so | |
1307 | // that no one transaction gets too big. | |
1308 | // | |
1309 | if (fsize > 0 && started_tr) { | |
1310 | hfs_end_transaction(hfsmp); | |
1311 | if (hfs_start_transaction(hfsmp) != 0) { | |
1312 | started_tr = 0; | |
1313 | break; | |
1314 | } | |
1315 | } | |
1316 | } | |
1317 | } | |
1318 | ||
1319 | if (rfork.ff_blocks > 0) { | |
1320 | rfork.ff_cp = &cnode; | |
1321 | cnode.c_datafork = NULL; | |
1322 | cnode.c_rsrcfork = &rfork; | |
1323 | if (TruncateFileC(vcb, (FCB*)&rfork, 0, false) != 0) { | |
1324 | printf("error truncting rsrc fork!\n"); | |
1325 | break; | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | /* Remove the file record from the Catalog */ | |
1330 | if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) { | |
1331 | printf("hfs_remove_oprhans: error deleting cat rec for id %d!\n", cnode.c_desc.cd_cnid); | |
1332 | hfs_volupdate(hfsmp, VOL_UPDATE, 0); | |
1333 | break; | |
1334 | } | |
1335 | ++orphanedlinks; | |
1336 | ||
1337 | /* Delete any attributes, ignore errors */ | |
1338 | (void) hfs_removeallattr(hfsmp, cnode.c_fileid); | |
1339 | ||
1340 | /* Update parent and volume counts */ | |
1341 | hfsmp->hfs_privdir_attr.ca_entries--; | |
1342 | (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc, | |
1343 | &hfsmp->hfs_privdir_attr, NULL, NULL); | |
1344 | hfs_volupdate(hfsmp, VOL_RMFILE, 0); | |
1345 | ||
1346 | /* Drop locks and end the transaction */ | |
1347 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1348 | cat_postflight(hfsmp, &cookie, p); | |
1349 | catlock = catreserve = 0; | |
1350 | if (started_tr) { | |
1351 | hfs_end_transaction(hfsmp); | |
1352 | started_tr = 0; | |
1353 | } | |
1354 | ||
1355 | } /* end if */ | |
1356 | } /* end for */ | |
1357 | if (orphanedlinks > 0) | |
1358 | printf("HFS: Removed %d orphaned unlinked files\n", orphanedlinks); | |
1359 | exit: | |
1360 | if (catlock) { | |
1361 | hfs_systemfile_unlock(hfsmp, lockflags); | |
1362 | } | |
1363 | if (catreserve) { | |
1364 | cat_postflight(hfsmp, &cookie, p); | |
1365 | } | |
1366 | if (started_tr) { | |
1367 | hfs_end_transaction(hfsmp); | |
1368 | } | |
1369 | ||
1370 | FREE(iterator, M_TEMP); | |
1371 | hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS; | |
1372 | } | |
1373 | ||
1374 | ||
1375 | /* | |
1376 | * This will return the correct logical block size for a given vnode. | |
1377 | * For most files, it is the allocation block size, for meta data like | |
1378 | * BTrees, this is kept as part of the BTree private nodeSize | |
1379 | */ | |
1380 | u_int32_t | |
1381 | GetLogicalBlockSize(struct vnode *vp) | |
1382 | { | |
1383 | u_int32_t logBlockSize; | |
1384 | ||
1385 | DBG_ASSERT(vp != NULL); | |
1386 | ||
1387 | /* start with default */ | |
1388 | logBlockSize = VTOHFS(vp)->hfs_logBlockSize; | |
1389 | ||
1390 | if (vnode_issystem(vp)) { | |
1391 | if (VTOF(vp)->fcbBTCBPtr != NULL) { | |
1392 | BTreeInfoRec bTreeInfo; | |
1393 | ||
1394 | /* | |
1395 | * We do not lock the BTrees, because if we are getting block..then the tree | |
1396 | * should be locked in the first place. | |
1397 | * We just want the nodeSize wich will NEVER change..so even if the world | |
1398 | * is changing..the nodeSize should remain the same. Which argues why lock | |
1399 | * it in the first place?? | |
1400 | */ | |
1401 | ||
1402 | (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo); | |
1403 | ||
1404 | logBlockSize = bTreeInfo.nodeSize; | |
1405 | ||
1406 | } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) { | |
1407 | logBlockSize = VTOVCB(vp)->vcbVBMIOSize; | |
1408 | } | |
1409 | } | |
1410 | ||
1411 | DBG_ASSERT(logBlockSize > 0); | |
1412 | ||
1413 | return logBlockSize; | |
1414 | } | |
1415 | ||
1416 | __private_extern__ | |
1417 | u_int32_t | |
1418 | hfs_freeblks(struct hfsmount * hfsmp, int wantreserve) | |
1419 | { | |
1420 | ExtendedVCB *vcb = HFSTOVCB(hfsmp); | |
1421 | u_int32_t freeblks; | |
1422 | ||
1423 | HFS_MOUNT_LOCK(hfsmp, TRUE); | |
1424 | freeblks = vcb->freeBlocks; | |
1425 | if (wantreserve) { | |
1426 | if (freeblks > vcb->reserveBlocks) | |
1427 | freeblks -= vcb->reserveBlocks; | |
1428 | else | |
1429 | freeblks = 0; | |
1430 | } | |
1431 | if (freeblks > vcb->loanedBlocks) | |
1432 | freeblks -= vcb->loanedBlocks; | |
1433 | else | |
1434 | freeblks = 0; | |
1435 | HFS_MOUNT_UNLOCK(hfsmp, TRUE); | |
1436 | ||
1437 | #ifdef HFS_SPARSE_DEV | |
1438 | /* | |
1439 | * When the underlying device is sparse, check the | |
1440 | * available space on the backing store volume. | |
1441 | */ | |
1442 | if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) { | |
1443 | struct vfsstatfs *vfsp; /* 272 bytes */ | |
1444 | u_int32_t vfreeblks; | |
1445 | u_int32_t loanedblks; | |
1446 | struct mount * backingfs_mp; | |
1447 | ||
1448 | backingfs_mp = vnode_mount(hfsmp->hfs_backingfs_rootvp); | |
1449 | ||
1450 | if (vfsp = vfs_statfs(backingfs_mp)) { | |
1451 | HFS_MOUNT_LOCK(hfsmp, TRUE); | |
1452 | vfreeblks = (u_int32_t)vfsp->f_bavail; | |
1453 | /* Normalize block count if needed. */ | |
1454 | if (vfsp->f_bsize != vcb->blockSize) { | |
1455 | vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)(vfsp->f_bsize)) / vcb->blockSize; | |
1456 | } | |
1457 | if (vfreeblks > hfsmp->hfs_sparsebandblks) | |
1458 | vfreeblks -= hfsmp->hfs_sparsebandblks; | |
1459 | else | |
1460 | vfreeblks = 0; | |
1461 | ||
1462 | /* Take into account any delayed allocations. */ | |
1463 | loanedblks = 2 * vcb->loanedBlocks; | |
1464 | if (vfreeblks > loanedblks) | |
1465 | vfreeblks -= loanedblks; | |
1466 | else | |
1467 | vfreeblks = 0; | |
1468 | ||
1469 | freeblks = MIN(vfreeblks, freeblks); | |
1470 | HFS_MOUNT_UNLOCK(hfsmp, TRUE); | |
1471 | } | |
1472 | } | |
1473 | #endif /* HFS_SPARSE_DEV */ | |
1474 | ||
1475 | return (freeblks); | |
1476 | } | |
1477 | ||
1478 | /* | |
1479 | * Map HFS Common errors (negative) to BSD error codes (positive). | |
1480 | * Positive errors (ie BSD errors) are passed through unchanged. | |
1481 | */ | |
1482 | short MacToVFSError(OSErr err) | |
1483 | { | |
1484 | if (err >= 0) | |
1485 | return err; | |
1486 | ||
1487 | switch (err) { | |
1488 | case dskFulErr: /* -34 */ | |
1489 | case btNoSpaceAvail: /* -32733 */ | |
1490 | return ENOSPC; | |
1491 | case fxOvFlErr: /* -32750 */ | |
1492 | return EOVERFLOW; | |
1493 | ||
1494 | case btBadNode: /* -32731 */ | |
1495 | return EIO; | |
1496 | ||
1497 | case memFullErr: /* -108 */ | |
1498 | return ENOMEM; /* +12 */ | |
1499 | ||
1500 | case cmExists: /* -32718 */ | |
1501 | case btExists: /* -32734 */ | |
1502 | return EEXIST; /* +17 */ | |
1503 | ||
1504 | case cmNotFound: /* -32719 */ | |
1505 | case btNotFound: /* -32735 */ | |
1506 | return ENOENT; /* 28 */ | |
1507 | ||
1508 | case cmNotEmpty: /* -32717 */ | |
1509 | return ENOTEMPTY; /* 66 */ | |
1510 | ||
1511 | case cmFThdDirErr: /* -32714 */ | |
1512 | return EISDIR; /* 21 */ | |
1513 | ||
1514 | case fxRangeErr: /* -32751 */ | |
1515 | return ERANGE; | |
1516 | ||
1517 | case bdNamErr: /* -37 */ | |
1518 | return ENAMETOOLONG; /* 63 */ | |
1519 | ||
1520 | case paramErr: /* -50 */ | |
1521 | case fileBoundsErr: /* -1309 */ | |
1522 | return EINVAL; /* +22 */ | |
1523 | ||
1524 | case fsBTBadNodeSize: | |
1525 | return ENXIO; | |
1526 | ||
1527 | default: | |
1528 | return EIO; /* +5 */ | |
1529 | } | |
1530 | } | |
1531 | ||
1532 | ||
1533 | /* | |
1534 | * Find the current thread's directory hint for a given index. | |
1535 | * | |
1536 | * Requires an exclusive lock on directory cnode. | |
1537 | */ | |
1538 | __private_extern__ | |
1539 | directoryhint_t * | |
1540 | hfs_getdirhint(struct cnode *dcp, int index) | |
1541 | { | |
1542 | struct timeval tv; | |
1543 | directoryhint_t *hint; | |
1544 | boolean_t need_remove, need_init; | |
1545 | char * name; | |
1546 | ||
1547 | microuptime(&tv); | |
1548 | ||
1549 | /* | |
1550 | * Look for an existing hint first. If not found, create a new one (when | |
1551 | * the list is not full) or recycle the oldest hint. Since new hints are | |
1552 | * always added to the head of the list, the last hint is always the | |
1553 | * oldest. | |
1554 | */ | |
1555 | TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) { | |
1556 | if (hint->dh_index == index) | |
1557 | break; | |
1558 | } | |
1559 | if (hint != NULL) { /* found an existing hint */ | |
1560 | need_init = false; | |
1561 | need_remove = true; | |
1562 | } else { /* cannot find an existing hint */ | |
1563 | need_init = true; | |
1564 | if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */ | |
1565 | /* Create a default directory hint */ | |
1566 | MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK); | |
1567 | ++dcp->c_dirhintcnt; | |
1568 | need_remove = false; | |
1569 | } else { /* recycle the last (i.e., the oldest) hint */ | |
1570 | hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); | |
1571 | if ((name = hint->dh_desc.cd_nameptr)) { | |
1572 | hint->dh_desc.cd_nameptr = NULL; | |
1573 | vfs_removename(name); | |
1574 | } | |
1575 | need_remove = true; | |
1576 | } | |
1577 | } | |
1578 | ||
1579 | if (need_remove) | |
1580 | TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link); | |
1581 | ||
1582 | TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link); | |
1583 | ||
1584 | if (need_init) { | |
1585 | hint->dh_index = index; | |
1586 | hint->dh_desc.cd_flags = 0; | |
1587 | hint->dh_desc.cd_encoding = 0; | |
1588 | hint->dh_desc.cd_namelen = 0; | |
1589 | hint->dh_desc.cd_nameptr = NULL; | |
1590 | hint->dh_desc.cd_parentcnid = dcp->c_cnid; | |
1591 | hint->dh_desc.cd_hint = dcp->c_childhint; | |
1592 | hint->dh_desc.cd_cnid = 0; | |
1593 | } | |
1594 | hint->dh_time = tv.tv_sec; | |
1595 | return (hint); | |
1596 | } | |
1597 | ||
1598 | /* | |
1599 | * Release a single directory hint. | |
1600 | * | |
1601 | * Requires an exclusive lock on directory cnode. | |
1602 | */ | |
1603 | __private_extern__ | |
1604 | void | |
1605 | hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint) | |
1606 | { | |
1607 | char * name; | |
1608 | ||
1609 | TAILQ_REMOVE(&dcp->c_hintlist, relhint, dh_link); | |
1610 | name = relhint->dh_desc.cd_nameptr; | |
1611 | if (name != NULL) { | |
1612 | relhint->dh_desc.cd_nameptr = NULL; | |
1613 | vfs_removename(name); | |
1614 | } | |
1615 | FREE_ZONE(relhint, sizeof(directoryhint_t), M_HFSDIRHINT); | |
1616 | --dcp->c_dirhintcnt; | |
1617 | } | |
1618 | ||
1619 | /* | |
1620 | * Release directory hints for given directory | |
1621 | * | |
1622 | * Requires an exclusive lock on directory cnode. | |
1623 | */ | |
1624 | __private_extern__ | |
1625 | void | |
1626 | hfs_reldirhints(struct cnode *dcp, int stale_hints_only) | |
1627 | { | |
1628 | struct timeval tv; | |
1629 | directoryhint_t *hint, *prev; | |
1630 | char * name; | |
1631 | ||
1632 | if (stale_hints_only) | |
1633 | microuptime(&tv); | |
1634 | ||
1635 | /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */ | |
1636 | for (hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); hint != NULL; hint = prev) { | |
1637 | if (stale_hints_only && (tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL) | |
1638 | break; /* stop here if this entry is too new */ | |
1639 | name = hint->dh_desc.cd_nameptr; | |
1640 | if (name != NULL) { | |
1641 | hint->dh_desc.cd_nameptr = NULL; | |
1642 | vfs_removename(name); | |
1643 | } | |
1644 | prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */ | |
1645 | TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link); | |
1646 | FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT); | |
1647 | --dcp->c_dirhintcnt; | |
1648 | } | |
1649 | } | |
1650 | ||
1651 | ||
1652 | /* | |
1653 | * Perform a case-insensitive compare of two UTF-8 filenames. | |
1654 | * | |
1655 | * Returns 0 if the strings match. | |
1656 | */ | |
1657 | __private_extern__ | |
1658 | int | |
1659 | hfs_namecmp(const char *str1, size_t len1, const char *str2, size_t len2) | |
1660 | { | |
1661 | u_int16_t *ustr1, *ustr2; | |
1662 | size_t ulen1, ulen2; | |
1663 | size_t maxbytes; | |
1664 | int cmp = -1; | |
1665 | ||
1666 | if (len1 != len2) | |
1667 | return (cmp); | |
1668 | ||
1669 | maxbytes = kHFSPlusMaxFileNameChars << 1; | |
1670 | MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK); | |
1671 | ustr2 = ustr1 + (maxbytes >> 1); | |
1672 | ||
1673 | if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0) | |
1674 | goto out; | |
1675 | if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0) | |
1676 | goto out; | |
1677 | ||
1678 | cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1); | |
1679 | out: | |
1680 | FREE(ustr1, M_TEMP); | |
1681 | return (cmp); | |
1682 | } | |
1683 | ||
1684 | ||
1685 | __private_extern__ | |
1686 | int | |
1687 | hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, | |
1688 | void *_args, off_t embeddedOffset, daddr64_t mdb_offset, | |
1689 | HFSMasterDirectoryBlock *mdbp, kauth_cred_t cred) | |
1690 | { | |
1691 | JournalInfoBlock *jibp; | |
1692 | struct buf *jinfo_bp, *bp; | |
1693 | int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0; | |
1694 | int retval, blksize = hfsmp->hfs_phys_block_size; | |
1695 | struct vnode *devvp; | |
1696 | struct hfs_mount_args *args = _args; | |
1697 | ||
1698 | devvp = hfsmp->hfs_devvp; | |
1699 | ||
1700 | if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) { | |
1701 | arg_flags = args->journal_flags; | |
1702 | arg_tbufsz = args->journal_tbuffer_size; | |
1703 | } | |
1704 | ||
1705 | sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize; | |
1706 | ||
1707 | retval = (int)buf_meta_bread(devvp, | |
1708 | (daddr64_t)((embeddedOffset/blksize) + | |
1709 | (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)), | |
1710 | SWAP_BE32(vhp->blockSize), cred, &jinfo_bp); | |
1711 | if (retval) | |
1712 | return retval; | |
1713 | ||
1714 | jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp); | |
1715 | jibp->flags = SWAP_BE32(jibp->flags); | |
1716 | jibp->offset = SWAP_BE64(jibp->offset); | |
1717 | jibp->size = SWAP_BE64(jibp->size); | |
1718 | ||
1719 | if (jibp->flags & kJIJournalInFSMask) { | |
1720 | hfsmp->jvp = hfsmp->hfs_devvp; | |
1721 | } else { | |
1722 | printf("hfs: journal not stored in fs! don't know what to do.\n"); | |
1723 | buf_brelse(jinfo_bp); | |
1724 | return EINVAL; | |
1725 | } | |
1726 | ||
1727 | // save this off for the hack-y check in hfs_remove() | |
1728 | hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize); | |
1729 | hfsmp->jnl_size = jibp->size; | |
1730 | ||
1731 | if (jibp->flags & kJIJournalNeedInitMask) { | |
1732 | printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n", | |
1733 | jibp->offset + embeddedOffset, jibp->size); | |
1734 | hfsmp->jnl = journal_create(hfsmp->jvp, | |
1735 | jibp->offset + embeddedOffset, | |
1736 | jibp->size, | |
1737 | devvp, | |
1738 | blksize, | |
1739 | arg_flags, | |
1740 | arg_tbufsz, | |
1741 | hfs_sync_metadata, hfsmp->hfs_mp); | |
1742 | ||
1743 | // no need to start a transaction here... if this were to fail | |
1744 | // we'd just re-init it on the next mount. | |
1745 | jibp->flags &= ~kJIJournalNeedInitMask; | |
1746 | jibp->flags = SWAP_BE32(jibp->flags); | |
1747 | jibp->offset = SWAP_BE64(jibp->offset); | |
1748 | jibp->size = SWAP_BE64(jibp->size); | |
1749 | buf_bwrite(jinfo_bp); | |
1750 | jinfo_bp = NULL; | |
1751 | jibp = NULL; | |
1752 | } else { | |
1753 | //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n", | |
1754 | // jibp->offset + embeddedOffset, | |
1755 | // jibp->size, SWAP_BE32(vhp->blockSize)); | |
1756 | ||
1757 | hfsmp->jnl = journal_open(hfsmp->jvp, | |
1758 | jibp->offset + embeddedOffset, | |
1759 | jibp->size, | |
1760 | devvp, | |
1761 | blksize, | |
1762 | arg_flags, | |
1763 | arg_tbufsz, | |
1764 | hfs_sync_metadata, hfsmp->hfs_mp); | |
1765 | ||
1766 | buf_brelse(jinfo_bp); | |
1767 | jinfo_bp = NULL; | |
1768 | jibp = NULL; | |
1769 | ||
1770 | if (hfsmp->jnl && mdbp) { | |
1771 | // reload the mdb because it could have changed | |
1772 | // if the journal had to be replayed. | |
1773 | if (mdb_offset == 0) { | |
1774 | mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize)); | |
1775 | } | |
1776 | retval = (int)buf_meta_bread(devvp, mdb_offset, blksize, cred, &bp); | |
1777 | if (retval) { | |
1778 | buf_brelse(bp); | |
1779 | printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n", | |
1780 | retval); | |
1781 | return retval; | |
1782 | } | |
1783 | bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(blksize), mdbp, 512); | |
1784 | buf_brelse(bp); | |
1785 | bp = NULL; | |
1786 | } | |
1787 | } | |
1788 | ||
1789 | ||
1790 | //printf("journal @ 0x%x\n", hfsmp->jnl); | |
1791 | ||
1792 | // if we expected the journal to be there and we couldn't | |
1793 | // create it or open it then we have to bail out. | |
1794 | if (hfsmp->jnl == NULL) { | |
1795 | printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval); | |
1796 | return EINVAL; | |
1797 | } | |
1798 | ||
1799 | return 0; | |
1800 | } | |
1801 | ||
1802 | ||
1803 | // | |
1804 | // This function will go and re-locate the .journal_info_block and | |
1805 | // the .journal files in case they moved (which can happen if you | |
1806 | // run Norton SpeedDisk). If we fail to find either file we just | |
1807 | // disable journaling for this volume and return. We turn off the | |
1808 | // journaling bit in the vcb and assume it will get written to disk | |
1809 | // later (if it doesn't on the next mount we'd do the same thing | |
1810 | // again which is harmless). If we disable journaling we don't | |
1811 | // return an error so that the volume is still mountable. | |
1812 | // | |
1813 | // If the info we find for the .journal_info_block and .journal files | |
1814 | // isn't what we had stored, we re-set our cached info and proceed | |
1815 | // with opening the journal normally. | |
1816 | // | |
1817 | static int | |
1818 | hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args) | |
1819 | { | |
1820 | JournalInfoBlock *jibp; | |
1821 | struct buf *jinfo_bp, *bp; | |
1822 | int sectors_per_fsblock, arg_flags=0, arg_tbufsz=0; | |
1823 | int retval, need_flush = 0, write_jibp = 0; | |
1824 | struct vnode *devvp; | |
1825 | struct cat_attr jib_attr, jattr; | |
1826 | struct cat_fork jib_fork, jfork; | |
1827 | ExtendedVCB *vcb; | |
1828 | u_long fid; | |
1829 | struct hfs_mount_args *args = _args; | |
1830 | ||
1831 | devvp = hfsmp->hfs_devvp; | |
1832 | vcb = HFSTOVCB(hfsmp); | |
1833 | ||
1834 | if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) { | |
1835 | if (args->journal_disable) { | |
1836 | return 0; | |
1837 | } | |
1838 | ||
1839 | arg_flags = args->journal_flags; | |
1840 | arg_tbufsz = args->journal_tbuffer_size; | |
1841 | } | |
1842 | ||
1843 | fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork); | |
1844 | if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) { | |
1845 | printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n", | |
1846 | jib_fork.cf_extents[0].startBlock); | |
1847 | vcb->vcbAtrb &= ~kHFSVolumeJournaledMask; | |
1848 | return 0; | |
1849 | } | |
1850 | hfsmp->hfs_jnlinfoblkid = fid; | |
1851 | ||
1852 | // make sure the journal_info_block begins where we think it should. | |
1853 | if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) { | |
1854 | printf("hfs: The journal_info_block moved (was: %d; is: %d). Fixing up\n", | |
1855 | SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock); | |
1856 | ||
1857 | vcb->vcbJinfoBlock = jib_fork.cf_extents[0].startBlock; | |
1858 | vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock); | |
1859 | } | |
1860 | ||
1861 | ||
1862 | sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_phys_block_size; | |
1863 | retval = (int)buf_meta_bread(devvp, | |
1864 | (daddr64_t)(vcb->hfsPlusIOPosOffset / hfsmp->hfs_phys_block_size + | |
1865 | (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)), | |
1866 | SWAP_BE32(vhp->blockSize), NOCRED, &jinfo_bp); | |
1867 | if (retval) { | |
1868 | printf("hfs: can't read journal info block. disabling journaling.\n"); | |
1869 | vcb->vcbAtrb &= ~kHFSVolumeJournaledMask; | |
1870 | return 0; | |
1871 | } | |
1872 | ||
1873 | jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp); | |
1874 | jibp->flags = SWAP_BE32(jibp->flags); | |
1875 | jibp->offset = SWAP_BE64(jibp->offset); | |
1876 | jibp->size = SWAP_BE64(jibp->size); | |
1877 | ||
1878 | fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork); | |
1879 | if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) { | |
1880 | printf("hfs: can't find the journal file! disabling journaling (start: %d)\n", | |
1881 | jfork.cf_extents[0].startBlock); | |
1882 | buf_brelse(jinfo_bp); | |
1883 | vcb->vcbAtrb &= ~kHFSVolumeJournaledMask; | |
1884 | return 0; | |
1885 | } | |
1886 | hfsmp->hfs_jnlfileid = fid; | |
1887 | ||
1888 | // make sure the journal file begins where we think it should. | |
1889 | if ((jibp->offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) { | |
1890 | printf("hfs: The journal file moved (was: %lld; is: %d). Fixing up\n", | |
1891 | (jibp->offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock); | |
1892 | ||
1893 | jibp->offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize; | |
1894 | write_jibp = 1; | |
1895 | } | |
1896 | ||
1897 | // check the size of the journal file. | |
1898 | if (jibp->size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) { | |
1899 | printf("hfs: The journal file changed size! (was %lld; is %lld). Fixing up.\n", | |
1900 | jibp->size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize); | |
1901 | ||
1902 | jibp->size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize; | |
1903 | write_jibp = 1; | |
1904 | } | |
1905 | ||
1906 | if (jibp->flags & kJIJournalInFSMask) { | |
1907 | hfsmp->jvp = hfsmp->hfs_devvp; | |
1908 | } else { | |
1909 | printf("hfs: journal not stored in fs! don't know what to do.\n"); | |
1910 | buf_brelse(jinfo_bp); | |
1911 | return EINVAL; | |
1912 | } | |
1913 | ||
1914 | // save this off for the hack-y check in hfs_remove() | |
1915 | hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize); | |
1916 | hfsmp->jnl_size = jibp->size; | |
1917 | ||
1918 | if (jibp->flags & kJIJournalNeedInitMask) { | |
1919 | printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n", | |
1920 | jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, jibp->size); | |
1921 | hfsmp->jnl = journal_create(hfsmp->jvp, | |
1922 | jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, | |
1923 | jibp->size, | |
1924 | devvp, | |
1925 | hfsmp->hfs_phys_block_size, | |
1926 | arg_flags, | |
1927 | arg_tbufsz, | |
1928 | hfs_sync_metadata, hfsmp->hfs_mp); | |
1929 | ||
1930 | // no need to start a transaction here... if this were to fail | |
1931 | // we'd just re-init it on the next mount. | |
1932 | jibp->flags &= ~kJIJournalNeedInitMask; | |
1933 | write_jibp = 1; | |
1934 | ||
1935 | } else { | |
1936 | // | |
1937 | // if we weren't the last person to mount this volume | |
1938 | // then we need to throw away the journal because it | |
1939 | // is likely that someone else mucked with the disk. | |
1940 | // if the journal is empty this is no big deal. if the | |
1941 | // disk is dirty this prevents us from replaying the | |
1942 | // journal over top of changes that someone else made. | |
1943 | // | |
1944 | arg_flags |= JOURNAL_RESET; | |
1945 | ||
1946 | //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n", | |
1947 | // jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, | |
1948 | // jibp->size, SWAP_BE32(vhp->blockSize)); | |
1949 | ||
1950 | hfsmp->jnl = journal_open(hfsmp->jvp, | |
1951 | jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, | |
1952 | jibp->size, | |
1953 | devvp, | |
1954 | hfsmp->hfs_phys_block_size, | |
1955 | arg_flags, | |
1956 | arg_tbufsz, | |
1957 | hfs_sync_metadata, hfsmp->hfs_mp); | |
1958 | } | |
1959 | ||
1960 | ||
1961 | if (write_jibp) { | |
1962 | jibp->flags = SWAP_BE32(jibp->flags); | |
1963 | jibp->offset = SWAP_BE64(jibp->offset); | |
1964 | jibp->size = SWAP_BE64(jibp->size); | |
1965 | ||
1966 | buf_bwrite(jinfo_bp); | |
1967 | } else { | |
1968 | buf_brelse(jinfo_bp); | |
1969 | } | |
1970 | jinfo_bp = NULL; | |
1971 | jibp = NULL; | |
1972 | ||
1973 | //printf("journal @ 0x%x\n", hfsmp->jnl); | |
1974 | ||
1975 | // if we expected the journal to be there and we couldn't | |
1976 | // create it or open it then we have to bail out. | |
1977 | if (hfsmp->jnl == NULL) { | |
1978 | printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval); | |
1979 | return EINVAL; | |
1980 | } | |
1981 | ||
1982 | return 0; | |
1983 | } | |
1984 | ||
1985 | /* | |
1986 | * Calculate the allocation zone for metadata. | |
1987 | * | |
1988 | * This zone includes the following: | |
1989 | * Allocation Bitmap file | |
1990 | * Overflow Extents file | |
1991 | * Journal file | |
1992 | * Quota files | |
1993 | * Clustered Hot files | |
1994 | * Catalog file | |
1995 | * | |
1996 | * METADATA ALLOCATION ZONE | |
1997 | * ____________________________________________________________________________ | |
1998 | * | | | | | | | | |
1999 | * | BM | JF | OEF | CATALOG |---> | HOT FILES | | |
2000 | * |____|____|_____|_______________|______________________________|___________| | |
2001 | * | |
2002 | * <------------------------------- N * 128 MB -------------------------------> | |
2003 | * | |
2004 | */ | |
2005 | #define GIGABYTE (u_int64_t)(1024*1024*1024) | |
2006 | ||
2007 | #define OVERFLOW_DEFAULT_SIZE (4*1024*1024) | |
2008 | #define OVERFLOW_MAXIMUM_SIZE (128*1024*1024) | |
2009 | #define JOURNAL_DEFAULT_SIZE (8*1024*1024) | |
2010 | #define JOURNAL_MAXIMUM_SIZE (512*1024*1024) | |
2011 | #define HOTBAND_MINIMUM_SIZE (10*1024*1024) | |
2012 | #define HOTBAND_MAXIMUM_SIZE (512*1024*1024) | |
2013 | ||
2014 | static void | |
2015 | hfs_metadatazone_init(struct hfsmount *hfsmp) | |
2016 | { | |
2017 | ExtendedVCB *vcb; | |
2018 | u_int64_t fs_size; | |
2019 | u_int64_t zonesize; | |
2020 | u_int64_t temp; | |
2021 | u_int64_t filesize; | |
2022 | u_int32_t blk; | |
2023 | int items; | |
2024 | ||
2025 | vcb = HFSTOVCB(hfsmp); | |
2026 | fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->totalBlocks; | |
2027 | ||
2028 | /* | |
2029 | * For volumes less than 10 GB, don't bother. | |
2030 | */ | |
2031 | if (fs_size < ((u_int64_t)10 * GIGABYTE)) | |
2032 | return; | |
2033 | /* | |
2034 | * Skip non-journaled volumes as well. | |
2035 | */ | |
2036 | if (hfsmp->jnl == NULL) | |
2037 | return; | |
2038 | ||
2039 | /* | |
2040 | * Start with allocation bitmap (a fixed size). | |
2041 | */ | |
2042 | zonesize = roundup(vcb->totalBlocks / 8, vcb->vcbVBMIOSize); | |
2043 | ||
2044 | /* | |
2045 | * Overflow Extents file gets 4 MB per 100 GB. | |
2046 | */ | |
2047 | items = fs_size / ((u_int64_t)100 * GIGABYTE); | |
2048 | filesize = (u_int64_t)(items + 1) * OVERFLOW_DEFAULT_SIZE; | |
2049 | if (filesize > OVERFLOW_MAXIMUM_SIZE) | |
2050 | filesize = OVERFLOW_MAXIMUM_SIZE; | |
2051 | zonesize += filesize; | |
2052 | hfsmp->hfs_overflow_maxblks = filesize / vcb->blockSize; | |
2053 | ||
2054 | /* | |
2055 | * Plan for at least 8 MB of journal for each | |
2056 | * 100 GB of disk space (up to a 512 MB). | |
2057 | */ | |
2058 | items = fs_size / ((u_int64_t)100 * GIGABYTE); | |
2059 | filesize = (u_int64_t)(items + 1) * JOURNAL_DEFAULT_SIZE; | |
2060 | if (filesize > JOURNAL_MAXIMUM_SIZE) | |
2061 | filesize = JOURNAL_MAXIMUM_SIZE; | |
2062 | zonesize += filesize; | |
2063 | ||
2064 | /* | |
2065 | * Catalog file gets 10 MB per 1 GB. | |
2066 | * | |
2067 | * How about considering the current catalog size (used nodes * node size) | |
2068 | * and the current file data size to help estimate the required | |
2069 | * catalog size. | |
2070 | */ | |
2071 | filesize = MIN((fs_size / 1024) * 10, GIGABYTE); | |
2072 | hfsmp->hfs_catalog_maxblks = filesize / vcb->blockSize; | |
2073 | zonesize += filesize; | |
2074 | ||
2075 | /* | |
2076 | * Add space for hot file region. | |
2077 | * | |
2078 | * ...for now, use 5 MB per 1 GB (0.5 %) | |
2079 | */ | |
2080 | filesize = (fs_size / 1024) * 5; | |
2081 | if (filesize > HOTBAND_MAXIMUM_SIZE) | |
2082 | filesize = HOTBAND_MAXIMUM_SIZE; | |
2083 | else if (filesize < HOTBAND_MINIMUM_SIZE) | |
2084 | filesize = HOTBAND_MINIMUM_SIZE; | |
2085 | /* | |
2086 | * Calculate user quota file requirements. | |
2087 | */ | |
2088 | items = QF_USERS_PER_GB * (fs_size / GIGABYTE); | |
2089 | if (items < QF_MIN_USERS) | |
2090 | items = QF_MIN_USERS; | |
2091 | else if (items > QF_MAX_USERS) | |
2092 | items = QF_MAX_USERS; | |
2093 | if (!powerof2(items)) { | |
2094 | int x = items; | |
2095 | items = 4; | |
2096 | while (x>>1 != 1) { | |
2097 | x = x >> 1; | |
2098 | items = items << 1; | |
2099 | } | |
2100 | } | |
2101 | filesize += (items + 1) * sizeof(struct dqblk); | |
2102 | /* | |
2103 | * Calculate group quota file requirements. | |
2104 | * | |
2105 | */ | |
2106 | items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE); | |
2107 | if (items < QF_MIN_GROUPS) | |
2108 | items = QF_MIN_GROUPS; | |
2109 | else if (items > QF_MAX_GROUPS) | |
2110 | items = QF_MAX_GROUPS; | |
2111 | if (!powerof2(items)) { | |
2112 | int x = items; | |
2113 | items = 4; | |
2114 | while (x>>1 != 1) { | |
2115 | x = x >> 1; | |
2116 | items = items << 1; | |
2117 | } | |
2118 | } | |
2119 | filesize += (items + 1) * sizeof(struct dqblk); | |
2120 | zonesize += filesize; | |
2121 | ||
2122 | /* | |
2123 | * Round up entire zone to a bitmap block's worth. | |
2124 | * The extra space goes to the catalog file and hot file area. | |
2125 | */ | |
2126 | temp = zonesize; | |
2127 | zonesize = roundup(zonesize, vcb->vcbVBMIOSize * 8 * vcb->blockSize); | |
2128 | temp = zonesize - temp; /* temp has extra space */ | |
2129 | filesize += temp / 3; | |
2130 | hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize; | |
2131 | ||
2132 | hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize; | |
2133 | ||
2134 | /* Convert to allocation blocks. */ | |
2135 | blk = zonesize / vcb->blockSize; | |
2136 | ||
2137 | /* The default metadata zone location is at the start of volume. */ | |
2138 | hfsmp->hfs_metazone_start = 1; | |
2139 | hfsmp->hfs_metazone_end = blk - 1; | |
2140 | ||
2141 | /* The default hotfile area is at the end of the zone. */ | |
2142 | hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize); | |
2143 | hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end; | |
2144 | hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp); | |
2145 | #if 0 | |
2146 | printf("HFS: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end); | |
2147 | printf("HFS: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end); | |
2148 | printf("HFS: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks); | |
2149 | #endif | |
2150 | hfsmp->hfs_flags |= HFS_METADATA_ZONE; | |
2151 | } | |
2152 | ||
2153 | ||
2154 | static u_int32_t | |
2155 | hfs_hotfile_freeblocks(struct hfsmount *hfsmp) | |
2156 | { | |
2157 | ExtendedVCB *vcb = HFSTOVCB(hfsmp); | |
2158 | int lockflags; | |
2159 | int freeblocks; | |
2160 | ||
2161 | lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK); | |
2162 | freeblocks = MetaZoneFreeBlocks(vcb); | |
2163 | hfs_systemfile_unlock(hfsmp, lockflags); | |
2164 | ||
2165 | /* Minus Extents overflow file reserve. */ | |
2166 | freeblocks -= | |
2167 | hfsmp->hfs_overflow_maxblks - VTOF(hfsmp->hfs_extents_vp)->ff_blocks; | |
2168 | /* Minus catalog file reserve. */ | |
2169 | freeblocks -= | |
2170 | hfsmp->hfs_catalog_maxblks - VTOF(hfsmp->hfs_catalog_vp)->ff_blocks; | |
2171 | if (freeblocks < 0) | |
2172 | freeblocks = 0; | |
2173 | ||
2174 | return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks); | |
2175 | } | |
2176 | ||
2177 | /* | |
2178 | * Determine if a file is a "virtual" metadata file. | |
2179 | * This includes journal and quota files. | |
2180 | */ | |
2181 | __private_extern__ | |
2182 | int | |
2183 | hfs_virtualmetafile(struct cnode *cp) | |
2184 | { | |
2185 | char * filename; | |
2186 | ||
2187 | ||
2188 | if (cp->c_parentcnid != kHFSRootFolderID) | |
2189 | return (0); | |
2190 | ||
2191 | filename = cp->c_desc.cd_nameptr; | |
2192 | if (filename == NULL) | |
2193 | return (0); | |
2194 | ||
2195 | if ((strcmp(filename, ".journal") == 0) || | |
2196 | (strcmp(filename, ".journal_info_block") == 0) || | |
2197 | (strcmp(filename, ".quota.user") == 0) || | |
2198 | (strcmp(filename, ".quota.group") == 0) || | |
2199 | (strcmp(filename, ".hotfiles.btree") == 0)) | |
2200 | return (1); | |
2201 | ||
2202 | return (0); | |
2203 | } | |
2204 | ||
2205 | ||
2206 | __private_extern__ | |
2207 | int | |
2208 | hfs_start_transaction(struct hfsmount *hfsmp) | |
2209 | { | |
2210 | int ret; | |
2211 | ||
2212 | if (hfsmp->jnl == NULL || journal_owner(hfsmp->jnl) != current_thread()) { | |
2213 | lck_rw_lock_shared(&hfsmp->hfs_global_lock); | |
2214 | } | |
2215 | ||
2216 | if (hfsmp->jnl) { | |
2217 | ret = journal_start_transaction(hfsmp->jnl); | |
2218 | if (ret == 0) { | |
2219 | OSAddAtomic(1, &hfsmp->hfs_global_lock_nesting); | |
2220 | } | |
2221 | } else { | |
2222 | ret = 0; | |
2223 | } | |
2224 | ||
2225 | if (ret != 0) { | |
2226 | lck_rw_done(&hfsmp->hfs_global_lock); | |
2227 | } | |
2228 | ||
2229 | return ret; | |
2230 | } | |
2231 | ||
2232 | __private_extern__ | |
2233 | int | |
2234 | hfs_end_transaction(struct hfsmount *hfsmp) | |
2235 | { | |
2236 | int need_unlock=0, ret; | |
2237 | ||
2238 | if ( hfsmp->jnl == NULL | |
2239 | || ( journal_owner(hfsmp->jnl) == current_thread() | |
2240 | && (OSAddAtomic(-1, &hfsmp->hfs_global_lock_nesting) == 1)) ) { | |
2241 | ||
2242 | need_unlock = 1; | |
2243 | } | |
2244 | ||
2245 | if (hfsmp->jnl) { | |
2246 | ret = journal_end_transaction(hfsmp->jnl); | |
2247 | } else { | |
2248 | ret = 0; | |
2249 | } | |
2250 | ||
2251 | if (need_unlock) { | |
2252 | lck_rw_done(&hfsmp->hfs_global_lock); | |
2253 | } | |
2254 | ||
2255 | return ret; | |
2256 | } |