2 * Copyright 2001-2011, Haiku Inc. All rights reserved.
3 * This file may be used under the terms of the MIT License.
10 #include "ExtentStream.h"
14 #include "CachedBlock.h"
21 # define TRACE(x...) dprintf("\33[34mext2:\33[0m ExtentStream::" x)
22 # define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
24 # define TRACE(x...) ;
27 #define ERROR(x...) dprintf("\33[34mext2:\33[0m ExtentStream::" x)
30 ExtentStream::ExtentStream(Volume
* volume
, ext2_extent_stream
* stream
,
35 fFirstBlock(volume
->FirstDataBlock()),
36 fAllocatedPos(fFirstBlock
),
39 fNumBlocks
= size
== 0 ? 0 : ((size
- 1) >> fVolume
->BlockShift()) + 1;
43 ExtentStream::~ExtentStream()
49 ExtentStream::FindBlock(off_t offset
, fsblock_t
& block
, uint32
*_count
)
51 fileblock_t index
= offset
>> fVolume
->BlockShift();
52 TRACE("FindBlock(%" B_PRIdOFF
", %" B_PRIu64
")\n", offset
, index
);
54 if (offset
>= fSize
) {
55 TRACE("FindBlock: offset larger than inode size\n");
56 return B_ENTRY_NOT_FOUND
;
59 ext2_extent_stream
*stream
= fStream
;
60 if (!stream
->extent_header
.IsValid())
61 panic("ExtentStream::FindBlock() invalid header\n");
63 CachedBlock
cached(fVolume
);
64 while (stream
->extent_header
.Depth() != 0) {
65 TRACE("FindBlock() depth %d\n",
66 stream
->extent_header
.Depth());
68 while (i
< stream
->extent_header
.NumEntries()
69 && stream
->extent_index
[i
].LogicalBlock() <= index
) {
72 TRACE("FindBlock() getting index %" B_PRId32
" at %" B_PRIu64
"\n",
73 i
- 1, stream
->extent_index
[i
- 1].PhysicalBlock());
74 stream
= (ext2_extent_stream
*)cached
.SetTo(
75 stream
->extent_index
[i
- 1].PhysicalBlock());
76 if (!stream
->extent_header
.IsValid())
77 panic("ExtentStream::FindBlock() invalid header\n");
80 // find the extend following the one that should contain the logical block
82 if (stream
->extent_header
.NumEntries() > 7) {
83 // binary search when enough entries
85 int32 high
= stream
->extent_header
.NumEntries() - 1;
87 int32 middle
= (high
+ low
+ 1) / 2;
88 if (stream
->extent_entries
[middle
].LogicalBlock() > index
)
94 extentIndex
= low
+ 1;
96 extentIndex
= stream
->extent_header
.NumEntries();
97 for (int32 i
= 0; i
< stream
->extent_header
.NumEntries(); i
++) {
98 if (stream
->extent_entries
[i
].LogicalBlock() > index
) {
105 fileblock_t logicalEndIndex
106 = (fSize
+ fVolume
->BlockSize() - 1) >> fVolume
->BlockShift();
108 if (extentIndex
== 0) {
109 // sparse block at the beginning of the stream
111 if (_count
!= NULL
) {
112 *_count
= stream
->extent_header
.NumEntries() == 0
113 ? logicalEndIndex
- index
114 : stream
->extent_entries
[0].LogicalBlock() - index
;
116 TRACE("FindBlock() sparse block index %" B_PRIu64
" at beginning of "
121 const ext2_extent_entry
& extent
= stream
->extent_entries
[extentIndex
- 1];
122 // the extent supposedly containing the offset
123 fileblock_t diff
= index
- extent
.LogicalBlock();
124 if (diff
>= extent
.Length()) {
125 // sparse block between extends or at the end of the stream
126 TRACE("FindBlock() sparse block index %" B_PRIu64
" at %" B_PRIu32
127 "\n", index
, extent
.LogicalBlock());
129 if (_count
!= NULL
) {
130 *_count
= stream
->extent_header
.NumEntries() == extentIndex
131 ? logicalEndIndex
- index
132 : stream
->extent_entries
[extentIndex
].LogicalBlock() - index
;
137 block
= extent
.PhysicalBlock() + diff
;
139 *_count
= extent
.Length() - diff
;
141 TRACE("FindBlock(offset %" B_PRIdOFF
"): %" B_PRIu64
" %" B_PRIu32
142 "\n", offset
, block
, _count
!= NULL
? *_count
: 1);
149 ExtentStream::Enlarge(Transaction
& transaction
, off_t
& numBlocks
)
151 TRACE("Enlarge(): current size: %" B_PRIdOFF
", target size: %" B_PRIdOFF
152 "\n", fNumBlocks
, numBlocks
);
154 off_t targetBlocks
= numBlocks
;
155 numBlocks
= targetBlocks
- fNumBlocks
;
156 uint32 allocated
= 0;
158 while (fNumBlocks
< targetBlocks
) {
159 // allocate new blocks
160 uint32 blockGroup
= (fAllocatedPos
- fFirstBlock
)
161 / fVolume
->BlocksPerGroup();
163 if (allocated
== 0) {
164 status_t status
= fVolume
->AllocateBlocks(transaction
, 1,
165 targetBlocks
- fNumBlocks
, blockGroup
, fAllocatedPos
,
167 if (status
!= B_OK
) {
168 ERROR("Enlarge(): AllocateBlocks() failed()\n");
173 ASSERT(_CheckBlock(fStream
, fAllocatedPos
) == B_OK
);
175 ext2_extent_stream
*stream
= fStream
;
176 fsblock_t path
[stream
->extent_header
.Depth()];
178 // search for the leaf
179 CachedBlock
cached(fVolume
);
181 for (; stream
->extent_header
.Depth() != 0;) {
182 if (stream
->extent_header
.NumEntries() == 0)
183 panic("stream->extent_header.NumEntries() == 0\n");
184 int32 lastIndex
= stream
->extent_header
.NumEntries() - 1;
185 TRACE("Enlarge() depth %d\n", stream
->extent_header
.Depth());
186 TRACE("Enlarge() getting index %" B_PRId32
" at %" B_PRIu64
"\n",
187 lastIndex
, stream
->extent_index
[lastIndex
].PhysicalBlock());
188 path
[++level
] = stream
->extent_index
[lastIndex
].PhysicalBlock();
189 stream
= (ext2_extent_stream
*)cached
.SetTo(path
[level
]);
194 // check if the new extent is adjacent
195 if (stream
->extent_header
.NumEntries() > 0) {
196 ext2_extent_entry
&last
= stream
->extent_entries
[
197 stream
->extent_header
.NumEntries() - 1];
198 TRACE("Enlarge() last %" B_PRIu64
" allocatedPos %" B_PRIu64
"\n",
199 last
.PhysicalBlock() + last
.Length(), fAllocatedPos
);
200 if (last
.PhysicalBlock() + last
.Length() == fAllocatedPos
201 && (last
.Length() + allocated
) <= EXT2_EXTENT_MAX_LENGTH
) {
202 if (stream
!= fStream
) {
203 stream
= (ext2_extent_stream
*)cached
.SetToWritable(
204 transaction
, cached
.BlockNumber());
208 stream
->extent_entries
[stream
->extent_header
.NumEntries() - 1]
209 .SetLength(last
.Length() + allocated
);
210 fNumBlocks
+= allocated
;
212 TRACE("Enlarge() entry extended\n");
217 if (stream
->extent_header
.NumEntries()
218 >= stream
->extent_header
.MaxEntries()) {
219 TRACE("Enlarge() adding leaf and indexes at depth %d level %"
220 B_PRId32
"\n", stream
->extent_header
.Depth(), level
);
221 // try to add a leaf and indexes
222 while (--level
>= 0) {
223 stream
= (ext2_extent_stream
*)cached
.SetTo(path
[level
]);
226 if (stream
->extent_header
.NumEntries()
227 < stream
->extent_header
.MaxEntries()) {
230 TRACE("Enlarge() going up from level %" B_PRId32
"\n", level
);
233 if (level
< 0 && fStream
->extent_header
.NumEntries()
234 >= fStream
->extent_header
.MaxEntries()) {
235 TRACE("Enlarge() add a level, increment root depth\n");
236 fsblock_t newBlock
= fStream
->extent_index
[0].PhysicalBlock();
238 status_t status
= fVolume
->AllocateBlocks(transaction
, 1, 1,
239 blockGroup
, newBlock
, _allocated
);
240 if (status
!= B_OK
) {
241 ERROR("Enlarge() AllocateBlocks() failed()\n");
244 ASSERT(_CheckBlock(fStream
, newBlock
) == B_OK
);
245 TRACE("Enlarge() move root to block %" B_PRIu64
"\n",
248 stream
= (ext2_extent_stream
*)cached
.SetToWritable(
249 transaction
, newBlock
);
252 ASSERT(fStream
->extent_header
.IsValid());
253 memcpy(stream
, fStream
, sizeof(ext2_extent_stream
));
254 fStream
->extent_header
.SetDepth(stream
->extent_header
.Depth()
256 TRACE("Enlarge() new root depth %d\n",
257 fStream
->extent_header
.Depth());
258 fStream
->extent_header
.SetNumEntries(1);
259 fStream
->extent_index
[0].SetLogicalBlock(0);
260 fStream
->extent_index
[0].SetPhysicalBlock(newBlock
);
261 stream
->extent_header
.SetMaxEntries((fVolume
->BlockSize()
262 - sizeof(ext2_extent_header
)) / sizeof(ext2_extent_index
));
263 ASSERT(stream
->extent_header
.IsValid());
272 uint16 depth
= stream
->extent_header
.Depth();
274 TRACE("Enlarge() adding an index block at depth %d\n", depth
);
275 fsblock_t newBlock
= path
[level
];
277 status_t status
= fVolume
->AllocateBlocks(transaction
, 1, 1,
278 blockGroup
, newBlock
, _allocated
);
279 if (status
!= B_OK
) {
280 ERROR("Enlarge(): AllocateBlocks() failed()\n");
283 ASSERT(_CheckBlock(fStream
, newBlock
) == B_OK
);
285 int32 index
= stream
->extent_header
.NumEntries();
286 stream
->extent_index
[index
].SetLogicalBlock(fNumBlocks
);
287 stream
->extent_index
[index
].SetPhysicalBlock(newBlock
);
288 stream
->extent_header
.SetNumEntries(index
+ 1);
289 path
[level
++] = newBlock
;
291 depth
= stream
->extent_header
.Depth() - 1;
292 TRACE("Enlarge() init index block %" B_PRIu64
" at depth %d\n",
294 stream
= (ext2_extent_stream
*)cached
.SetToWritable(
295 transaction
, newBlock
);
298 stream
->extent_header
.magic
= EXT2_EXTENT_MAGIC
;
299 stream
->extent_header
.SetNumEntries(0);
300 stream
->extent_header
.SetMaxEntries((fVolume
->BlockSize()
301 - sizeof(ext2_extent_header
)) / sizeof(ext2_extent_index
));
302 stream
->extent_header
.SetDepth(depth
);
303 stream
->extent_header
.SetGeneration(0);
308 TRACE("Enlarge() depth %d level %" B_PRId32
"\n",
309 stream
->extent_header
.Depth(), level
);
311 if (stream
->extent_header
.Depth() == 1) {
312 TRACE("Enlarge() adding an entry block at depth %d level %"
313 B_PRId32
"\n", depth
, level
);
316 newBlock
= path
[level
];
318 newBlock
= fStream
->extent_index
[0].PhysicalBlock();
320 status_t status
= fVolume
->AllocateBlocks(transaction
, 1, 1,
321 blockGroup
, newBlock
, _allocated
);
322 if (status
!= B_OK
) {
323 ERROR("Enlarge(): AllocateBlocks() failed()\n");
327 ASSERT(_CheckBlock(fStream
, newBlock
) == B_OK
);
329 int32 index
= stream
->extent_header
.NumEntries();
330 stream
->extent_index
[index
].SetLogicalBlock(fNumBlocks
);
331 stream
->extent_index
[index
].SetPhysicalBlock(newBlock
);
332 stream
->extent_header
.SetNumEntries(index
+ 1);
334 TRACE("Enlarge() init entry block %" B_PRIu64
335 " at depth %d\n", newBlock
, depth
);
336 stream
= (ext2_extent_stream
*)cached
.SetToWritable(
337 transaction
, newBlock
);
340 stream
->extent_header
.magic
= EXT2_EXTENT_MAGIC
;
341 stream
->extent_header
.SetNumEntries(0);
342 stream
->extent_header
.SetMaxEntries((fVolume
->BlockSize()
343 - sizeof(ext2_extent_header
)) / sizeof(ext2_extent_entry
));
344 stream
->extent_header
.SetDepth(0);
345 stream
->extent_header
.SetGeneration(0);
351 TRACE("Enlarge() add entry %" B_PRIu64
"\n", fAllocatedPos
);
352 if (stream
!= fStream
) {
353 stream
= (ext2_extent_stream
*)cached
.SetToWritable(
354 transaction
, cached
.BlockNumber());
358 int32 index
= stream
->extent_header
.NumEntries();
359 stream
->extent_entries
[index
].SetLogicalBlock(fNumBlocks
);
360 stream
->extent_entries
[index
].SetLength(allocated
);
361 stream
->extent_entries
[index
].SetPhysicalBlock(fAllocatedPos
);
362 stream
->extent_header
.SetNumEntries(index
+ 1);
363 TRACE("Enlarge() entry added at index %" B_PRId32
"\n", index
);
364 ASSERT(stream
->extent_header
.IsValid());
366 fNumBlocks
+= allocated
;
375 ExtentStream::Shrink(Transaction
& transaction
, off_t
& numBlocks
)
377 TRACE("DataStream::Shrink(): current size: %" B_PRIdOFF
", target size: %"
378 B_PRIdOFF
"\n", fNumBlocks
, numBlocks
);
380 off_t targetBlocks
= numBlocks
;
381 numBlocks
= fNumBlocks
- targetBlocks
;
383 while (targetBlocks
< fNumBlocks
) {
384 ext2_extent_stream
*stream
= fStream
;
385 fsblock_t path
[stream
->extent_header
.Depth()];
387 // search for the leaf
388 CachedBlock
cached(fVolume
);
390 for (; stream
->extent_header
.Depth() != 0;) {
391 if (stream
->extent_header
.NumEntries() == 0)
392 panic("stream->extent_header.NumEntries() == 0\n");
393 int32 lastIndex
= stream
->extent_header
.NumEntries() - 1;
394 TRACE("Shrink() depth %d\n", stream
->extent_header
.Depth());
395 TRACE("Shrink() getting index %" B_PRId32
" at %" B_PRIu64
"\n",
396 lastIndex
, stream
->extent_index
[lastIndex
].PhysicalBlock());
397 path
[++level
] = stream
->extent_index
[lastIndex
].PhysicalBlock();
398 stream
= (ext2_extent_stream
*)cached
.SetToWritable(transaction
,
402 if (!stream
->extent_header
.IsValid())
403 panic("Shrink() invalid header\n");
406 int32 index
= stream
->extent_header
.NumEntries() - 1;
407 status_t status
= B_OK
;
408 while (index
>= 0 && targetBlocks
< fNumBlocks
) {
409 ext2_extent_entry
&last
= stream
->extent_entries
[index
];
410 if (last
.LogicalBlock() + last
.Length() < targetBlocks
) {
414 uint16 length
= min_c(last
.Length(), fNumBlocks
- targetBlocks
);
415 fsblock_t block
= last
.PhysicalBlock() + last
.Length() - length
;
416 TRACE("Shrink() free block %" B_PRIu64
" length %d\n", block
,
418 status
= fVolume
->FreeBlocks(transaction
, block
, length
);
421 fNumBlocks
-= length
;
422 stream
->extent_entries
[index
].SetLength(last
.Length() - length
);
423 TRACE("Shrink() new length for %" B_PRId32
": %d\n", index
, last
.Length());
424 if (last
.Length() != 0)
427 TRACE("Shrink() next index: %" B_PRId32
"\n", index
);
429 TRACE("Shrink() new entry count: %" B_PRId32
"\n", index
+ 1);
430 stream
->extent_header
.SetNumEntries(index
+ 1);
436 while (stream
!= fStream
&& stream
->extent_header
.NumEntries() == 0) {
437 TRACE("Shrink() empty leaf at depth %d\n",
438 stream
->extent_header
.Depth());
441 stream
= (ext2_extent_stream
*)cached
.SetToWritable(
442 transaction
, path
[level
]);
447 if (!stream
->extent_header
.IsValid())
448 panic("Shrink() invalid header\n");
449 uint16 index
= stream
->extent_header
.NumEntries() - 1;
450 ext2_extent_index
&last
= stream
->extent_index
[index
];
451 if (last
.PhysicalBlock() != path
[level
+ 1])
452 panic("Shrink() last.PhysicalBlock() != path[level + 1]\n");
453 status
= fVolume
->FreeBlocks(transaction
, last
.PhysicalBlock(), 1);
457 stream
->extent_header
.SetNumEntries(index
);
458 TRACE("Shrink() new entry count: %d\n", index
);
460 if (stream
== fStream
&& stream
->extent_header
.NumEntries() == 0)
461 stream
->extent_header
.SetDepth(0);
473 fStream
->extent_header
.magic
= EXT2_EXTENT_MAGIC
;
474 fStream
->extent_header
.SetNumEntries(0);
475 fStream
->extent_header
.SetMaxEntries(4);
476 fStream
->extent_header
.SetDepth(0);
477 fStream
->extent_header
.SetGeneration(0);
482 ExtentStream::_Check(ext2_extent_stream
*stream
, fileblock_t
&block
)
484 if (!stream
->extent_header
.IsValid()) {
485 panic("_Check() invalid header\n");
488 if (stream
->extent_header
.Depth() == 0) {
489 for (int32 i
= 0; i
< stream
->extent_header
.NumEntries(); i
++) {
490 ext2_extent_entry
&entry
= stream
->extent_entries
[i
];
491 if (entry
.LogicalBlock() < block
) {
492 panic("_Check() entry %" B_PRId32
" %" B_PRIu64
" %" B_PRIu32
493 "\n", i
, block
, entry
.LogicalBlock());
496 block
= entry
.LogicalBlock() + entry
.Length();
501 CachedBlock
cached(fVolume
);
502 for (int32 i
= 0; i
< stream
->extent_header
.NumEntries(); i
++) {
503 ext2_extent_index
&index
= stream
->extent_index
[i
];
504 if (index
.LogicalBlock() < block
) {
505 panic("_Check() index %" B_PRId32
" %" B_PRIu64
" %" B_PRIu32
506 "\n", i
, block
, index
.LogicalBlock());
509 ext2_extent_stream
*child
= (ext2_extent_stream
*)cached
.SetTo(
510 index
.PhysicalBlock());
511 if (child
->extent_header
.Depth() != (stream
->extent_header
.Depth() - 1)
512 || _Check(child
, block
) != B_OK
)
520 ExtentStream::Check()
522 fileblock_t block
= 0;
523 return _Check(fStream
, block
) == B_OK
;
528 ExtentStream::_CheckBlock(ext2_extent_stream
*stream
, fsblock_t block
)
530 if (stream
->extent_header
.Depth() == 0) {
531 for (int32 i
= 0; i
< stream
->extent_header
.NumEntries(); i
++) {
532 ext2_extent_entry
&entry
= stream
->extent_entries
[i
];
533 if (entry
.PhysicalBlock() <= block
534 && (entry
.PhysicalBlock() + entry
.Length()) > block
) {
535 panic("_CheckBlock() entry %" B_PRId32
" %" B_PRIu64
" %"
536 B_PRIu64
" %d\n", i
, block
, entry
.PhysicalBlock(),
544 CachedBlock
cached(fVolume
);
545 for (int32 i
= 0; i
< stream
->extent_header
.NumEntries(); i
++) {
546 ext2_extent_index
&index
= stream
->extent_index
[i
];
547 if (index
.PhysicalBlock() == block
) {
548 panic("_CheckBlock() index %" B_PRId32
" %" B_PRIu64
"\n", i
, block
);
551 ext2_extent_stream
*child
= (ext2_extent_stream
*)cached
.SetTo(
552 index
.PhysicalBlock());
553 if (child
->extent_header
.Depth() != (stream
->extent_header
.Depth() - 1)
554 || _CheckBlock(child
, block
) != B_OK
)