btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / ext2 / Volume.cpp
blob1477ee0cd2bc49b34dc69efe6d208e95dfbede9f
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 */
8 //! Superblock, mounting, etc.
11 #include "Volume.h"
13 #include <errno.h>
14 #include <new>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
19 #include <fs_cache.h>
20 #include <fs_volume.h>
22 #include <util/AutoLock.h>
24 #include "CachedBlock.h"
25 #include "CRCTable.h"
26 #include "Inode.h"
27 #include "InodeJournal.h"
28 #include "NoJournal.h"
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)
40 class DeviceOpener {
41 public:
42 DeviceOpener(int fd, int mode);
43 DeviceOpener(const char* device, int mode);
44 ~DeviceOpener();
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);
51 void Keep();
53 int Device() const { return fDevice; }
54 int Mode() const { return fMode; }
55 bool IsReadOnly() const { return _IsReadOnly(fMode); }
57 status_t GetSize(off_t* _size, uint32* _blockSize = NULL);
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;}
65 int fDevice;
66 int fMode;
67 void* fBlockCache;
71 DeviceOpener::DeviceOpener(const char* device, int mode)
73 fBlockCache(NULL)
75 Open(device, mode);
79 DeviceOpener::DeviceOpener(int fd, int mode)
81 fBlockCache(NULL)
83 Open(fd, mode);
87 DeviceOpener::~DeviceOpener()
89 if (fDevice >= 0) {
90 RemoveCache(false);
91 close(fDevice);
96 int
97 DeviceOpener::Open(const char* device, int mode)
99 fDevice = open(device, mode | O_NOCACHE);
100 if (fDevice < 0)
101 fDevice = errno;
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);
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);
124 return fDevice;
129 DeviceOpener::Open(int fd, int mode)
131 fDevice = dup(fd);
132 if (fDevice < 0)
133 return errno;
135 fMode = mode;
137 return fDevice;
141 void*
142 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
144 return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
145 IsReadOnly());
149 void
150 DeviceOpener::RemoveCache(bool allowWrites)
152 if (fBlockCache == NULL)
153 return;
155 block_cache_delete(fBlockCache, allowWrites);
156 fBlockCache = NULL;
160 void
161 DeviceOpener::Keep()
163 fDevice = -1;
167 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
168 to compute the size, or fstat() if that failed.
170 status_t
171 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
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;
180 if (_size)
181 *_size = stat.st_size;
182 if (_blockSize) // that shouldn't cause us any problems
183 *_blockSize = 512;
185 return B_OK;
188 if (_size) {
189 *_size = 1ULL * geometry.head_count * geometry.cylinder_count
190 * geometry.sectors_per_track * geometry.bytes_per_sector;
192 if (_blockSize)
193 *_blockSize = geometry.bytes_per_sector;
195 return B_OK;
199 // #pragma mark -
202 bool
203 ext2_super_block::IsValid()
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;
214 return true;
218 // #pragma mark -
221 Volume::Volume(fs_volume* volume)
223 fFSVolume(volume),
224 fBlockAllocator(NULL),
225 fInodeAllocator(this),
226 fJournalInode(NULL),
227 fFlags(0),
228 fGroupBlocks(NULL),
229 fRootNode(NULL)
231 mutex_init(&fLock, "ext2 volume");
235 Volume::~Volume()
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]);
245 free(fGroupBlocks);
250 bool
251 Volume::IsValidSuperBlock()
253 return fSuperBlock.IsValid();
257 bool
258 Volume::HasExtendedAttributes() const
260 return (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR) != 0;
264 const char*
265 Volume::Name() const
267 if (fSuperBlock.name[0])
268 return fSuperBlock.name;
270 return fName;
274 void
275 Volume::SetName(const char* name)
277 strlcpy(fSuperBlock.name, name, sizeof(fSuperBlock.name));
281 status_t
282 Volume::Mount(const char* deviceName, uint32 flags)
284 // flags |= B_MOUNT_READ_ONLY;
285 // we only support read-only for now
287 if ((flags & B_MOUNT_READ_ONLY) != 0) {
288 TRACE("Volume::Mount(): Read only\n");
289 } else {
290 TRACE("Volume::Mount(): Read write\n");
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;
301 if (opener.IsReadOnly())
302 fFlags |= VOLUME_READ_ONLY;
304 TRACE("features %" B_PRIx32 ", incompatible features %" B_PRIx32
305 ", read-only features %" B_PRIx32 "\n",
306 fSuperBlock.CompatibleFeatures(), fSuperBlock.IncompatibleFeatures(),
307 fSuperBlock.ReadOnlyFeatures());
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;
316 // check read-only features if mounting read-write
317 if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0)
318 return B_UNSUPPORTED;
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();
327 fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
328 fFreeInodes = fSuperBlock.FreeInodes();
330 off_t numBlocks = fSuperBlock.NumBlocks(Has64bitFeature()) - fFirstDataBlock;
331 uint32 blocksPerGroup = fSuperBlock.BlocksPerGroup();
332 fNumGroups = numBlocks / blocksPerGroup;
333 if (numBlocks % blocksPerGroup != 0)
334 fNumGroups++;
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();
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);
349 uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) / fGroupsPerBlock;
351 fGroupBlocks = (uint8**)malloc(blockCount * sizeof(uint8*));
352 if (fGroupBlocks == NULL)
353 return B_NO_MEMORY;
355 memset(fGroupBlocks, 0, blockCount * sizeof(uint8*));
356 fInodesPerBlock = fBlockSize / InodeSize();
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;
366 fBlockCache = opener.InitCache(NumBlocks(), fBlockSize);
367 if (fBlockCache == NULL)
368 return B_ERROR;
370 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
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());
380 if (fJournalInode == NULL)
381 return B_NO_MEMORY;
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;
390 } else {
391 TRACE("Opening a fake journal (NoJournal).\n");
392 fJournal = new(std::nothrow) NoJournal(this);
395 if (fJournal == NULL) {
396 TRACE("No memory to create the journal\n");
397 return B_NO_MEMORY;
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;
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;
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;
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();
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);
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;
449 // all went fine
450 opener.Keep();
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';
465 double size = double((10 * diskSize + divisor - 1) / divisor);
466 // %g in the kernel does not support precision...
468 snprintf(fName, sizeof(fName), "%g %cB Ext2 Volume",
469 size / 10, unit);
472 return B_OK;
476 status_t
477 Volume::Unmount()
479 TRACE("Volume::Unmount()\n");
481 status_t status = fJournal->Uninit();
483 delete fJournal;
484 delete fJournalInode;
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);
493 TRACE("Volume::Unmount(): Done\n");
494 return status;
498 status_t
499 Volume::GetInodeBlock(ino_t id, off_t& block)
501 ext2_block_group* group;
502 status_t status = GetBlockGroup((id - 1) / fSuperBlock.InodesPerGroup(),
503 &group);
504 if (status != B_OK)
505 return status;
507 block = group->InodeTable(Has64bitFeature())
508 + ((id - 1) % fSuperBlock.InodesPerGroup()) / fInodesPerBlock;
509 return B_OK;
513 uint32
514 Volume::InodeBlockIndex(ino_t id) const
516 return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock;
520 /*static*/ uint32
521 Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock)
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;
532 if (unsupported != 0) {
533 FATAL("ext2: incompatible features not supported: %" B_PRIx32
534 " (extents %x)\n", unsupported, EXT2_INCOMPATIBLE_FEATURE_EXTENTS);
537 return unsupported;
541 /*static*/ uint32
542 Volume::_UnsupportedReadOnlyFeatures(ext2_super_block& superBlock)
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
553 uint32 unsupported = superBlock.ReadOnlyFeatures() & ~supportedReadOnly;
555 if (unsupported != 0) {
556 FATAL("ext2: readonly features not supported: %" B_PRIx32 "\n",
557 unsupported);
560 return unsupported;
564 uint32
565 Volume::_GroupDescriptorBlock(uint32 blockIndex)
567 if ((fSuperBlock.IncompatibleFeatures()
568 & EXT2_INCOMPATIBLE_FEATURE_META_GROUP) == 0
569 || blockIndex < fSuperBlock.FirstMetaBlockGroup())
570 return fFirstDataBlock + blockIndex + 1;
572 panic("meta block");
573 return 0;
577 uint16
578 Volume::_GroupCheckSum(ext2_block_group *group, int32 index)
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);
592 return checksum;
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.
600 status_t
601 Volume::GetBlockGroup(int32 index, ext2_block_group** _group)
603 if (index < 0 || (uint32)index > fNumGroups)
604 return B_BAD_VALUE;
606 int32 blockIndex = index / fGroupsPerBlock;
607 int32 blockOffset = index % fGroupsPerBlock;
609 MutexLocker _(fLock);
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;
617 fGroupBlocks[blockIndex] = (uint8*)malloc(fBlockSize);
618 if (fGroupBlocks[blockIndex] == NULL)
619 return B_NO_MEMORY;
621 memcpy(fGroupBlocks[blockIndex], block, fBlockSize);
623 TRACE("group [%" B_PRId32 "]: inode table %" B_PRIu64 "\n", index,
624 ((ext2_block_group*)(fGroupBlocks[blockIndex] + blockOffset
625 * fGroupDescriptorSize))->InodeTable(Has64bitFeature()));
628 *_group = (ext2_block_group*)(fGroupBlocks[blockIndex]
629 + blockOffset * fGroupDescriptorSize);
630 if (HasChecksumFeature()
631 && (*_group)->checksum != _GroupCheckSum(*_group, index)) {
632 return B_BAD_DATA;
634 return B_OK;
638 status_t
639 Volume::WriteBlockGroup(Transaction& transaction, int32 index)
641 if (index < 0 || (uint32)index > fNumGroups)
642 return B_BAD_VALUE;
644 TRACE("Volume::WriteBlockGroup()\n");
646 int32 blockIndex = index / fGroupsPerBlock;
647 int32 blockOffset = index % fGroupsPerBlock;
649 MutexLocker _(fLock);
651 if (fGroupBlocks[blockIndex] == NULL)
652 return B_BAD_VALUE;
654 ext2_block_group *group = (ext2_block_group*)(fGroupBlocks[blockIndex]
655 + blockOffset * fGroupDescriptorSize);
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()));
663 CachedBlock cached(this);
664 uint8* block = cached.SetToWritable(transaction,
665 _GroupDescriptorBlock(blockIndex));
666 if (block == NULL)
667 return B_IO_ERROR;
669 memcpy(block, (const uint8*)fGroupBlocks[blockIndex], fBlockSize);
671 // TODO: Write copies
673 return B_OK;
677 status_t
678 Volume::ActivateLargeFiles(Transaction& transaction)
680 if ((fSuperBlock.ReadOnlyFeatures()
681 & EXT2_READ_ONLY_FEATURE_LARGE_FILE) != 0)
682 return B_OK;
684 fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures()
685 | EXT2_READ_ONLY_FEATURE_LARGE_FILE);
687 return WriteSuperBlock(transaction);
691 status_t
692 Volume::ActivateDirNLink(Transaction& transaction)
694 if ((fSuperBlock.ReadOnlyFeatures()
695 & EXT2_READ_ONLY_FEATURE_DIR_NLINK) != 0)
696 return B_OK;
698 fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures()
699 | EXT2_READ_ONLY_FEATURE_DIR_NLINK);
701 return WriteSuperBlock(transaction);
705 status_t
706 Volume::SaveOrphan(Transaction& transaction, ino_t newID, ino_t& oldID)
708 oldID = fSuperBlock.LastOrphan();
709 TRACE("Volume::SaveOrphan(): Old: %d, New: %d\n", (int)oldID, (int)newID);
710 fSuperBlock.SetLastOrphan(newID);
712 return WriteSuperBlock(transaction);
716 status_t
717 Volume::RemoveOrphan(Transaction& transaction, ino_t id)
719 ino_t currentID = fSuperBlock.LastOrphan();
720 TRACE("Volume::RemoveOrphan(): ID: %d\n", (int)id);
721 if (currentID == 0)
722 return B_OK;
724 CachedBlock cached(this);
726 off_t blockNum;
727 status_t status = GetInodeBlock(currentID, blockNum);
728 if (status != B_OK)
729 return status;
731 uint8* block = cached.SetToWritable(transaction, blockNum);
732 if (block == NULL)
733 return B_IO_ERROR;
735 ext2_inode* inode = (ext2_inode*)(block
736 + InodeBlockIndex(currentID) * InodeSize());
738 if (currentID == id) {
739 TRACE("Volume::RemoveOrphan(): First entry. Updating head to: %d\n",
740 (int)inode->NextOrphan());
741 fSuperBlock.SetLastOrphan(inode->NextOrphan());
743 return WriteSuperBlock(transaction);
746 currentID = inode->NextOrphan();
747 if (currentID == 0)
748 return B_OK;
750 do {
751 off_t lastBlockNum = blockNum;
752 status = GetInodeBlock(currentID, blockNum);
753 if (status != B_OK)
754 return status;
756 if (blockNum != lastBlockNum) {
757 block = cached.SetToWritable(transaction, blockNum);
758 if (block == NULL)
759 return B_IO_ERROR;
762 ext2_inode* inode = (ext2_inode*)(block
763 + InodeBlockIndex(currentID) * InodeSize());
765 currentID = inode->NextOrphan();
766 if (currentID == 0)
767 return B_OK;
768 } while(currentID != id);
770 CachedBlock cachedRemoved(this);
772 status = GetInodeBlock(id, blockNum);
773 if (status != B_OK)
774 return status;
776 uint8* removedBlock = cachedRemoved.SetToWritable(transaction, blockNum);
777 if (removedBlock == NULL)
778 return B_IO_ERROR;
780 ext2_inode* removedInode = (ext2_inode*)(removedBlock
781 + InodeBlockIndex(id) * InodeSize());
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());
788 return status;
792 status_t
793 Volume::AllocateInode(Transaction& transaction, Inode* parent, int32 mode,
794 ino_t& id)
796 status_t status = fInodeAllocator.New(transaction, parent, mode, id);
797 if (status != B_OK)
798 return status;
800 --fFreeInodes;
802 return WriteSuperBlock(transaction);
806 status_t
807 Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory)
809 status_t status = fInodeAllocator.Free(transaction, id, isDirectory);
810 if (status != B_OK)
811 return status;
813 ++fFreeInodes;
815 return WriteSuperBlock(transaction);
819 status_t
820 Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum,
821 uint32& blockGroup, fsblock_t& start, uint32& length)
823 TRACE("Volume::AllocateBlocks()\n");
824 if (IsReadOnly())
825 return B_READ_ONLY_DEVICE;
827 TRACE("Volume::AllocateBlocks(): Calling the block allocator\n");
829 status_t status = fBlockAllocator->AllocateBlocks(transaction, minimum,
830 maximum, blockGroup, start, length);
831 if (status != B_OK)
832 return status;
834 TRACE("Volume::AllocateBlocks(): Allocated %" B_PRIu32 " blocks\n",
835 length);
837 fFreeBlocks -= length;
839 return WriteSuperBlock(transaction);
843 status_t
844 Volume::FreeBlocks(Transaction& transaction, fsblock_t start, uint32 length)
846 TRACE("Volume::FreeBlocks(%" B_PRIu64 ", %" B_PRIu32 ")\n", start, length);
847 if (IsReadOnly())
848 return B_READ_ONLY_DEVICE;
850 status_t status = fBlockAllocator->Free(transaction, start, length);
851 if (status != B_OK)
852 return status;
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);
860 return WriteSuperBlock(transaction);
864 status_t
865 Volume::LoadSuperBlock()
867 CachedBlock cached(this);
868 const uint8* block = cached.SetTo(fFirstDataBlock);
870 if (block == NULL)
871 return B_IO_ERROR;
873 if (fFirstDataBlock == 0)
874 memcpy(&fSuperBlock, block + 1024, sizeof(fSuperBlock));
875 else
876 memcpy(&fSuperBlock, block, sizeof(fSuperBlock));
878 fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
879 fFreeInodes = fSuperBlock.FreeInodes();
881 return B_OK;
885 status_t
886 Volume::WriteSuperBlock(Transaction& transaction)
888 TRACE("Volume::WriteSuperBlock()\n");
889 fSuperBlock.SetFreeBlocks(fFreeBlocks, Has64bitFeature());
890 fSuperBlock.SetFreeInodes(fFreeInodes);
891 // TODO: Rest of fields that can be modified
893 TRACE("Volume::WriteSuperBlock(): free blocks: %" B_PRIu64 ", free inodes:"
894 " %" B_PRIu32 "\n", fSuperBlock.FreeBlocks(Has64bitFeature()),
895 fSuperBlock.FreeInodes());
897 CachedBlock cached(this);
898 uint8* block = cached.SetToWritable(transaction, fFirstDataBlock);
900 if (block == NULL)
901 return B_IO_ERROR;
903 TRACE("Volume::WriteSuperBlock(): first data block: %" B_PRIu32 ", block:"
904 " %p, superblock: %p\n", fFirstDataBlock, block, &fSuperBlock);
906 if (fFirstDataBlock == 0)
907 memcpy(block + 1024, &fSuperBlock, sizeof(fSuperBlock));
908 else
909 memcpy(block, &fSuperBlock, sizeof(fSuperBlock));
911 TRACE("Volume::WriteSuperBlock(): Done\n");
913 return B_OK;
917 status_t
918 Volume::FlushDevice()
920 TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache);
921 return block_cache_sync(fBlockCache);
925 status_t
926 Volume::Sync()
928 TRACE("Volume::Sync()\n");
929 return fJournal->Uninit();
933 // #pragma mark - Disk scanning and initialization
936 /*static*/ status_t
937 Volume::Identify(int fd, ext2_super_block* superBlock)
939 if (read_pos(fd, EXT2_SUPER_BLOCK_OFFSET, superBlock,
940 sizeof(ext2_super_block)) != sizeof(ext2_super_block))
941 return B_IO_ERROR;
943 if (!superBlock->IsValid()) {
944 FATAL("invalid superblock!\n");
945 return B_BAD_VALUE;
948 return _UnsupportedIncompatibleFeatures(*superBlock) == 0
949 ? B_OK : B_UNSUPPORTED;
953 void
954 Volume::TransactionDone(bool success)
956 if (!success) {
957 status_t status = LoadSuperBlock();
958 if (status != B_OK)
959 panic("Failed to reload ext2 superblock.\n");
964 void
965 Volume::RemovedFromTransaction()
967 // TODO: Does it make a difference?