]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
9bccf70c | 2 | * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. |
1c79356b A |
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 | */ | |
1c79356b A |
22 | |
23 | #include <sys/param.h> | |
24 | #include <sys/systm.h> | |
25 | #include <sys/buf.h> | |
9bccf70c | 26 | #include <sys/kernel.h> |
1c79356b A |
27 | #include <sys/mount.h> |
28 | #include <sys/vnode.h> | |
29 | ||
30 | ||
31 | #include "hfs.h" | |
9bccf70c | 32 | #include "hfs_cnode.h" |
1c79356b A |
33 | #include "hfs_dbg.h" |
34 | #include "hfs_endian.h" | |
35 | ||
36 | #include "hfscommon/headers/FileMgrInternal.h" | |
37 | #include "hfscommon/headers/BTreesPrivate.h" | |
38 | ||
39 | #define FORCESYNCBTREEWRITES 0 | |
40 | ||
1c79356b A |
41 | |
42 | static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount); | |
43 | ||
44 | ||
9bccf70c | 45 | __private_extern__ |
1c79356b A |
46 | OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount) |
47 | { | |
48 | BTreeControlBlockPtr bTreePtr; | |
49 | ||
50 | DBG_ASSERT(vp != NULL); | |
1c79356b A |
51 | DBG_ASSERT(blockSize >= kMinNodeSize); |
52 | if (blockSize > MAXBSIZE ) | |
53 | return (fsBTBadNodeSize); | |
54 | ||
9bccf70c | 55 | bTreePtr = (BTreeControlBlockPtr)VTOF(vp)->fcbBTCBPtr; |
1c79356b A |
56 | bTreePtr->nodeSize = blockSize; |
57 | ||
58 | return (E_NONE); | |
59 | } | |
60 | ||
61 | ||
9bccf70c | 62 | __private_extern__ |
1c79356b A |
63 | OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block) |
64 | { | |
65 | OSStatus retval = E_NONE; | |
66 | struct buf *bp = NULL; | |
67 | ||
9bccf70c A |
68 | if (options & kGetEmptyBlock) |
69 | bp = getblk(vp, blockNum, block->blockSize, 0, 0, BLK_META); | |
70 | else | |
71 | retval = meta_bread(vp, blockNum, block->blockSize, NOCRED, &bp); | |
1c79356b A |
72 | |
73 | DBG_ASSERT(bp != NULL); | |
74 | DBG_ASSERT(bp->b_data != NULL); | |
75 | DBG_ASSERT(bp->b_bcount == block->blockSize); | |
76 | DBG_ASSERT(bp->b_lblkno == blockNum); | |
77 | ||
78 | if (bp == NULL) | |
79 | retval = -1; //XXX need better error | |
80 | ||
81 | if (retval == E_NONE) { | |
82 | block->blockHeader = bp; | |
d52fe63f | 83 | block->buffer = bp->b_data; |
1c79356b A |
84 | block->blockReadFromDisk = (bp->b_flags & B_CACHE) == 0; /* not found in cache ==> came from disk */ |
85 | ||
86 | #if BYTE_ORDER == LITTLE_ENDIAN | |
87 | /* Endian swap B-Tree node (only if it's a valid block) */ | |
88 | if (!(options & kGetEmptyBlock)) { | |
89 | /* This happens when we first open the b-tree, we might not have all the node data on hand */ | |
90 | if ((((BTNodeDescriptor *)block->buffer)->kind == kBTHeaderNode) && | |
91 | (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize != bp->b_bcount) && | |
92 | (SWAP_BE16 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize) != bp->b_bcount)) { | |
93 | ||
94 | /* Don't swap the descriptors at all, we don't care (this block will be invalidated) */ | |
9bccf70c | 95 | SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), VTOC(vp)->c_fileid, 3); |
1c79356b A |
96 | |
97 | /* The node needs swapping */ | |
98 | } else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) == 0x0e00) { | |
9bccf70c | 99 | SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), VTOC(vp)->c_fileid, 0); |
1c79356b A |
100 | #if 0 |
101 | /* The node is not already in native byte order, hence corrupt */ | |
102 | } else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) != 0x000e) { | |
103 | panic ("%s Corrupt B-Tree node detected!\n", "GetBTreeBlock:"); | |
104 | #endif | |
105 | } | |
106 | } | |
107 | #endif | |
108 | } else { | |
109 | if (bp) | |
110 | brelse(bp); | |
111 | block->blockHeader = NULL; | |
112 | block->buffer = NULL; | |
113 | } | |
114 | ||
115 | return (retval); | |
116 | } | |
117 | ||
118 | ||
9bccf70c | 119 | __private_extern__ |
1c79356b A |
120 | OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options) |
121 | { | |
9bccf70c | 122 | extern int bdwrite_internal(struct buf *, int); |
1c79356b A |
123 | OSStatus retval = E_NONE; |
124 | struct buf *bp = NULL; | |
125 | ||
126 | bp = (struct buf *) blockPtr->blockHeader; | |
127 | ||
128 | if (bp == NULL) { | |
1c79356b A |
129 | retval = -1; |
130 | goto exit; | |
131 | } | |
132 | ||
133 | if (options & kTrashBlock) { | |
134 | bp->b_flags |= B_INVAL; | |
135 | brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */ | |
136 | } else { | |
137 | if (options & kForceWriteBlock) { | |
1c79356b A |
138 | retval = VOP_BWRITE(bp); |
139 | } else if (options & kMarkBlockDirty) { | |
1c79356b A |
140 | #if FORCESYNCBTREEWRITES |
141 | VOP_BWRITE(bp); | |
142 | #else | |
9bccf70c A |
143 | if (options & kLockTransaction) { |
144 | /* | |
145 | * | |
146 | * Set the B_LOCKED flag and unlock the buffer, causing brelse to move | |
147 | * the buffer onto the LOCKED free list. This is necessary, otherwise | |
148 | * getnewbuf() would try to reclaim the buffers using bawrite, which | |
149 | * isn't going to work. | |
150 | * | |
151 | */ | |
152 | extern int count_lock_queue __P((void)); | |
153 | /* Don't hog all the buffers... */ | |
154 | if (count_lock_queue() > kMaxLockedMetaBuffers) { | |
155 | hfs_btsync(vp, HFS_SYNCTRANS); | |
156 | /* Rollback sync time to cause a sync on lock release... */ | |
157 | (void) BTSetLastSync(VTOF(vp), time.tv_sec - (kMaxSecsForFsync + 1)); | |
158 | } | |
159 | bp->b_flags |= B_LOCKED; | |
160 | } | |
161 | /* | |
162 | * Delay-write this block. | |
163 | * If the maximum delayed buffers has been exceeded then | |
164 | * free up some buffers and fall back to an asynchronous write. | |
165 | */ | |
166 | if (bdwrite_internal(bp, 1) != 0) { | |
167 | hfs_btsync(vp, 0); | |
168 | /* Rollback sync time to cause a sync on lock release... */ | |
169 | (void) BTSetLastSync(VTOF(vp), time.tv_sec - (kMaxSecsForFsync + 1)); | |
170 | bp->b_flags &= ~B_LOCKED; | |
171 | bawrite(bp); | |
172 | } | |
1c79356b A |
173 | |
174 | #endif | |
175 | } else { | |
176 | brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */ | |
177 | }; | |
178 | }; | |
179 | ||
180 | exit: | |
181 | return (retval); | |
182 | } | |
183 | ||
184 | ||
9bccf70c | 185 | __private_extern__ |
1c79356b A |
186 | OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF) |
187 | { | |
188 | #pragma unused (maxEOF) | |
189 | ||
190 | OSStatus retval; | |
191 | UInt64 actualBytesAdded; | |
192 | UInt64 bytesToAdd; | |
193 | UInt32 extendFlags; | |
9bccf70c A |
194 | u_int32_t startAllocation; |
195 | u_int32_t fileblocks; | |
1c79356b A |
196 | BTreeInfoRec btInfo; |
197 | ExtendedVCB *vcb; | |
198 | FCB *filePtr; | |
199 | struct proc *p = NULL; | |
200 | ||
201 | ||
202 | filePtr = GetFileControlBlock(vp); | |
203 | ||
204 | if ( minEOF > filePtr->fcbEOF ) | |
205 | { | |
206 | bytesToAdd = minEOF - filePtr->fcbEOF; | |
207 | ||
9bccf70c A |
208 | if (bytesToAdd < filePtr->ff_clumpsize) |
209 | bytesToAdd = filePtr->ff_clumpsize; //XXX why not always be a mutiple of clump size? | |
1c79356b A |
210 | } |
211 | else | |
212 | { | |
1c79356b A |
213 | return -1; |
214 | } | |
215 | ||
9bccf70c | 216 | vcb = VTOVCB(vp); |
1c79356b A |
217 | |
218 | /* | |
219 | * The Extents B-tree can't have overflow extents. ExtendFileC will | |
220 | * return an error if an attempt is made to extend the Extents B-tree | |
221 | * when the resident extents are exhausted. | |
222 | */ | |
223 | /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */ | |
9bccf70c | 224 | if(VTOC(vp)->c_fileid != kHFSExtentsFileID) |
1c79356b A |
225 | { |
226 | p = current_proc(); | |
227 | /* lock extents b-tree (also protects volume bitmap) */ | |
228 | retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p); | |
229 | if (retval) | |
230 | return (retval); | |
231 | } | |
232 | ||
233 | (void) BTGetInformation(filePtr, 0, &btInfo); | |
234 | ||
235 | /* | |
9bccf70c | 236 | * The b-tree code expects nodes to be contiguous. So when |
1c79356b | 237 | * the allocation block size is less than the b-tree node |
9bccf70c A |
238 | * size, we need to force disk allocations to be contiguous. |
239 | */ | |
1c79356b A |
240 | if (vcb->blockSize >= btInfo.nodeSize) { |
241 | extendFlags = 0; | |
242 | } else { | |
243 | /* Ensure that all b-tree nodes are contiguous on disk */ | |
244 | extendFlags = kEFAllMask | kEFContigMask; | |
245 | } | |
246 | ||
9bccf70c A |
247 | fileblocks = filePtr->ff_blocks; |
248 | startAllocation = vcb->nextAllocation; | |
1c79356b | 249 | |
9bccf70c | 250 | retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0, extendFlags, &actualBytesAdded); |
1c79356b | 251 | |
9bccf70c A |
252 | /* |
253 | * If a new extent was added then move the roving allocator | |
254 | * reference forward by the current b-tree file size so | |
255 | * there's plenty of room to grow. | |
256 | */ | |
257 | if ((retval == 0) && | |
258 | (vcb->nextAllocation > startAllocation) && | |
259 | ((vcb->nextAllocation + fileblocks) < vcb->totalBlocks)) { | |
260 | vcb->nextAllocation += fileblocks; | |
261 | } | |
262 | ||
263 | if(VTOC(vp)->c_fileid != kHFSExtentsFileID) { | |
264 | /* | |
265 | * Get any extents overflow b-tree changes to disk ASAP! | |
266 | */ | |
267 | if (retval == 0) { | |
268 | (void) BTFlushPath(VTOF(vcb->extentsRefNum)); | |
269 | (void) VOP_FSYNC(vcb->extentsRefNum, NOCRED, MNT_WAIT, p); | |
270 | } | |
271 | (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p); | |
272 | } | |
1c79356b A |
273 | if (retval) |
274 | return (retval); | |
1c79356b | 275 | |
9bccf70c | 276 | filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize; |
1c79356b A |
277 | |
278 | retval = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded); | |
279 | if (retval) | |
280 | return (retval); | |
281 | ||
282 | /* | |
283 | * Update the Alternate MDB or Alternate VolumeHeader | |
284 | */ | |
9bccf70c A |
285 | if ((VTOC(vp)->c_fileid == kHFSExtentsFileID) || |
286 | (VTOC(vp)->c_fileid == kHFSCatalogFileID) || | |
287 | (VTOC(vp)->c_fileid == kHFSAttributesFileID) | |
1c79356b A |
288 | ) { |
289 | MarkVCBDirty( vcb ); | |
9bccf70c | 290 | retval = hfs_flushvolumeheader(VCBTOHFS(vcb), MNT_WAIT, HFS_ALTFLUSH); |
1c79356b A |
291 | } |
292 | ||
293 | return retval; | |
294 | } | |
295 | ||
296 | ||
1c79356b A |
297 | /* |
298 | * Clear out (zero) new b-tree nodes on disk. | |
299 | */ | |
300 | static int | |
301 | ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount) | |
302 | { | |
303 | struct buf *bp = NULL; | |
304 | daddr_t blk; | |
305 | daddr_t blkcnt; | |
306 | ||
307 | blk = offset / blksize; | |
308 | blkcnt = amount / blksize; | |
309 | ||
310 | while (blkcnt > 0) { | |
311 | bp = getblk(vp, blk, blksize, 0, 0, BLK_META); | |
312 | if (bp == NULL) | |
313 | continue; | |
314 | bzero((char *)bp->b_data, blksize); | |
9bccf70c | 315 | bp->b_flags |= B_AGE; |
1c79356b A |
316 | |
317 | /* wait/yield every 32 blocks so we don't hog all the buffers */ | |
318 | if ((blk % 32) == 0) | |
319 | VOP_BWRITE(bp); | |
320 | else | |
321 | bawrite(bp); | |
322 | --blkcnt; | |
323 | ++blk; | |
324 | } | |
325 | ||
326 | return (0); | |
327 | } |