xref: /haiku/src/add-ons/kernel/file_systems/ext2/Volume.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
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 //! Super block, 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 
40 class DeviceOpener {
41 public:
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 
59 private:
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 
71 DeviceOpener::DeviceOpener(const char* device, int mode)
72 	:
73 	fBlockCache(NULL)
74 {
75 	Open(device, mode);
76 }
77 
78 
79 DeviceOpener::DeviceOpener(int fd, int mode)
80 	:
81 	fBlockCache(NULL)
82 {
83 	Open(fd, mode);
84 }
85 
86 
87 DeviceOpener::~DeviceOpener()
88 {
89 	if (fDevice >= 0) {
90 		RemoveCache(false);
91 		close(fDevice);
92 	}
93 }
94 
95 
96 int
97 DeviceOpener::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 
128 int
129 DeviceOpener::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 
141 void*
142 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
143 {
144 	return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
145 		IsReadOnly());
146 }
147 
148 
149 void
150 DeviceOpener::RemoveCache(bool allowWrites)
151 {
152 	if (fBlockCache == NULL)
153 		return;
154 
155 	block_cache_delete(fBlockCache, allowWrites);
156 	fBlockCache = NULL;
157 }
158 
159 
160 void
161 DeviceOpener::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 */
170 status_t
171 DeviceOpener::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 
202 bool
203 ext2_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 
216 Volume::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 
230 Volume::~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 
245 bool
246 Volume::IsValidSuperBlock()
247 {
248 	return fSuperBlock.IsValid();
249 }
250 
251 
252 bool
253 Volume::HasExtendedAttributes() const
254 {
255 	return (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR) != 0;
256 }
257 
258 
259 const char*
260 Volume::Name() const
261 {
262 	if (fSuperBlock.name[0])
263 		return fSuperBlock.name;
264 
265 	return fName;
266 }
267 
268 
269 void
270 Volume::SetName(const char* name)
271 {
272 	strlcpy(fSuperBlock.name, name, sizeof(fSuperBlock.name));
273 }
274 
275 
276 status_t
277 Volume::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 super block
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_NOT_SUPPORTED;
313 
314 	// initialize short hands to the super block (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_NOT_SUPPORTED;
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 
469 status_t
470 Volume::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 
491 status_t
492 Volume::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 
506 uint32
507 Volume::InodeBlockIndex(ino_t id) const
508 {
509 	return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock;
510 }
511 
512 
513 /*static*/ uint32
514 Volume::_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
535 Volume::_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 
555 uint32
556 Volume::_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 
568 uint16
569 Volume::_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 */
591 status_t
592 Volume::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 
629 status_t
630 Volume::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 
668 status_t
669 Volume::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 
682 status_t
683 Volume::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 
696 status_t
697 Volume::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 
707 status_t
708 Volume::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 
783 status_t
784 Volume::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 
797 status_t
798 Volume::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 
810 status_t
811 Volume::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 
833 status_t
834 Volume::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 
854 status_t
855 Volume::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 
875 status_t
876 Volume::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 
906 status_t
907 Volume::FlushDevice()
908 {
909 	TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache);
910 	return block_cache_sync(fBlockCache);
911 }
912 
913 
914 status_t
915 Volume::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
926 Volume::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 super block!\n");
934 		return B_BAD_VALUE;
935 	}
936 
937 	return _UnsupportedIncompatibleFeatures(*superBlock) == 0
938 		? B_OK : B_NOT_SUPPORTED;
939 }
940 
941 
942 void
943 Volume::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 
953 void
954 Volume::RemovedFromTransaction()
955 {
956 	// TODO: Does it make a difference?
957 }
958