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