1/*
2 * Copyright (c) 2000-2003, 2005, 2007-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "SRuntime.h"
25#include "Scavenger.h"
26#include "../cache.h"
27
28
29
30extern Cache_t fscache;
31
32
33static OSStatus  ReadFragmentedBlock (SFCB *file, UInt32 blockNum, BlockDescriptor *block);
34static OSStatus  WriteFragmentedBlock( 	SFCB *file,
35										BlockDescriptor *block,
36										int age,
37										uint32_t writeOptions );
38static OSStatus  ReleaseFragmentedBlock (SFCB *file, BlockDescriptor *block, int age);
39
40
41void
42InitBlockCache(SVCB *volume)
43{
44	volume->vcbBlockCache = (void *) &fscache;
45}
46
47
48/*
49 *  kGetBlock
50 *  kForceReadBlock
51 *  kGetEmptyBlock
52 *  kSkipEndianSwap
53 */
54OSStatus
55GetVolumeBlock (SVCB *volume, UInt64 blockNum, GetBlockOptions options, BlockDescriptor *block)
56{
57	UInt32  blockSize;
58	SInt64  offset;
59	UInt16  signature;
60	OSStatus result;
61	Buf_t *   buffer;
62	Cache_t * cache;
63
64	buffer = NULL;
65	cache  = (Cache_t *) volume->vcbBlockCache;
66	blockSize = 512;
67
68	offset = (SInt64) ((UInt64) blockNum) << kSectorShift;
69
70	result = CacheRead (cache, offset, blockSize, &buffer);
71
72	if (result == 0) {
73		block->blockHeader = buffer;
74		block->buffer = buffer->Buffer;
75		block->blockNum = blockNum;
76		block->blockSize = blockSize;
77		block->blockReadFromDisk = 0;
78		block->fragmented = 0;
79	} else {
80		block->blockHeader = NULL;
81		block->buffer = NULL;
82	}
83
84	if (!(options & kSkipEndianSwap) && (result == 0)) {
85		HFSMasterDirectoryBlock *mdb;
86
87		mdb = (HFSMasterDirectoryBlock *)block->buffer;
88		signature = SWAP_BE16(mdb->drSigWord);
89		if (signature == kHFSPlusSigWord || signature == kHFSXSigWord)
90			SWAP_HFSPLUSVH(block->buffer);
91		else if (signature == kHFSSigWord)
92			SWAP_HFSMDB(block->buffer);
93	}
94	return (result);
95}
96
97
98/*
99 *  kReleaseBlock
100 *  kForceWriteBlock
101 *  kMarkBlockDirty
102 *  kTrashBlock
103 *  kSkipEndianSwap
104 */
105OSStatus
106ReleaseVolumeBlock (SVCB *volume, BlockDescriptor *block, ReleaseBlockOptions options)
107{
108	OSStatus  result = 0;
109	Cache_t * cache;
110	Buf_t *   buffer;
111	int       age;
112	UInt16  signature;
113
114	cache  = (Cache_t *) volume->vcbBlockCache;
115	buffer = (Buf_t *) block->blockHeader;
116	age    = ((options & kTrashBlock) != 0);
117
118	/*
119	 * Always leave the blocks in the cache in big endian
120	 */
121	if (!(options & kSkipEndianSwap)) {
122		signature = ((HFSMasterDirectoryBlock *)block->buffer)->drSigWord;
123		if (signature == kHFSPlusSigWord || signature == kHFSXSigWord)
124			SWAP_HFSPLUSVH(block->buffer);
125		else if (signature == kHFSSigWord)
126			SWAP_HFSMDB(block->buffer);
127	}
128
129	if (options & (kMarkBlockDirty | kForceWriteBlock)) {
130		result = CacheWrite(cache, buffer, age, 0);
131	} else { /* not dirty */
132		result = CacheRelease (cache, buffer, age);
133	}
134	return (result);
135}
136
137
138/*
139 *  kGetBlock
140 *  kForceReadBlock
141 *  kGetEmptyBlock
142 */
143OSStatus
144GetFileBlock (SFCB *file, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block)
145{
146	UInt64	diskBlock;
147	UInt32	contiguousBytes;
148	SInt64  offset;
149
150	OSStatus result;
151	Buf_t *   buffer;
152	Cache_t * cache;
153
154	buffer = NULL;
155	block->buffer = NULL;
156	block->blockHeader = NULL;
157	cache  = (Cache_t *)file->fcbVolume->vcbBlockCache;
158
159	/* Map file block to volume block */
160	result = MapFileBlockC(file->fcbVolume, file, file->fcbBlockSize,
161			(((UInt64)blockNum * (UInt64)file->fcbBlockSize) >> kSectorShift),
162			&diskBlock, &contiguousBytes);
163	if (result) return (result);
164
165	if (contiguousBytes < file->fcbBlockSize)
166		return ( ReadFragmentedBlock(file, blockNum, block) );
167
168	offset = (SInt64) ((UInt64) diskBlock) << kSectorShift;
169
170	result = CacheRead (cache, offset, file->fcbBlockSize, &buffer);
171	if (result)  return (result);
172
173	block->blockHeader = buffer;
174	block->buffer = buffer->Buffer;
175	block->blockNum = blockNum;
176	block->blockSize = file->fcbBlockSize;
177	block->blockReadFromDisk = 0;
178	block->fragmented = 0;
179
180	return (noErr);
181}
182
183
184/*
185 *  kReleaseBlock
186 *  kForceWriteBlock
187 *  kMarkBlockDirty
188 *  kTrashBlock
189 */
190OSStatus
191ReleaseFileBlock (SFCB *file, BlockDescriptor *block, ReleaseBlockOptions options)
192{
193	OSStatus  result = 0;
194	Cache_t * cache;
195	Buf_t *   buffer;
196	int       age;
197	uint32_t  writeOptions = 0;
198
199	cache  = (Cache_t *)file->fcbVolume->vcbBlockCache;
200	buffer = (Buf_t *) block->blockHeader;
201	age    = ((options & kTrashBlock) != 0);
202
203	if ( (options & kForceWriteBlock) == 0 )
204		/* only write if we're forced to */
205		writeOptions |= kLazyWrite;
206
207	if (options & (kMarkBlockDirty | kForceWriteBlock)) {
208		if (block->fragmented)
209			result = WriteFragmentedBlock(file, block, age, writeOptions);
210		else
211			result = CacheWrite(cache, buffer, age, writeOptions);
212	} else { /* not dirty */
213
214		if (block->fragmented)
215			result = ReleaseFragmentedBlock(file, block, age);
216		else
217			result = CacheRelease (cache, buffer, age);
218	}
219	return (result);
220}
221
222
223/*
224 *
225 */
226OSStatus
227SetFileBlockSize (SFCB *file, ByteCount blockSize)
228{
229	file->fcbBlockSize = blockSize;
230
231	return (0);
232}
233
234
235/*
236 * Read a block that is fragmented across 2 or more allocation blocks
237 *
238 *  - a block descriptor buffer is allocated here
239 *  - the blockHeader field holds a list of Buf_t buffers.
240 *  - the fragmented flag is set
241 */
242static OSStatus
243ReadFragmentedBlock (SFCB *file, UInt32 blockNum, BlockDescriptor *block)
244{
245	UInt64	sector;
246	UInt32	fragSize, blockSize;
247	UInt64  fileOffset;
248	SInt64  diskOffset;
249	SVCB *  volume;
250	int     i, maxFrags;
251	OSStatus result;
252	Buf_t **   bufs;   /* list of Buf_t pointers */
253	Cache_t * cache;
254	char *	buffer;
255
256	volume = file->fcbVolume;
257	cache  = (Cache_t *)volume->vcbBlockCache;
258
259	blockSize = file->fcbBlockSize;
260	maxFrags = blockSize / volume->vcbBlockSize;
261	fileOffset = (UInt64)blockNum * (UInt64)blockSize;
262
263	buffer = (char *) AllocateMemory(blockSize);
264	bufs = (Buf_t **) AllocateClearMemory(maxFrags * sizeof(Buf_t *));
265	if (buffer == NULL || bufs == NULL) {
266		result = memFullErr;
267		return (result);
268	}
269
270	block->buffer = buffer;
271	block->blockHeader = bufs;
272	block->blockNum = blockNum;
273	block->blockSize = blockSize;
274	block->blockReadFromDisk = false;
275	block->fragmented = true;
276
277	for (i = 0; (i < maxFrags) && (blockSize > 0); ++i) {
278		result = MapFileBlockC (volume, file, blockSize,
279					fileOffset >> kSectorShift,
280					&sector, &fragSize);
281		if (result) goto ErrorExit;
282
283		diskOffset = (SInt64) (sector) << kSectorShift;
284		result = CacheRead (cache, diskOffset, fragSize, &bufs[i]);
285		if (result) goto ErrorExit;
286
287		if (bufs[i]->Length != fragSize) {
288			plog("ReadFragmentedBlock: cache failure (Length != fragSize)\n");
289			result = -1;
290			goto ErrorExit;
291		}
292
293		CopyMemory(bufs[i]->Buffer, buffer, fragSize);
294		buffer     += fragSize;
295		fileOffset += fragSize;
296		blockSize  -= fragSize;
297	}
298
299	return (noErr);
300
301ErrorExit:
302	i = 0;
303	while (bufs[i] != NULL) {
304		(void) CacheRelease (cache, bufs[i], true);
305		++i;
306	}
307
308	DisposeMemory(block->buffer);
309	DisposeMemory(block->blockHeader);
310
311	block->blockHeader = NULL;
312	block->buffer = NULL;
313
314	return (result);
315}
316
317
318/*
319 * Write a block that is fragmented across 2 or more allocation blocks
320 *
321 */
322static OSStatus
323WriteFragmentedBlock( SFCB *file, BlockDescriptor *block, int age, uint32_t writeOptions )
324{
325	Cache_t * cache;
326	Buf_t **  bufs;  /* list of Buf_t pointers */
327	char *	  buffer;
328	char *    bufEnd;
329	UInt32	  fragSize;
330	OSStatus  result;
331	int i = 0;
332
333	result = 0;
334	cache  = (Cache_t *) file->fcbVolume->vcbBlockCache;
335	bufs   = (Buf_t **) block->blockHeader;
336	buffer = (char *) block->buffer;
337	bufEnd = buffer + file->fcbBlockSize;
338
339	if (bufs == NULL) {
340		plog("WriteFragmentedBlock: NULL bufs list!\n");
341		return (-1);
342	}
343
344	while ((bufs[i] != NULL) && (buffer < bufEnd)) {
345		fragSize = bufs[i]->Length;
346
347		/* copy data for this fragment */
348		CopyMemory(buffer, bufs[i]->Buffer, fragSize);
349
350		/* write it back to cache */
351		result = CacheWrite(cache, bufs[i], age, writeOptions);
352		if (result) break;
353
354		buffer += fragSize;
355		++i;
356	}
357
358	DisposeMemory(block->buffer);
359	DisposeMemory(block->blockHeader);
360
361	block->buffer = NULL;
362	block->blockHeader = NULL;
363	block->fragmented = false;
364
365	return (result);
366}
367
368
369/*
370 * Release a block that is fragmented across 2 or more allocation blocks
371 *
372 */
373static OSStatus
374ReleaseFragmentedBlock (SFCB *file, BlockDescriptor *block, int age)
375{
376	Cache_t * cache;
377	Buf_t **   bufs;  /* list of Buf_t pointers */
378	char	*buffer;
379	char	*bufEnd;
380	UInt32	fragSize;
381	int i = 0;
382
383	cache = (Cache_t *)file->fcbVolume->vcbBlockCache;
384	bufs  = (Buf_t **) block->blockHeader;
385
386	if (bufs == NULL) {
387		plog("ReleaseFragmentedBlock: NULL buf list!\n");
388		return (-1);
389	}
390
391	buffer = (char*)block->buffer;
392	bufEnd = buffer + file->fcbBlockSize;
393
394	while (bufs[i] != NULL && (buffer < bufEnd)) {
395		fragSize = bufs[i]->Length;
396		buffer += fragSize;
397		(void) CacheRelease (cache, bufs[i], true);
398		++i;
399	}
400
401	DisposeMemory(block->buffer);
402	DisposeMemory(block->blockHeader);
403
404	block->buffer = NULL;
405	block->blockHeader = NULL;
406	block->fragmented = false;
407
408	return (noErr);
409}
410
411