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
44bool
45ext2_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
66Volume::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
80Volume::~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
95bool
96Volume::IsValidSuperBlock()
97{
98	return fSuperBlock.IsValid();
99}
100
101
102bool
103Volume::HasExtendedAttributes() const
104{
105	return (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR) != 0;
106}
107
108
109const char*
110Volume::Name() const
111{
112	// The name may be empty, in that case, userspace will generate one.
113	return fSuperBlock.name;
114}
115
116
117void
118Volume::SetName(const char* name)
119{
120	strlcpy(fSuperBlock.name, name, sizeof(fSuperBlock.name));
121}
122
123
124status_t
125Volume::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
338status_t
339Volume::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
362status_t
363Volume::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
379uint32
380Volume::InodeBlockIndex(ino_t id) const
381{
382	return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock;
383}
384
385
386/*static*/ uint32
387Volume::_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
409Volume::_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
432uint32
433Volume::_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
445uint16
446Volume::_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*/
481status_t
482Volume::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
519status_t
520Volume::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
558status_t
559Volume::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
572status_t
573Volume::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
586status_t
587Volume::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
597status_t
598Volume::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
673status_t
674Volume::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
687status_t
688Volume::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
700status_t
701Volume::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
724status_t
725Volume::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
745status_t
746Volume::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
766status_t
767Volume::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
803void
804Volume::_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
816bool
817Volume::_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
828status_t
829Volume::FlushDevice()
830{
831	TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache);
832	return block_cache_sync(fBlockCache);
833}
834
835
836status_t
837Volume::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
848Volume::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
866void
867Volume::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
877void
878Volume::RemovedFromTransaction()
879{
880	// TODO: Does it make a difference?
881}
882