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