xref: /haiku/src/add-ons/kernel/file_systems/ext2/Volume.cpp (revision b28ed9e04a771e5de38be68abd08148c0bbafc56)
1 /*
2  * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3  * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
4  * This file may be used under the terms of the MIT License.
5  */
6 
7 
8 //! Superblock, mounting, etc.
9 
10 
11 #include "Volume.h"
12 
13 #include <errno.h>
14 #include <new>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include <fs_cache.h>
20 #include <fs_volume.h>
21 
22 #include <util/AutoLock.h>
23 
24 #include "CachedBlock.h"
25 #include "CRCTable.h"
26 #include "DeviceOpener.h"
27 #include "Inode.h"
28 #include "InodeJournal.h"
29 #include "NoJournal.h"
30 
31 
32 //#define TRACE_EXT2
33 #ifdef TRACE_EXT2
34 #	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
35 #else
36 #	define TRACE(x...) ;
37 #endif
38 #	define FATAL(x...) dprintf("\33[34mext2:\33[0m " x)
39 
40 
41 //	#pragma mark -
42 
43 
44 bool
45 ext2_super_block::IsValid()
46 {
47 	if (Magic() != (uint32)EXT2_SUPER_BLOCK_MAGIC
48 			|| BlockShift() > 16
49 			|| BlocksPerGroup() != (1UL << BlockShift()) * 8
50 			|| InodeSize() > (1UL << BlockShift())
51 			|| RevisionLevel() > EXT2_MAX_REVISION
52 			|| ReservedGDTBlocks() > (1UL << BlockShift()) / 4
53 			|| NumInodes() == 0
54 			|| InodeSize() == 0
55 			|| FreeInodes() > NumInodes()) {
56 		return false;
57 	}
58 
59 	return true;
60 }
61 
62 
63 //	#pragma mark -
64 
65 
66 Volume::Volume(fs_volume* volume)
67 	:
68 	fFSVolume(volume),
69 	fBlockAllocator(NULL),
70 	fInodeAllocator(this),
71 	fJournalInode(NULL),
72 	fFlags(0),
73 	fGroupBlocks(NULL),
74 	fRootNode(NULL)
75 {
76 	mutex_init(&fLock, "ext2 volume");
77 }
78 
79 
80 Volume::~Volume()
81 {
82 	TRACE("Volume destructor.\n");
83 	delete fBlockAllocator;
84 	if (fGroupBlocks != NULL) {
85 		uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1)
86 			/ fGroupsPerBlock;
87 		for (uint32 i = 0; i < blockCount; i++)
88 			free(fGroupBlocks[i]);
89 
90 		free(fGroupBlocks);
91 	}
92 }
93 
94 
95 bool
96 Volume::IsValidSuperBlock()
97 {
98 	return fSuperBlock.IsValid();
99 }
100 
101 
102 bool
103 Volume::HasExtendedAttributes() const
104 {
105 	return (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR) != 0;
106 }
107 
108 
109 const char*
110 Volume::Name() const
111 {
112 	// The name may be empty, in that case, userspace will generate one.
113 	return fSuperBlock.name;
114 }
115 
116 
117 void
118 Volume::SetName(const char* name)
119 {
120 	strlcpy(fSuperBlock.name, name, sizeof(fSuperBlock.name));
121 }
122 
123 
124 status_t
125 Volume::Mount(const char* deviceName, uint32 flags)
126 {
127 	// flags |= B_MOUNT_READ_ONLY;
128 		// we only support read-only for now
129 
130 	if ((flags & B_MOUNT_READ_ONLY) != 0) {
131 		TRACE("Volume::Mount(): Read only\n");
132 	} else {
133 		TRACE("Volume::Mount(): Read write\n");
134 	}
135 
136 	DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
137 		? O_RDONLY : O_RDWR);
138 	fDevice = opener.Device();
139 	if (fDevice < B_OK) {
140 		FATAL("Volume::Mount(): couldn't open device\n");
141 		return fDevice;
142 	}
143 
144 	if (opener.IsReadOnly())
145 		fFlags |= VOLUME_READ_ONLY;
146 
147 	// read the superblock
148 	status_t status = Identify(fDevice, &fSuperBlock);
149 	if (status != B_OK) {
150 		FATAL("Volume::Mount(): Identify() failed\n");
151 		return status;
152 	}
153 
154 	TRACE("features %" B_PRIx32 ", incompatible features %" B_PRIx32
155 		", read-only features %" B_PRIx32 "\n",
156 		fSuperBlock.CompatibleFeatures(), fSuperBlock.IncompatibleFeatures(),
157 		fSuperBlock.ReadOnlyFeatures());
158 
159 	// check read-only features if mounting read-write
160 	if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0)
161 		return B_UNSUPPORTED;
162 
163 	if (HasMetaGroupChecksumFeature() && HasChecksumFeature())
164 		return B_ERROR;
165 
166 	if (!_VerifySuperBlock())
167 		return B_ERROR;
168 
169 	if ((fSuperBlock.State() & EXT2_FS_STATE_VALID) == 0
170 		|| (fSuperBlock.State() & EXT2_FS_STATE_ERROR) != 0) {
171 		if (!IsReadOnly()) {
172 			FATAL("Volume::Mount(): can't mount R/W, volume not clean\n");
173 			return B_NOT_ALLOWED;
174 		} else {
175 			FATAL("Volume::Mount(): warning: volume not clean\n");
176 		}
177 	}
178 
179 	// initialize short hands to the superblock (to save byte swapping)
180 	fBlockShift = fSuperBlock.BlockShift();
181 	if (fBlockShift < 10 || fBlockShift > 16)
182 		return B_ERROR;
183 	if (fBlockShift > 12) {
184 		FATAL("blocksize not supported!\n");
185 		return B_UNSUPPORTED;
186 	}
187 	fBlockSize = 1UL << fBlockShift;
188 	fFirstDataBlock = fSuperBlock.FirstDataBlock();
189 
190 	fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
191 	fFreeInodes = fSuperBlock.FreeInodes();
192 
193 	if (fFirstDataBlock > fSuperBlock.NumBlocks(Has64bitFeature()))
194 		return B_ERROR;
195 
196 	off_t numBlocks = fSuperBlock.NumBlocks(Has64bitFeature()) - fFirstDataBlock;
197 	uint32 blocksPerGroup = fSuperBlock.BlocksPerGroup();
198 	fNumGroups = numBlocks / blocksPerGroup;
199 	if (numBlocks % blocksPerGroup != 0)
200 		fNumGroups++;
201 
202 	if (blocksPerGroup == 0 || fSuperBlock.FragmentsPerGroup() == 0) {
203 		FATAL("zero blocksPerGroup or fragmentsPerGroup!\n");
204 		return B_UNSUPPORTED;
205 	}
206 	if (blocksPerGroup != fSuperBlock.FragmentsPerGroup()) {
207 		FATAL("blocksPerGroup is not equal to fragmentsPerGroup!\n");
208 		return B_UNSUPPORTED;
209 	}
210 
211 	if (Has64bitFeature()) {
212 		TRACE("64bits\n");
213 		fGroupDescriptorSize = fSuperBlock.GroupDescriptorSize();
214 		if (fGroupDescriptorSize < sizeof(ext2_block_group))
215 			return B_ERROR;
216 		if (fGroupDescriptorSize != sizeof(ext2_block_group))
217 			return B_UNSUPPORTED;
218 	} else
219 		fGroupDescriptorSize = EXT2_BLOCK_GROUP_NORMAL_SIZE;
220 	fGroupsPerBlock = fBlockSize / fGroupDescriptorSize;
221 	fNumInodes = fSuperBlock.NumInodes();
222 
223 	TRACE("block size %" B_PRIu32 ", num groups %" B_PRIu32 ", groups per "
224 		"block %" B_PRIu32 ", first %" B_PRIu32 ", group_descriptor_size %"
225 		B_PRIu32 "\n", fBlockSize, fNumGroups,
226 		fGroupsPerBlock, fFirstDataBlock, fGroupDescriptorSize);
227 
228 	uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) / fGroupsPerBlock;
229 
230 	fGroupBlocks = (uint8**)malloc(blockCount * sizeof(uint8*));
231 	if (fGroupBlocks == NULL)
232 		return B_NO_MEMORY;
233 
234 	memset(fGroupBlocks, 0, blockCount * sizeof(uint8*));
235 	fInodesPerBlock = fBlockSize / InodeSize();
236 
237 	_SuperBlockChecksumSeed();
238 
239 	// check if the device size is large enough to hold the file system
240 	off_t diskSize;
241 	status = opener.GetSize(&diskSize);
242 	if (status != B_OK)
243 		return status;
244 	if ((diskSize + fBlockSize) <= ((off_t)NumBlocks() << BlockShift())) {
245 		FATAL("diskSize is too small for the number of blocks!\n");
246 		return B_BAD_VALUE;
247 	}
248 
249 	fBlockCache = opener.InitCache(NumBlocks(), fBlockSize);
250 	if (fBlockCache == NULL)
251 		return B_ERROR;
252 
253 	TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
254 
255 	// initialize journal if mounted read-write
256 	if (!IsReadOnly() && HasJournalFeature()) {
257 		// TODO: There should be a mount option to ignore the existent journal
258 		if (fSuperBlock.JournalInode() != 0) {
259 			fJournalInode = new(std::nothrow) Inode(this,
260 				fSuperBlock.JournalInode());
261 
262 			if (fJournalInode == NULL)
263 				return B_NO_MEMORY;
264 
265 			TRACE("Opening an on disk, inode mapped journal.\n");
266 			fJournal = new(std::nothrow) InodeJournal(fJournalInode);
267 		} else {
268 			// TODO: external journal
269 			TRACE("Can not open an external journal.\n");
270 			return B_UNSUPPORTED;
271 		}
272 	} else {
273 		TRACE("Opening a fake journal (NoJournal).\n");
274 		fJournal = new(std::nothrow) NoJournal(this);
275 	}
276 
277 	if (fJournal == NULL) {
278 		TRACE("No memory to create the journal\n");
279 		return B_NO_MEMORY;
280 	}
281 
282 	TRACE("Volume::Mount(): Checking if journal was initialized\n");
283 	status = fJournal->InitCheck();
284 	if (status != B_OK) {
285 		FATAL("could not initialize journal!\n");
286 		return status;
287 	}
288 
289 	// TODO: Only recover if asked to
290 	TRACE("Volume::Mount(): Asking journal to recover\n");
291 	status = fJournal->Recover();
292 	if (status != B_OK) {
293 		FATAL("could not recover journal!\n");
294 		return status;
295 	}
296 
297 	TRACE("Volume::Mount(): Restart journal log\n");
298 	status = fJournal->StartLog();
299 	if (status != B_OK) {
300 		FATAL("could not initialize start journal!\n");
301 		return status;
302 	}
303 
304 	if (!IsReadOnly()) {
305 		// Initialize allocators
306 		fBlockAllocator = new(std::nothrow) BlockAllocator(this);
307 		if (fBlockAllocator != NULL) {
308 			TRACE("Volume::Mount(): Initialize block allocator\n");
309 			status = fBlockAllocator->Initialize();
310 		}
311 		if (fBlockAllocator == NULL || status != B_OK) {
312 			delete fBlockAllocator;
313 			fBlockAllocator = NULL;
314 			FATAL("could not initialize block allocator, going read-only!\n");
315 			fFlags |= VOLUME_READ_ONLY;
316 			fJournal->Uninit();
317 			delete fJournal;
318 			delete fJournalInode;
319 			fJournalInode = NULL;
320 			fJournal = new(std::nothrow) NoJournal(this);
321 		}
322 	}
323 
324 	// ready
325 	status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode);
326 	if (status != B_OK) {
327 		FATAL("could not create root node: get_vnode() failed!\n");
328 		return status;
329 	}
330 
331 	// all went fine
332 	opener.Keep();
333 
334 	return B_OK;
335 }
336 
337 
338 status_t
339 Volume::Unmount()
340 {
341 	TRACE("Volume::Unmount()\n");
342 
343 	status_t status = fJournal->Uninit();
344 
345 	// this will wait on the block notifier/writer thread
346 	FlushDevice();
347 	delete fJournal;
348 	delete fJournalInode;
349 
350 	TRACE("Volume::Unmount(): Putting root node\n");
351 	put_vnode(fFSVolume, RootNode()->ID());
352 	TRACE("Volume::Unmount(): Deleting the block cache\n");
353 	block_cache_delete(fBlockCache, !IsReadOnly());
354 	TRACE("Volume::Unmount(): Closing device\n");
355 	close(fDevice);
356 
357 	TRACE("Volume::Unmount(): Done\n");
358 	return status;
359 }
360 
361 
362 status_t
363 Volume::GetInodeBlock(ino_t id, off_t& block)
364 {
365 	ext2_block_group* group;
366 	status_t status = GetBlockGroup((id - 1) / fSuperBlock.InodesPerGroup(),
367 		&group);
368 	if (status != B_OK)
369 		return status;
370 
371 	block = group->InodeTable(Has64bitFeature())
372 		+ ((id - 1) % fSuperBlock.InodesPerGroup()) / fInodesPerBlock;
373 	if (block < 0)
374 		return B_BAD_DATA;
375 	return B_OK;
376 }
377 
378 
379 uint32
380 Volume::InodeBlockIndex(ino_t id) const
381 {
382 	return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock;
383 }
384 
385 
386 /*static*/ uint32
387 Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock)
388 {
389 	uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE
390 		| EXT2_INCOMPATIBLE_FEATURE_RECOVER
391 		| EXT2_INCOMPATIBLE_FEATURE_JOURNAL
392 		| EXT2_INCOMPATIBLE_FEATURE_EXTENTS
393 		| EXT2_INCOMPATIBLE_FEATURE_FLEX_GROUP
394 		| EXT2_INCOMPATIBLE_FEATURE_64BIT;
395 		/*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/;
396 	uint32 unsupported = superBlock.IncompatibleFeatures()
397 		& ~supportedIncompatible;
398 
399 	if (unsupported != 0) {
400 		FATAL("ext2: incompatible features not supported: %" B_PRIx32
401 			" (extents %x)\n", unsupported, EXT2_INCOMPATIBLE_FEATURE_EXTENTS);
402 	}
403 
404 	return unsupported;
405 }
406 
407 
408 /*static*/ uint32
409 Volume::_UnsupportedReadOnlyFeatures(ext2_super_block& superBlock)
410 {
411 	uint32 supportedReadOnly = EXT2_READ_ONLY_FEATURE_SPARSE_SUPER
412 		| EXT2_READ_ONLY_FEATURE_LARGE_FILE
413 		| EXT2_READ_ONLY_FEATURE_HUGE_FILE
414 		| EXT2_READ_ONLY_FEATURE_EXTRA_ISIZE
415 		| EXT2_READ_ONLY_FEATURE_DIR_NLINK
416 		| EXT2_READ_ONLY_FEATURE_GDT_CSUM
417 		| EXT4_READ_ONLY_FEATURE_METADATA_CSUM;
418 	// TODO actually implement EXT2_READ_ONLY_FEATURE_SPARSE_SUPER when
419 	// implementing superblock backup copies
420 
421 	uint32 unsupported = superBlock.ReadOnlyFeatures() & ~supportedReadOnly;
422 
423 	if (unsupported != 0) {
424 		FATAL("ext2: readonly features not supported: %" B_PRIx32 "\n",
425 			unsupported);
426 	}
427 
428 	return unsupported;
429 }
430 
431 
432 uint32
433 Volume::_GroupDescriptorBlock(uint32 blockIndex)
434 {
435 	if ((fSuperBlock.IncompatibleFeatures()
436 			& EXT2_INCOMPATIBLE_FEATURE_META_GROUP) == 0
437 		|| blockIndex < fSuperBlock.FirstMetaBlockGroup())
438 		return fFirstDataBlock + blockIndex + 1;
439 
440 	panic("meta block");
441 	return 0;
442 }
443 
444 
445 uint16
446 Volume::_GroupCheckSum(ext2_block_group *group, int32 index)
447 {
448 	int32 number = B_HOST_TO_LENDIAN_INT32(index);
449 	size_t offset = offsetof(ext2_block_group, checksum);
450 	size_t offsetExt4 = offsetof(ext2_block_group, block_bitmap_high);
451 	if (HasMetaGroupChecksumFeature()) {
452 		uint16 dummy = 0;
453 		uint32 checksum = calculate_crc32c(fChecksumSeed, (uint8*)&number,
454 			sizeof(number));
455 		checksum = calculate_crc32c(checksum, (uint8*)group, offset);
456 		checksum = calculate_crc32c(checksum, (uint8*)&dummy, sizeof(dummy));
457 		if (fGroupDescriptorSize > offset + sizeof(dummy)) {
458 			checksum = calculate_crc32c(checksum, (uint8*)group + offsetExt4,
459 				fGroupDescriptorSize - offsetExt4);
460 		}
461 		return checksum & 0xffff;
462 	} else if (HasChecksumFeature()) {
463 		uint16 checksum = calculate_crc(0xffff, fSuperBlock.uuid,
464 			sizeof(fSuperBlock.uuid));
465 		checksum = calculate_crc(checksum, (uint8*)&number, sizeof(number));
466 		checksum = calculate_crc(checksum, (uint8*)group, offset);
467 		if (Has64bitFeature() && fGroupDescriptorSize > offsetExt4) {
468 			checksum = calculate_crc(checksum, (uint8*)group + offsetExt4,
469 				fGroupDescriptorSize - offsetExt4);
470 		}
471 		return checksum;
472 	}
473 	return 0;
474 }
475 
476 
477 /*!	Makes the requested block group available.
478 	The block groups are loaded on demand, but are kept in memory until the
479 	volume is unmounted; therefore we don't use the block cache.
480 */
481 status_t
482 Volume::GetBlockGroup(int32 index, ext2_block_group** _group)
483 {
484 	if (index < 0 || (uint32)index > fNumGroups)
485 		return B_BAD_VALUE;
486 
487 	int32 blockIndex = index / fGroupsPerBlock;
488 	int32 blockOffset = index % fGroupsPerBlock;
489 
490 	MutexLocker _(fLock);
491 
492 	if (fGroupBlocks[blockIndex] == NULL) {
493 		CachedBlock cached(this);
494 		const uint8* block = cached.SetTo(_GroupDescriptorBlock(blockIndex));
495 		if (block == NULL)
496 			return B_IO_ERROR;
497 
498 		fGroupBlocks[blockIndex] = (uint8*)malloc(fBlockSize);
499 		if (fGroupBlocks[blockIndex] == NULL)
500 			return B_NO_MEMORY;
501 
502 		memcpy(fGroupBlocks[blockIndex], block, fBlockSize);
503 
504 		TRACE("group [%" B_PRId32 "]: inode table %" B_PRIu64 "\n", index,
505 			((ext2_block_group*)(fGroupBlocks[blockIndex] + blockOffset
506 				* fGroupDescriptorSize))->InodeTable(Has64bitFeature()));
507 	}
508 
509 	*_group = (ext2_block_group*)(fGroupBlocks[blockIndex]
510 		+ blockOffset * fGroupDescriptorSize);
511 	if (HasChecksumFeature()
512 		&& (*_group)->checksum != _GroupCheckSum(*_group, index)) {
513 		return B_BAD_DATA;
514 	}
515 	return B_OK;
516 }
517 
518 
519 status_t
520 Volume::WriteBlockGroup(Transaction& transaction, int32 index)
521 {
522 	if (index < 0 || (uint32)index > fNumGroups)
523 		return B_BAD_VALUE;
524 
525 	TRACE("Volume::WriteBlockGroup()\n");
526 
527 	int32 blockIndex = index / fGroupsPerBlock;
528 	int32 blockOffset = index % fGroupsPerBlock;
529 
530 	MutexLocker _(fLock);
531 
532 	if (fGroupBlocks[blockIndex] == NULL)
533 		return B_BAD_VALUE;
534 
535 	ext2_block_group *group = (ext2_block_group*)(fGroupBlocks[blockIndex]
536 		+ blockOffset * fGroupDescriptorSize);
537 
538 	group->checksum = _GroupCheckSum(group, index);
539 	TRACE("Volume::WriteBlockGroup() checksum 0x%x for group %" B_PRId32 " "
540 		"(free inodes %" B_PRIu32 ", unused %" B_PRIu32 ")\n", group->checksum,
541 		index, group->FreeInodes(Has64bitFeature()),
542 		group->UnusedInodes(Has64bitFeature()));
543 
544 	CachedBlock cached(this);
545 	uint8* block = cached.SetToWritable(transaction,
546 		_GroupDescriptorBlock(blockIndex));
547 	if (block == NULL)
548 		return B_IO_ERROR;
549 
550 	memcpy(block, (const uint8*)fGroupBlocks[blockIndex], fBlockSize);
551 
552 	// TODO: Write copies
553 
554 	return B_OK;
555 }
556 
557 
558 status_t
559 Volume::ActivateLargeFiles(Transaction& transaction)
560 {
561 	if ((fSuperBlock.ReadOnlyFeatures()
562 		& EXT2_READ_ONLY_FEATURE_LARGE_FILE) != 0)
563 		return B_OK;
564 
565 	fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures()
566 		| EXT2_READ_ONLY_FEATURE_LARGE_FILE);
567 
568 	return WriteSuperBlock(transaction);
569 }
570 
571 
572 status_t
573 Volume::ActivateDirNLink(Transaction& transaction)
574 {
575 	if ((fSuperBlock.ReadOnlyFeatures()
576 		& EXT2_READ_ONLY_FEATURE_DIR_NLINK) != 0)
577 		return B_OK;
578 
579 	fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures()
580 		| EXT2_READ_ONLY_FEATURE_DIR_NLINK);
581 
582 	return WriteSuperBlock(transaction);
583 }
584 
585 
586 status_t
587 Volume::SaveOrphan(Transaction& transaction, ino_t newID, ino_t& oldID)
588 {
589 	oldID = fSuperBlock.LastOrphan();
590 	TRACE("Volume::SaveOrphan(): Old: %d, New: %d\n", (int)oldID, (int)newID);
591 	fSuperBlock.SetLastOrphan(newID);
592 
593 	return WriteSuperBlock(transaction);
594 }
595 
596 
597 status_t
598 Volume::RemoveOrphan(Transaction& transaction, ino_t id)
599 {
600 	ino_t currentID = fSuperBlock.LastOrphan();
601 	TRACE("Volume::RemoveOrphan(): ID: %d\n", (int)id);
602 	if (currentID == 0)
603 		return B_OK;
604 
605 	CachedBlock cached(this);
606 
607 	off_t blockNum;
608 	status_t status = GetInodeBlock(currentID, blockNum);
609 	if (status != B_OK)
610 		return status;
611 
612 	uint8* block = cached.SetToWritable(transaction, blockNum);
613 	if (block == NULL)
614 		return B_IO_ERROR;
615 
616 	ext2_inode* inode = (ext2_inode*)(block
617 		+ InodeBlockIndex(currentID) * InodeSize());
618 
619 	if (currentID == id) {
620 		TRACE("Volume::RemoveOrphan(): First entry. Updating head to: %d\n",
621 			(int)inode->NextOrphan());
622 		fSuperBlock.SetLastOrphan(inode->NextOrphan());
623 
624 		return WriteSuperBlock(transaction);
625 	}
626 
627 	currentID = inode->NextOrphan();
628 	if (currentID == 0)
629 		return B_OK;
630 
631 	do {
632 		off_t lastBlockNum = blockNum;
633 		status = GetInodeBlock(currentID, blockNum);
634 		if (status != B_OK)
635 			return status;
636 
637 		if (blockNum != lastBlockNum) {
638 			block = cached.SetToWritable(transaction, blockNum);
639 			if (block == NULL)
640 				return B_IO_ERROR;
641 		}
642 
643 		ext2_inode* inode = (ext2_inode*)(block
644 			+ InodeBlockIndex(currentID) * InodeSize());
645 
646 		currentID = inode->NextOrphan();
647 		if (currentID == 0)
648 			return B_OK;
649 	} while (currentID != id);
650 
651 	CachedBlock cachedRemoved(this);
652 
653 	status = GetInodeBlock(id, blockNum);
654 	if (status != B_OK)
655 		return status;
656 
657 	uint8* removedBlock = cachedRemoved.SetToWritable(transaction, blockNum);
658 	if (removedBlock == NULL)
659 		return B_IO_ERROR;
660 
661 	ext2_inode* removedInode = (ext2_inode*)(removedBlock
662 		+ InodeBlockIndex(id) * InodeSize());
663 
664 	// Next orphan is stored inside deletion time
665 	inode->deletion_time = removedInode->deletion_time;
666 	TRACE("Volume::RemoveOrphan(): Updated pointer to %d\n",
667 		(int)inode->NextOrphan());
668 
669 	return status;
670 }
671 
672 
673 status_t
674 Volume::AllocateInode(Transaction& transaction, Inode* parent, int32 mode,
675 	ino_t& id)
676 {
677 	status_t status = fInodeAllocator.New(transaction, parent, mode, id);
678 	if (status != B_OK)
679 		return status;
680 
681 	--fFreeInodes;
682 
683 	return WriteSuperBlock(transaction);
684 }
685 
686 
687 status_t
688 Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory)
689 {
690 	status_t status = fInodeAllocator.Free(transaction, id, isDirectory);
691 	if (status != B_OK)
692 		return status;
693 
694 	++fFreeInodes;
695 
696 	return WriteSuperBlock(transaction);
697 }
698 
699 
700 status_t
701 Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum,
702 	uint32& blockGroup, fsblock_t& start, uint32& length)
703 {
704 	TRACE("Volume::AllocateBlocks()\n");
705 	if (IsReadOnly())
706 		return B_READ_ONLY_DEVICE;
707 
708 	TRACE("Volume::AllocateBlocks(): Calling the block allocator\n");
709 
710 	status_t status = fBlockAllocator->AllocateBlocks(transaction, minimum,
711 		maximum, blockGroup, start, length);
712 	if (status != B_OK)
713 		return status;
714 
715 	TRACE("Volume::AllocateBlocks(): Allocated %" B_PRIu32 " blocks\n",
716 		length);
717 
718 	fFreeBlocks -= length;
719 
720 	return WriteSuperBlock(transaction);
721 }
722 
723 
724 status_t
725 Volume::FreeBlocks(Transaction& transaction, fsblock_t start, uint32 length)
726 {
727 	TRACE("Volume::FreeBlocks(%" B_PRIu64 ", %" B_PRIu32 ")\n", start, length);
728 	if (IsReadOnly())
729 		return B_READ_ONLY_DEVICE;
730 
731 	status_t status = fBlockAllocator->Free(transaction, start, length);
732 	if (status != B_OK)
733 		return status;
734 
735 	TRACE("Volume::FreeBlocks(): number of free blocks (before): %" B_PRIdOFF
736 		"\n", fFreeBlocks);
737 	fFreeBlocks += length;
738 	TRACE("Volume::FreeBlocks(): number of free blocks (after): %" B_PRIdOFF
739 		"\n", fFreeBlocks);
740 
741 	return WriteSuperBlock(transaction);
742 }
743 
744 
745 status_t
746 Volume::LoadSuperBlock()
747 {
748 	CachedBlock cached(this);
749 	const uint8* block = cached.SetTo(fFirstDataBlock);
750 
751 	if (block == NULL)
752 		return B_IO_ERROR;
753 
754 	if (fFirstDataBlock == 0)
755 		memcpy(&fSuperBlock, block + 1024, sizeof(fSuperBlock));
756 	else
757 		memcpy(&fSuperBlock, block, sizeof(fSuperBlock));
758 
759 	fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
760 	fFreeInodes = fSuperBlock.FreeInodes();
761 
762 	return B_OK;
763 }
764 
765 
766 status_t
767 Volume::WriteSuperBlock(Transaction& transaction)
768 {
769 	TRACE("Volume::WriteSuperBlock()\n");
770 	fSuperBlock.SetFreeBlocks(fFreeBlocks, Has64bitFeature());
771 	fSuperBlock.SetFreeInodes(fFreeInodes);
772 	// TODO: Rest of fields that can be modified
773 
774 	if (HasMetaGroupChecksumFeature()) {
775 		fSuperBlock.checksum = calculate_crc32c(0xffffffff, (uint8*)&fSuperBlock,
776 			offsetof(struct ext2_super_block, checksum));
777 	}
778 
779 	TRACE("Volume::WriteSuperBlock(): free blocks: %" B_PRIu64 ", free inodes:"
780 		" %" B_PRIu32 "\n", fSuperBlock.FreeBlocks(Has64bitFeature()),
781 		fSuperBlock.FreeInodes());
782 
783 	CachedBlock cached(this);
784 	uint8* block = cached.SetToWritable(transaction, fFirstDataBlock);
785 
786 	if (block == NULL)
787 		return B_IO_ERROR;
788 
789 	TRACE("Volume::WriteSuperBlock(): first data block: %" B_PRIu32 ", block:"
790 		" %p, superblock: %p\n", fFirstDataBlock, block, &fSuperBlock);
791 
792 	if (fFirstDataBlock == 0)
793 		memcpy(block + 1024, &fSuperBlock, sizeof(fSuperBlock));
794 	else
795 		memcpy(block, &fSuperBlock, sizeof(fSuperBlock));
796 
797 	TRACE("Volume::WriteSuperBlock(): Done\n");
798 
799 	return B_OK;
800 }
801 
802 
803 void
804 Volume::_SuperBlockChecksumSeed()
805 {
806 	if (HasChecksumSeedFeature()) {
807 		fChecksumSeed = fSuperBlock.checksum_seed;
808 	} else if (HasMetaGroupChecksumFeature()) {
809 		fChecksumSeed = calculate_crc32c(0xffffffff, (uint8*)fSuperBlock.uuid,
810 			sizeof(fSuperBlock.uuid));
811 	} else
812 		fChecksumSeed = 0;
813 }
814 
815 
816 bool
817 Volume::_VerifySuperBlock()
818 {
819 	if (!HasMetaGroupChecksumFeature())
820 		return true;
821 
822 	uint32 checksum = calculate_crc32c(0xffffffff, (uint8*)&fSuperBlock,
823 		offsetof(struct ext2_super_block, checksum));
824 	return checksum == fSuperBlock.checksum;
825 }
826 
827 
828 status_t
829 Volume::FlushDevice()
830 {
831 	TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache);
832 	return block_cache_sync(fBlockCache);
833 }
834 
835 
836 status_t
837 Volume::Sync()
838 {
839 	TRACE("Volume::Sync()\n");
840 	return fJournal->Uninit();
841 }
842 
843 
844 //	#pragma mark - Disk scanning and initialization
845 
846 
847 /*static*/ status_t
848 Volume::Identify(int fd, ext2_super_block* superBlock)
849 {
850 	if (read_pos(fd, EXT2_SUPER_BLOCK_OFFSET, superBlock,
851 			sizeof(ext2_super_block)) != sizeof(ext2_super_block))
852 		return B_IO_ERROR;
853 
854 	if (!superBlock->IsValid()) {
855 		FATAL("invalid superblock!\n");
856 		return B_BAD_VALUE;
857 	}
858 
859 	if (_UnsupportedIncompatibleFeatures(*superBlock) != 0)
860 		return B_UNSUPPORTED;
861 
862 	return B_OK;
863 }
864 
865 
866 void
867 Volume::TransactionDone(bool success)
868 {
869 	if (!success) {
870 		status_t status = LoadSuperBlock();
871 		if (status != B_OK)
872 			panic("Failed to reload ext2 superblock.\n");
873 	}
874 }
875 
876 
877 void
878 Volume::RemovedFromTransaction()
879 {
880 	// TODO: Does it make a difference?
881 }
882