]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* @(#)hfs_btreeio.c | |
23 | * | |
24 | * (c) 1998, 2000 Apple Computer, Inc. All Rights Reserved | |
25 | * | |
26 | * hfs_btreeio.c -- I/O Routines for the HFS B-tree files. | |
27 | * | |
28 | * HISTORY | |
29 | * 15-Feb-2000 Don Brady Added ClearBTNodes. | |
30 | * 16-Jul-1998 Don Brady In ExtendBtreeFile force all b-tree nodes to be contiguous on disk. | |
31 | * 4-Jun-1998 Pat Dirks Changed to do all B*-Tree writes synchronously (FORCESYNCBTREEWRITES = 1) | |
32 | * 18-apr-1998 Don Brady Call brelse on bread failure. | |
33 | * 17-Apr-1998 Pat Dirks Fixed ReleaseBTreeBlock to not call brelse when bwrite or bdwrite is called. | |
34 | * 13-apr-1998 Don Brady Add ExtendBTreeFile routine (from BTreeWrapper.c). | |
35 | * 26-mar-1998 Don Brady SetBTreeBlockSize was incorrectly excluding 512 byte blockSize. | |
36 | * 18-feb-1998 Don Brady Initially created file. | |
37 | * | |
38 | */ | |
39 | ||
40 | #include <sys/param.h> | |
41 | #include <sys/systm.h> | |
42 | #include <sys/buf.h> | |
43 | #include <sys/mount.h> | |
44 | #include <sys/vnode.h> | |
45 | ||
46 | ||
47 | #include "hfs.h" | |
48 | #include "hfs_dbg.h" | |
49 | #include "hfs_endian.h" | |
50 | ||
51 | #include "hfscommon/headers/FileMgrInternal.h" | |
52 | #include "hfscommon/headers/BTreesPrivate.h" | |
53 | ||
54 | #define FORCESYNCBTREEWRITES 0 | |
55 | ||
56 | static OSStatus FlushAlternate( ExtendedVCB *vcb ); | |
57 | ||
58 | static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount); | |
59 | ||
60 | ||
61 | OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount) | |
62 | { | |
63 | BTreeControlBlockPtr bTreePtr; | |
64 | ||
65 | DBG_ASSERT(vp != NULL); | |
66 | DBG_ASSERT(VTOFCB(vp) != NULL); | |
67 | DBG_ASSERT(VTOFCB(vp)->fcbBTCBPtr != NULL); | |
68 | DBG_ASSERT(blockSize >= kMinNodeSize); | |
69 | if (blockSize > MAXBSIZE ) | |
70 | return (fsBTBadNodeSize); | |
71 | ||
72 | DBG_TREE(("SetBlockSizeProc: blockSize=%ld for file %ld\n", blockSize, H_FILEID(VTOH(vp)))); | |
73 | ||
74 | bTreePtr = (BTreeControlBlockPtr)(VTOH(vp)->fcbBTCBPtr); | |
75 | bTreePtr->nodeSize = blockSize; | |
76 | ||
77 | return (E_NONE); | |
78 | } | |
79 | ||
80 | ||
81 | OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block) | |
82 | { | |
83 | OSStatus retval = E_NONE; | |
84 | struct buf *bp = NULL; | |
85 | ||
86 | if (options & kGetEmptyBlock) | |
87 | bp = getblk (vp, | |
88 | IOBLKNOFORBLK(blockNum, VTOHFS(vp)->hfs_phys_block_size), | |
89 | IOBYTECCNTFORBLK(blockNum, block->blockSize, VTOHFS(vp)->hfs_phys_block_size), | |
90 | 0, | |
91 | 0, | |
92 | BLK_META); | |
93 | else | |
94 | retval = meta_bread(vp, | |
95 | IOBLKNOFORBLK(blockNum, VTOHFS(vp)->hfs_phys_block_size), | |
96 | IOBYTECCNTFORBLK(blockNum, block->blockSize, VTOHFS(vp)->hfs_phys_block_size), | |
97 | NOCRED, | |
98 | &bp); | |
99 | ||
100 | DBG_ASSERT(bp != NULL); | |
101 | DBG_ASSERT(bp->b_data != NULL); | |
102 | DBG_ASSERT(bp->b_bcount == block->blockSize); | |
103 | DBG_ASSERT(bp->b_lblkno == blockNum); | |
104 | ||
105 | if (bp == NULL) | |
106 | retval = -1; //XXX need better error | |
107 | ||
108 | if (retval == E_NONE) { | |
109 | block->blockHeader = bp; | |
110 | block->buffer = bp->b_data + IOBYTEOFFSETFORBLK(bp->b_blkno, VTOHFS(vp)->hfs_phys_block_size); | |
111 | block->blockReadFromDisk = (bp->b_flags & B_CACHE) == 0; /* not found in cache ==> came from disk */ | |
112 | ||
113 | #if BYTE_ORDER == LITTLE_ENDIAN | |
114 | /* Endian swap B-Tree node (only if it's a valid block) */ | |
115 | if (!(options & kGetEmptyBlock)) { | |
116 | /* This happens when we first open the b-tree, we might not have all the node data on hand */ | |
117 | if ((((BTNodeDescriptor *)block->buffer)->kind == kBTHeaderNode) && | |
118 | (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize != bp->b_bcount) && | |
119 | (SWAP_BE16 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize) != bp->b_bcount)) { | |
120 | ||
121 | /* Don't swap the descriptors at all, we don't care (this block will be invalidated) */ | |
122 | SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), H_FILEID(VTOH(vp)), 3); | |
123 | ||
124 | /* The node needs swapping */ | |
125 | } else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) == 0x0e00) { | |
126 | SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), H_FILEID(VTOH(vp)), 0); | |
127 | #if 0 | |
128 | /* The node is not already in native byte order, hence corrupt */ | |
129 | } else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) != 0x000e) { | |
130 | panic ("%s Corrupt B-Tree node detected!\n", "GetBTreeBlock:"); | |
131 | #endif | |
132 | } | |
133 | } | |
134 | #endif | |
135 | } else { | |
136 | if (bp) | |
137 | brelse(bp); | |
138 | block->blockHeader = NULL; | |
139 | block->buffer = NULL; | |
140 | } | |
141 | ||
142 | return (retval); | |
143 | } | |
144 | ||
145 | ||
146 | OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options) | |
147 | { | |
148 | OSStatus retval = E_NONE; | |
149 | struct buf *bp = NULL; | |
150 | ||
151 | bp = (struct buf *) blockPtr->blockHeader; | |
152 | ||
153 | if (bp == NULL) { | |
154 | DBG_TREE(("ReleaseBlockProc: blockHeader is zero!\n")); | |
155 | retval = -1; | |
156 | goto exit; | |
157 | } | |
158 | ||
159 | if (options & kTrashBlock) { | |
160 | bp->b_flags |= B_INVAL; | |
161 | brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */ | |
162 | } else { | |
163 | if (options & kForceWriteBlock) { | |
164 | bp->b_flags |= B_DIRTY; | |
165 | retval = VOP_BWRITE(bp); | |
166 | } else if (options & kMarkBlockDirty) { | |
167 | bp->b_flags |= B_DIRTY; | |
168 | #if FORCESYNCBTREEWRITES | |
169 | VOP_BWRITE(bp); | |
170 | #else | |
171 | if (options & kLockTransaction) { | |
172 | ||
173 | /* | |
174 | * | |
175 | * Set the B_LOCKED flag and unlock the buffer, causing brelse to move | |
176 | * the buffer onto the LOCKED free list. This is necessary, otherwise | |
177 | * getnewbuf() would try to reclaim the buffers using bawrite, which | |
178 | * isn't going to work. | |
179 | * | |
180 | */ | |
181 | bp->b_flags |= B_LOCKED; | |
182 | }; | |
183 | bdwrite(bp); | |
184 | ||
185 | #endif | |
186 | } else { | |
187 | brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */ | |
188 | }; | |
189 | }; | |
190 | ||
191 | exit: | |
192 | return (retval); | |
193 | } | |
194 | ||
195 | ||
196 | OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF) | |
197 | { | |
198 | #pragma unused (maxEOF) | |
199 | ||
200 | OSStatus retval; | |
201 | UInt64 actualBytesAdded; | |
202 | UInt64 bytesToAdd; | |
203 | UInt32 extendFlags; | |
204 | BTreeInfoRec btInfo; | |
205 | ExtendedVCB *vcb; | |
206 | FCB *filePtr; | |
207 | struct proc *p = NULL; | |
208 | ||
209 | ||
210 | filePtr = GetFileControlBlock(vp); | |
211 | ||
212 | if ( minEOF > filePtr->fcbEOF ) | |
213 | { | |
214 | bytesToAdd = minEOF - filePtr->fcbEOF; | |
215 | ||
216 | if (bytesToAdd < filePtr->fcbClmpSize) | |
217 | bytesToAdd = filePtr->fcbClmpSize; //XXX why not always be a mutiple of clump size? | |
218 | } | |
219 | else | |
220 | { | |
221 | DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!")); | |
222 | return -1; | |
223 | } | |
224 | ||
225 | vcb = FCBTOVCB(filePtr); | |
226 | ||
227 | /* | |
228 | * The Extents B-tree can't have overflow extents. ExtendFileC will | |
229 | * return an error if an attempt is made to extend the Extents B-tree | |
230 | * when the resident extents are exhausted. | |
231 | */ | |
232 | /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */ | |
233 | if(H_FILEID(filePtr) != kHFSExtentsFileID) | |
234 | { | |
235 | p = current_proc(); | |
236 | /* lock extents b-tree (also protects volume bitmap) */ | |
237 | retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p); | |
238 | if (retval) | |
239 | return (retval); | |
240 | } | |
241 | ||
242 | (void) BTGetInformation(filePtr, 0, &btInfo); | |
243 | ||
244 | /* | |
245 | * The b-tree code expects nodes to be contiguous. So when | |
246 | * the allocation block size is less than the b-tree node | |
247 | * size, we need to force disk allocations to be contiguous. | |
248 | */ | |
249 | if (vcb->blockSize >= btInfo.nodeSize) { | |
250 | extendFlags = 0; | |
251 | } else { | |
252 | /* Ensure that all b-tree nodes are contiguous on disk */ | |
253 | extendFlags = kEFAllMask | kEFContigMask; | |
254 | } | |
255 | ||
256 | retval = ExtendFileC(vcb, filePtr, bytesToAdd, extendFlags, &actualBytesAdded ); | |
257 | ||
258 | if(H_FILEID(filePtr) != kHFSExtentsFileID) | |
259 | (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p); | |
260 | ||
261 | if (retval) | |
262 | return (retval); | |
263 | ||
264 | if (actualBytesAdded < bytesToAdd) | |
265 | DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!")); | |
266 | ||
267 | filePtr->fcbEOF = filePtr->fcbPLen; | |
268 | ||
269 | retval = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded); | |
270 | if (retval) | |
271 | return (retval); | |
272 | ||
273 | /* | |
274 | * Update the Alternate MDB or Alternate VolumeHeader | |
275 | */ | |
276 | if ((H_FILEID(filePtr) == kHFSExtentsFileID) || | |
277 | (H_FILEID(filePtr) == kHFSCatalogFileID) || | |
278 | (H_FILEID(filePtr) == kHFSAttributesFileID) | |
279 | ) { | |
280 | MarkVCBDirty( vcb ); | |
281 | if (vcb->vcbSigWord == kHFSPlusSigWord) { | |
282 | retval = hfs_flushvolumeheader(VCBTOHFS(vcb), 0); | |
283 | } else { | |
284 | retval = hfs_flushMDB(VCBTOHFS(vcb), 0); | |
285 | } | |
286 | if (retval == 0) { | |
287 | retval = FlushAlternate(vcb); | |
288 | } | |
289 | } | |
290 | ||
291 | return retval; | |
292 | } | |
293 | ||
294 | ||
295 | static OSStatus | |
296 | FlushAlternate( ExtendedVCB *vcb ) | |
297 | { | |
298 | void *maindata; | |
299 | void *altdata; | |
300 | int result; | |
301 | ||
302 | /* Get the main MDB/VolumeHeader block */ | |
303 | result = GetBlock_glue(gbDefault, | |
304 | (vcb->hfsPlusIOPosOffset / kHFSBlockSize) + kMasterDirectoryBlock, | |
305 | (Ptr *)&maindata, kNoFileReference, vcb); | |
306 | if (result) return (result); | |
307 | ||
308 | /* Get the alternate MDB/VolumeHeader block */ | |
309 | result = GetBlock_glue( gbDefault, vcb->altIDSector, | |
310 | (Ptr *)&altdata, kNoFileReference, vcb ); | |
311 | ||
312 | if (result == 0) { | |
313 | bcopy(maindata, altdata, kMDBSize); | |
314 | ||
315 | result = RelBlock_glue( (Ptr)altdata, rbWriteMask ); | |
316 | } | |
317 | ||
318 | (void) RelBlock_glue( (Ptr)maindata, rbFreeMask ); | |
319 | ||
320 | return (result); | |
321 | } | |
322 | ||
323 | ||
324 | /* | |
325 | * Clear out (zero) new b-tree nodes on disk. | |
326 | */ | |
327 | static int | |
328 | ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount) | |
329 | { | |
330 | struct buf *bp = NULL; | |
331 | daddr_t blk; | |
332 | daddr_t blkcnt; | |
333 | ||
334 | blk = offset / blksize; | |
335 | blkcnt = amount / blksize; | |
336 | ||
337 | while (blkcnt > 0) { | |
338 | bp = getblk(vp, blk, blksize, 0, 0, BLK_META); | |
339 | if (bp == NULL) | |
340 | continue; | |
341 | bzero((char *)bp->b_data, blksize); | |
342 | bp->b_flags |= (B_DIRTY | B_AGE); | |
343 | ||
344 | /* wait/yield every 32 blocks so we don't hog all the buffers */ | |
345 | if ((blk % 32) == 0) | |
346 | VOP_BWRITE(bp); | |
347 | else | |
348 | bawrite(bp); | |
349 | --blkcnt; | |
350 | ++blk; | |
351 | } | |
352 | ||
353 | return (0); | |
354 | } |