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