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