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