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