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