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