1/*
2 * Copyright 2001-2010, Haiku Inc. All rights reserved.
3 * This file may be used under the terms of the MIT License.
4 *
5 * Authors:
6 *		Janito V. Ferreira Filho
7 */
8
9
10#include "DataStream.h"
11
12#include "CachedBlock.h"
13#include "Volume.h"
14
15
16//#define TRACE_EXT2
17#ifdef TRACE_EXT2
18#	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
19#else
20#	define TRACE(x...) ;
21#endif
22#define ERROR(x...)	dprintf("\33[34mext2:\33[0m " x)
23
24
25DataStream::DataStream(Volume* volume, ext2_data_stream* stream,
26	off_t size)
27	:
28	kBlockSize(volume->BlockSize()),
29	kIndirectsPerBlock(kBlockSize / sizeof(uint32)),
30	kIndirectsPerBlock2(kIndirectsPerBlock * kIndirectsPerBlock),
31	kIndirectsPerBlock3(kIndirectsPerBlock2 * kIndirectsPerBlock),
32	kMaxDirect(EXT2_DIRECT_BLOCKS),
33	kMaxIndirect(kMaxDirect + kIndirectsPerBlock),
34	kMaxDoubleIndirect(kMaxIndirect + kIndirectsPerBlock2),
35	fVolume(volume),
36	fStream(stream),
37	fFirstBlock(volume->FirstDataBlock()),
38	fAllocated(0),
39	fAllocatedPos(fFirstBlock),
40	fWaiting(0),
41	fFreeStart(0),
42	fFreeCount(0),
43	fRemovedBlocks(0),
44	fSize(size)
45{
46	fNumBlocks = size == 0 ? 0 : ((size - 1) >> fVolume->BlockShift()) + 1;
47}
48
49
50DataStream::~DataStream()
51{
52}
53
54
55status_t
56DataStream::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
57{
58	uint32 index = offset >> fVolume->BlockShift();
59
60	if (offset >= fSize) {
61		TRACE("FindBlock: offset larger than inode size\n");
62		return B_ENTRY_NOT_FOUND;
63	}
64
65	// TODO: we could return the size of the sparse range, as this might be more
66	// than just a block
67
68	if (index < EXT2_DIRECT_BLOCKS) {
69		// direct blocks
70		block = B_LENDIAN_TO_HOST_INT32(fStream->direct[index]);
71		ASSERT(block != 0);
72		if (_count) {
73			*_count = 1;
74			uint32 nextBlock = block;
75			while (++index < EXT2_DIRECT_BLOCKS
76				&& fStream->direct[index] == ++nextBlock)
77				(*_count)++;
78		}
79	} else if ((index -= EXT2_DIRECT_BLOCKS) < kIndirectsPerBlock) {
80		// indirect blocks
81		CachedBlock cached(fVolume);
82		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
83			fStream->indirect));
84		if (indirectBlocks == NULL)
85			return B_IO_ERROR;
86
87		block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index]);
88		ASSERT(block != 0);
89		if (_count) {
90			*_count = 1;
91			uint32 nextBlock = block;
92			while (++index < kIndirectsPerBlock
93				&& indirectBlocks[index] == ++nextBlock)
94				(*_count)++;
95		}
96	} else if ((index -= kIndirectsPerBlock) < kIndirectsPerBlock2) {
97		// double indirect blocks
98		CachedBlock cached(fVolume);
99		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
100			fStream->double_indirect));
101		if (indirectBlocks == NULL)
102			return B_IO_ERROR;
103
104		uint32 indirectIndex = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index
105			/ kIndirectsPerBlock]);
106		if (indirectIndex == 0) {
107			// a sparse indirect block
108			block = 0;
109		} else {
110			indirectBlocks = (uint32*)cached.SetTo(indirectIndex);
111			if (indirectBlocks == NULL)
112				return B_IO_ERROR;
113
114			block = B_LENDIAN_TO_HOST_INT32(
115				indirectBlocks[index & (kIndirectsPerBlock - 1)]);
116			if (_count) {
117				*_count = 1;
118				uint32 nextBlock = block;
119				while (((++index & (kIndirectsPerBlock - 1)) != 0)
120					&& indirectBlocks[index & (kIndirectsPerBlock - 1)]
121						== ++nextBlock)
122					(*_count)++;
123			}
124		}
125		ASSERT(block != 0);
126	} else if ((index -= kIndirectsPerBlock2) < kIndirectsPerBlock3) {
127		// triple indirect blocks
128		CachedBlock cached(fVolume);
129		uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
130			fStream->triple_indirect));
131		if (indirectBlocks == NULL)
132			return B_IO_ERROR;
133
134		uint32 indirectIndex = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index
135			/ kIndirectsPerBlock2]);
136		if (indirectIndex == 0) {
137			// a sparse indirect block
138			block = 0;
139		} else {
140			indirectBlocks = (uint32*)cached.SetTo(indirectIndex);
141			if (indirectBlocks == NULL)
142				return B_IO_ERROR;
143
144			indirectIndex = B_LENDIAN_TO_HOST_INT32(
145				indirectBlocks[(index / kIndirectsPerBlock) & (kIndirectsPerBlock - 1)]);
146			if (indirectIndex == 0) {
147				// a sparse indirect block
148				block = 0;
149			} else {
150				indirectBlocks = (uint32*)cached.SetTo(indirectIndex);
151				if (indirectBlocks == NULL)
152					return B_IO_ERROR;
153
154				block = B_LENDIAN_TO_HOST_INT32(
155					indirectBlocks[index & (kIndirectsPerBlock - 1)]);
156				if (_count) {
157					*_count = 1;
158					uint32 nextBlock = block;
159					while (((++index & (kIndirectsPerBlock - 1)) != 0)
160						&& indirectBlocks[index & (kIndirectsPerBlock - 1)]
161							== ++nextBlock)
162						(*_count)++;
163				}
164			}
165		}
166		ASSERT(block != 0);
167	} else {
168		// Outside of the possible data stream
169		dprintf("ext2: block outside datastream!\n");
170		return B_ERROR;
171	}
172
173	TRACE("inode %Ld: FindBlock(offset %lld): %lld %ld\n", ID(), offset, block,
174		_count != NULL ? *_count : 1);
175	return B_OK;
176}
177
178
179status_t
180DataStream::Enlarge(Transaction& transaction, off_t& numBlocks)
181{
182	TRACE("DataStream::Enlarge(): current size: %llu, target size: %llu\n",
183		fNumBlocks, numBlocks);
184
185	off_t targetBlocks = numBlocks;
186	fWaiting = _BlocksNeeded(numBlocks);
187	numBlocks = fWaiting;
188
189	status_t status;
190
191	if (fNumBlocks <= kMaxDirect) {
192		status = _AddForDirectBlocks(transaction, targetBlocks);
193
194		if (status != B_OK) {
195			ERROR("DataStream::Enlarge(): _AddForDirectBlocks() failed\n");
196			return status;
197		}
198
199		TRACE("DataStream::Enlarge(): current size: %llu, target size: %llu\n",
200			fNumBlocks, targetBlocks);
201
202		if (fNumBlocks == targetBlocks)
203			return B_OK;
204	}
205
206	TRACE("DataStream::Enlarge(): indirect current size: %llu, target size: %llu\n",
207		fNumBlocks, targetBlocks);
208
209	if (fNumBlocks <= kMaxIndirect) {
210		status = _AddForIndirectBlock(transaction, targetBlocks);
211
212		if (status != B_OK) {
213			ERROR("DataStream::Enlarge(): _AddForIndirectBlock() failed\n");
214			return status;
215		}
216
217		TRACE("DataStream::Enlarge(): current size: %llu, target size: %llu\n",
218			fNumBlocks, targetBlocks);
219
220		if (fNumBlocks == targetBlocks)
221			return B_OK;
222	}
223
224	TRACE("DataStream::Enlarge(): indirect2 current size: %llu, target size: %llu\n",
225		fNumBlocks, targetBlocks);
226
227	if (fNumBlocks <= kMaxDoubleIndirect) {
228		status = _AddForDoubleIndirectBlock(transaction, targetBlocks);
229
230		if (status != B_OK) {
231			ERROR("DataStream::Enlarge(): _AddForDoubleIndirectBlock() failed\n");
232			return status;
233		}
234
235		TRACE("DataStream::Enlarge(): current size: %llu, target size: %llu\n",
236			fNumBlocks, targetBlocks);
237
238		if (fNumBlocks == targetBlocks)
239			return B_OK;
240	}
241
242	TRACE("DataStream::Enlarge(): indirect3 current size: %llu, target size: %llu\n",
243		fNumBlocks, targetBlocks);
244
245	TRACE("DataStream::Enlarge(): allocated: %lu, waiting: %lu\n", fAllocated,
246		fWaiting);
247
248	return _AddForTripleIndirectBlock(transaction, targetBlocks);
249}
250
251
252status_t
253DataStream::Shrink(Transaction& transaction, off_t& numBlocks)
254{
255	TRACE("DataStream::Shrink(): current size: %llu, target size: %llu\n",
256		fNumBlocks, numBlocks);
257
258	fFreeStart = 0;
259	fFreeCount = 0;
260	fRemovedBlocks = 0;
261
262	off_t oldNumBlocks = fNumBlocks;
263	off_t blocksToRemove = fNumBlocks - numBlocks;
264
265	status_t status;
266
267	if (numBlocks < kMaxDirect) {
268		status = _RemoveFromDirectBlocks(transaction, numBlocks);
269
270		if (status != B_OK) {
271			ERROR("DataStream::Shrink(): _RemoveFromDirectBlocks() failed\n");
272			return status;
273		}
274
275		if (fRemovedBlocks == blocksToRemove) {
276			fNumBlocks -= fRemovedBlocks;
277			numBlocks = _BlocksNeeded(oldNumBlocks);
278
279			return _PerformFree(transaction);
280		}
281	}
282
283	if (numBlocks < kMaxIndirect) {
284		status = _RemoveFromIndirectBlock(transaction, numBlocks);
285
286		if (status != B_OK) {
287			ERROR("DataStream::Shrink(): _RemoveFromIndirectBlock() failed\n");
288			return status;
289		}
290
291		if (fRemovedBlocks == blocksToRemove) {
292			fNumBlocks -= fRemovedBlocks;
293			numBlocks = _BlocksNeeded(oldNumBlocks);
294
295			return _PerformFree(transaction);
296		}
297	}
298
299	if (numBlocks < kMaxDoubleIndirect) {
300		status = _RemoveFromDoubleIndirectBlock(transaction, numBlocks);
301
302		if (status != B_OK) {
303			ERROR("DataStream::Shrink(): _RemoveFromDoubleIndirectBlock() failed\n");
304			return status;
305		}
306
307		if (fRemovedBlocks == blocksToRemove) {
308			fNumBlocks -= fRemovedBlocks;
309			numBlocks = _BlocksNeeded(oldNumBlocks);
310
311			return _PerformFree(transaction);
312		}
313	}
314
315	status = _RemoveFromTripleIndirectBlock(transaction, numBlocks);
316
317	if (status != B_OK) {
318		ERROR("DataStream::Shrink(): _RemoveFromTripleIndirectBlock() failed\n");
319		return status;
320	}
321
322	fNumBlocks -= fRemovedBlocks;
323	numBlocks = _BlocksNeeded(oldNumBlocks);
324
325	return _PerformFree(transaction);
326}
327
328
329uint32
330DataStream::_BlocksNeeded(off_t numBlocks)
331{
332	TRACE("DataStream::BlocksNeeded(): num blocks %llu\n", numBlocks);
333	off_t blocksNeeded = 0;
334
335	if (numBlocks > fNumBlocks) {
336		blocksNeeded += numBlocks - fNumBlocks;
337
338		if (numBlocks > kMaxDirect) {
339			if (fNumBlocks <= kMaxDirect)
340				blocksNeeded += 1;
341
342			if (numBlocks > kMaxIndirect) {
343				if (fNumBlocks <= kMaxIndirect) {
344					blocksNeeded += 2 + (numBlocks - kMaxIndirect - 1)
345						/ kIndirectsPerBlock;
346				} else {
347					blocksNeeded += (numBlocks - kMaxIndirect - 1)
348						/ kIndirectsPerBlock - (fNumBlocks
349							- kMaxIndirect - 1) / kIndirectsPerBlock;
350				}
351
352				if (numBlocks > kMaxDoubleIndirect) {
353					if (fNumBlocks <= kMaxDoubleIndirect) {
354						blocksNeeded += 2 + (numBlocks - kMaxDoubleIndirect - 1)
355							/ kIndirectsPerBlock2;
356					} else {
357						blocksNeeded += (numBlocks - kMaxDoubleIndirect - 1)
358							/ kIndirectsPerBlock - (fNumBlocks
359								- kMaxDoubleIndirect - 1) / kIndirectsPerBlock;
360					}
361				}
362			}
363		}
364	}
365
366	TRACE("DataStream::BlocksNeeded(): %llu\n", blocksNeeded);
367	return blocksNeeded;
368}
369
370
371status_t
372DataStream::_GetBlock(Transaction& transaction, uint32& blockNum)
373{
374	TRACE("DataStream::_GetBlock(): allocated: %lu, pos: %llu, waiting: %lu\n",
375		fAllocated, fAllocatedPos, fWaiting);
376
377	if (fAllocated == 0) {
378		uint32 blockGroup = (fAllocatedPos - fFirstBlock)
379			/ fVolume->BlocksPerGroup();
380
381		status_t status = fVolume->AllocateBlocks(transaction, 1, fWaiting,
382			blockGroup, fAllocatedPos, fAllocated);
383		if (status != B_OK) {
384			ERROR("DataStream::_GetBlock(): AllocateBlocks() failed()\n");
385			return status;
386		}
387
388		fWaiting -= fAllocated;
389
390		TRACE("DataStream::_GetBlock(): newAllocated: %lu, newpos: %llu,"
391			"newwaiting: %lu\n", fAllocated, fAllocatedPos, fWaiting);
392	}
393
394	fAllocated--;
395	blockNum = (uint32)fAllocatedPos++;
396
397	return B_OK;
398}
399
400
401status_t
402DataStream::_PrepareBlock(Transaction& transaction, uint32* pos,
403	uint32& blockNum, bool& clear)
404{
405	blockNum = B_LENDIAN_TO_HOST_INT32(*pos);
406	clear = false;
407
408	if (blockNum == 0) {
409		status_t status = _GetBlock(transaction, blockNum);
410		if (status != B_OK) {
411			ERROR("DataStream::_PrepareBlock() _GetBlock() failed blockNum %ld\n", blockNum);
412			return status;
413		}
414
415		*pos = B_HOST_TO_LENDIAN_INT32(blockNum);
416		clear = true;
417	}
418
419	return B_OK;
420}
421
422
423status_t
424DataStream::_AddBlocks(Transaction& transaction, uint32* block, off_t _count)
425{
426	off_t count = _count;
427	TRACE("DataStream::_AddBlocks(): count: %llu\n", count);
428
429	while (count > 0) {
430		uint32 blockNum;
431		status_t status = _GetBlock(transaction, blockNum);
432		if (status != B_OK)
433			return status;
434
435		*(block++) = B_HOST_TO_LENDIAN_INT32(blockNum);
436		--count;
437	}
438
439	fNumBlocks += _count;
440
441	return B_OK;
442}
443
444
445status_t
446DataStream::_AddBlocks(Transaction& transaction, uint32* block, off_t start,
447	off_t end, int recursion)
448{
449	TRACE("DataStream::_AddBlocks(): start: %llu, end %llu, recursion: %d\n",
450		start, end, recursion);
451
452	bool clear;
453	uint32 blockNum;
454	status_t status = _PrepareBlock(transaction, block, blockNum, clear);
455	if (status != B_OK)
456		return status;
457
458	CachedBlock cached(fVolume);
459	uint32* childBlock = (uint32*)cached.SetToWritable(transaction, blockNum,
460		clear);
461	if (childBlock == NULL)
462		return B_IO_ERROR;
463
464	if (recursion == 0)
465		return _AddBlocks(transaction, &childBlock[start], end - start);
466
467	uint32 elementWidth;
468	if (recursion == 1)
469		elementWidth = kIndirectsPerBlock;
470	else if (recursion == 2)
471		elementWidth = kIndirectsPerBlock2;
472	else {
473		panic("Undefined recursion level\n");
474		elementWidth = 0;
475	}
476
477	uint32 elementPos = start / elementWidth;
478	uint32 endPos = end / elementWidth;
479
480	TRACE("DataStream::_AddBlocks(): element pos: %lu, end pos: %lu\n",
481		elementPos, endPos);
482
483	recursion--;
484
485	if (elementPos == endPos) {
486		return _AddBlocks(transaction, &childBlock[elementPos],
487			start % elementWidth, end % elementWidth, recursion);
488	}
489
490	if (start % elementWidth != 0) {
491		status = _AddBlocks(transaction, &childBlock[elementPos],
492			start % elementWidth, elementWidth, recursion);
493		if (status != B_OK) {
494			ERROR("DataStream::_AddBlocks() _AddBlocks() start failed\n");
495			return status;
496		}
497
498		elementPos++;
499	}
500
501	while (elementPos < endPos) {
502		status = _AddBlocks(transaction, &childBlock[elementPos], 0,
503			elementWidth, recursion);
504		if (status != B_OK) {
505			ERROR("DataStream::_AddBlocks() _AddBlocks() mid failed\n");
506			return status;
507		}
508
509		elementPos++;
510	}
511
512	if (end % elementWidth != 0) {
513		status = _AddBlocks(transaction, &childBlock[elementPos], 0,
514			end % elementWidth, recursion);
515		if (status != B_OK) {
516			ERROR("DataStream::_AddBlocks() _AddBlocks() end failed\n");
517			return status;
518		}
519	}
520
521	return B_OK;
522}
523
524
525status_t
526DataStream::_AddForDirectBlocks(Transaction& transaction, uint32 numBlocks)
527{
528	TRACE("DataStream::_AddForDirectBlocks(): current size: %llu, target size: "
529		"%lu\n", fNumBlocks, numBlocks);
530	uint32* direct = &fStream->direct[fNumBlocks];
531	uint32 end = numBlocks > kMaxDirect ? kMaxDirect : numBlocks;
532
533	return _AddBlocks(transaction, direct, end - fNumBlocks);
534}
535
536
537status_t
538DataStream::_AddForIndirectBlock(Transaction& transaction, uint32 numBlocks)
539{
540	TRACE("DataStream::_AddForIndirectBlocks(): current size: %llu, target "
541		"size: %lu\n", fNumBlocks, numBlocks);
542	uint32 *indirect = &fStream->indirect;
543	uint32 start = fNumBlocks - kMaxDirect;
544	uint32 end = numBlocks - kMaxDirect;
545
546	if (end > kIndirectsPerBlock)
547		end = kIndirectsPerBlock;
548
549	return _AddBlocks(transaction, indirect, start, end, 0);
550}
551
552
553status_t
554DataStream::_AddForDoubleIndirectBlock(Transaction& transaction,
555	uint32 numBlocks)
556{
557	TRACE("DataStream::_AddForDoubleIndirectBlock(): current size: %llu, "
558		"target size: %lu\n", fNumBlocks, numBlocks);
559	uint32 *doubleIndirect = &fStream->double_indirect;
560	uint32 start = fNumBlocks - kMaxIndirect;
561	uint32 end = numBlocks - kMaxIndirect;
562
563	if (end > kIndirectsPerBlock2)
564		end = kIndirectsPerBlock2;
565
566	return _AddBlocks(transaction, doubleIndirect, start, end, 1);
567}
568
569
570status_t
571DataStream::_AddForTripleIndirectBlock(Transaction& transaction,
572	uint32 numBlocks)
573{
574	TRACE("DataStream::_AddForTripleIndirectBlock(): current size: %llu, "
575		"target size: %lu\n", fNumBlocks, numBlocks);
576	uint32 *tripleIndirect = &fStream->triple_indirect;
577	uint32 start = fNumBlocks - kMaxDoubleIndirect;
578	uint32 end = numBlocks - kMaxDoubleIndirect;
579
580	return _AddBlocks(transaction, tripleIndirect, start, end, 2);
581}
582
583
584status_t
585DataStream::_PerformFree(Transaction& transaction)
586{
587	TRACE("DataStream::_PerformFree(): start: %lu, count: %lu\n", fFreeStart,
588		fFreeCount);
589	status_t status;
590
591	if (fFreeCount == 0)
592		status = B_OK;
593	else
594		status = fVolume->FreeBlocks(transaction, fFreeStart, fFreeCount);
595
596	fFreeStart = 0;
597	fFreeCount = 0;
598
599	return status;
600}
601
602
603status_t
604DataStream::_MarkBlockForRemoval(Transaction& transaction, uint32* block)
605{
606
607	TRACE("DataStream::_MarkBlockForRemoval(*(%p) = %lu): free start: %lu, "
608		"free count: %lu\n", block, B_LENDIAN_TO_HOST_INT32(*block),
609		fFreeStart, fFreeCount);
610	uint32 blockNum = B_LENDIAN_TO_HOST_INT32(*block);
611	*block = 0;
612
613	if (blockNum != fFreeStart + fFreeCount) {
614		if (fFreeCount != 0) {
615			status_t status = fVolume->FreeBlocks(transaction, fFreeStart,
616				fFreeCount);
617			if (status != B_OK)
618				return status;
619		}
620
621		fFreeStart = blockNum;
622		fFreeCount = 0;
623	}
624
625	fFreeCount++;
626
627	return B_OK;
628}
629
630
631status_t
632DataStream::_FreeBlocks(Transaction& transaction, uint32* block, uint32 _count)
633{
634	uint32 count = _count;
635	TRACE("DataStream::_FreeBlocks(%p, %lu)\n", block, count);
636
637	while (count > 0) {
638		status_t status = _MarkBlockForRemoval(transaction, block);
639		if (status != B_OK)
640			return status;
641
642		block++;
643		count--;
644	}
645
646	fRemovedBlocks += _count;
647
648	return B_OK;
649}
650
651
652status_t
653DataStream::_FreeBlocks(Transaction& transaction, uint32* block, off_t start,
654	off_t end, bool freeParent, int recursion)
655{
656	// TODO: Designed specifically for shrinking. Perhaps make it more general?
657	TRACE("DataStream::_FreeBlocks(%p, %llu, %llu, %c, %d)\n",
658		block, start, end, freeParent ? 't' : 'f', recursion);
659
660	uint32 blockNum = B_LENDIAN_TO_HOST_INT32(*block);
661
662	if (freeParent) {
663		status_t status = _MarkBlockForRemoval(transaction, block);
664		if (status != B_OK)
665			return status;
666	}
667
668	CachedBlock cached(fVolume);
669	uint32* childBlock = (uint32*)cached.SetToWritable(transaction, blockNum);
670	if (childBlock == NULL)
671		return B_IO_ERROR;
672
673	if (recursion == 0)
674		return _FreeBlocks(transaction, &childBlock[start], end - start);
675
676	uint32 elementWidth;
677	if (recursion == 1)
678		elementWidth = kIndirectsPerBlock;
679	else if (recursion == 2)
680		elementWidth = kIndirectsPerBlock2;
681	else {
682		panic("Undefinied recursion level\n");
683		elementWidth = 0;
684	}
685
686	uint32 elementPos = start / elementWidth;
687	uint32 endPos = end / elementWidth;
688
689	recursion--;
690
691	if (elementPos == endPos) {
692		bool free = freeParent || start % elementWidth == 0;
693		return _FreeBlocks(transaction, &childBlock[elementPos],
694			start % elementWidth, end % elementWidth, free, recursion);
695	}
696
697	status_t status = B_OK;
698
699	if (start % elementWidth != 0) {
700		status = _FreeBlocks(transaction, &childBlock[elementPos],
701			start % elementWidth, elementWidth, false, recursion);
702		if (status != B_OK)
703			return status;
704
705		elementPos++;
706	}
707
708	while (elementPos < endPos) {
709		status = _FreeBlocks(transaction, &childBlock[elementPos], 0,
710			elementWidth, true, recursion);
711		if (status != B_OK)
712			return status;
713
714		elementPos++;
715	}
716
717	if (end % elementWidth != 0) {
718		status = _FreeBlocks(transaction, &childBlock[elementPos], 0,
719			end % elementWidth, true, recursion);
720	}
721
722	return status;
723}
724
725
726status_t
727DataStream::_RemoveFromDirectBlocks(Transaction& transaction, uint32 numBlocks)
728{
729	TRACE("DataStream::_RemoveFromDirectBlocks(): current size: %llu, "
730		"target size: %lu\n", fNumBlocks, numBlocks);
731	uint32* direct = &fStream->direct[numBlocks];
732	off_t end = fNumBlocks > kMaxDirect ? kMaxDirect : fNumBlocks;
733
734	return _FreeBlocks(transaction, direct, end - numBlocks);
735}
736
737
738status_t
739DataStream::_RemoveFromIndirectBlock(Transaction& transaction, uint32 numBlocks)
740{
741	TRACE("DataStream::_RemoveFromIndirectBlock(): current size: %llu, "
742		"target size: %lu\n", fNumBlocks, numBlocks);
743	uint32* indirect = &fStream->indirect;
744	off_t start = numBlocks <= kMaxDirect ? 0 : numBlocks - kMaxDirect;
745	off_t end = fNumBlocks - kMaxDirect;
746
747	if (end > kIndirectsPerBlock)
748		end = kIndirectsPerBlock;
749
750	bool freeAll = start == 0;
751
752	return _FreeBlocks(transaction, indirect, start, end, freeAll, 0);
753}
754
755
756status_t
757DataStream::_RemoveFromDoubleIndirectBlock(Transaction& transaction,
758	uint32 numBlocks)
759{
760	TRACE("DataStream::_RemoveFromDoubleIndirectBlock(): current size: %llu, "
761		"target size: %lu\n", fNumBlocks, numBlocks);
762	uint32* doubleIndirect = &fStream->double_indirect;
763	off_t start = numBlocks <= kMaxIndirect ? 0 : numBlocks - kMaxIndirect;
764	off_t end = fNumBlocks - kMaxIndirect;
765
766	if (end > kIndirectsPerBlock2)
767		end = kIndirectsPerBlock2;
768
769	bool freeAll = start == 0;
770
771	return _FreeBlocks(transaction, doubleIndirect, start, end, freeAll, 1);
772}
773
774
775status_t
776DataStream::_RemoveFromTripleIndirectBlock(Transaction& transaction,
777	uint32 numBlocks)
778{
779	TRACE("DataStream::_RemoveFromTripleIndirectBlock(): current size: %llu, "
780		"target size: %lu\n", fNumBlocks, numBlocks);
781	uint32* tripleIndirect = &fStream->triple_indirect;
782	off_t start = numBlocks <= kMaxDoubleIndirect ? 0
783		: numBlocks - kMaxDoubleIndirect;
784	off_t end = fNumBlocks - kMaxDoubleIndirect;
785
786	bool freeAll = start == 0;
787
788	return _FreeBlocks(transaction, tripleIndirect, start, end, freeAll, 2);
789}
790