status_t Volume::SaveOrphan(Transaction& transaction, ino_t newID, ino_t& oldID) { oldID = fSuperBlock.LastOrphan(); TRACE("Volume::SaveOrphan(): Old: %d, New: %d\n", (int)oldID, (int)newID); fSuperBlock.SetLastOrphan(newID); return WriteSuperBlock(transaction); }
status_t Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory) { status_t status = fInodeAllocator.Free(transaction, id, isDirectory); if (status != B_OK) return status; ++fFreeInodes; return WriteSuperBlock(transaction); }
status_t Volume::AllocateInode(Transaction& transaction, Inode* parent, int32 mode, ino_t& id) { status_t status = fInodeAllocator.New(transaction, parent, mode, id); if (status != B_OK) return status; --fFreeInodes; return WriteSuperBlock(transaction); }
status_t Volume::ActivateDirNLink(Transaction& transaction) { if ((fSuperBlock.ReadOnlyFeatures() & EXT2_READ_ONLY_FEATURE_DIR_NLINK) != 0) return B_OK; fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures() | EXT2_READ_ONLY_FEATURE_DIR_NLINK); return WriteSuperBlock(transaction); }
status_t Volume::ActivateLargeFiles(Transaction& transaction) { if ((fSuperBlock.ReadOnlyFeatures() & EXT2_READ_ONLY_FEATURE_LARGE_FILE) != 0) return B_OK; fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures() | EXT2_READ_ONLY_FEATURE_LARGE_FILE); return WriteSuperBlock(transaction); }
status_t Volume::CreateIndicesRoot(Transaction& transaction) { off_t id; status_t status = Inode::Create(transaction, NULL, NULL, S_INDEX_DIR | S_STR_INDEX | S_DIRECTORY | 0700, 0, 0, NULL, &id, &fIndicesNode, NULL, BFS_DO_NOT_PUBLISH_VNODE); if (status < B_OK) RETURN_ERROR(status); fSuperBlock.indices = ToBlockRun(id); return WriteSuperBlock(); }
status_t Volume::FreeBlocks(Transaction& transaction, fsblock_t start, uint32 length) { TRACE("Volume::FreeBlocks(%llu, %lu)\n", start, length); if (IsReadOnly()) return B_READ_ONLY_DEVICE; status_t status = fBlockAllocator->Free(transaction, start, length); if (status != B_OK) return status; TRACE("Volume::FreeBlocks(): number of free blocks (before): %llu\n", fFreeBlocks); fFreeBlocks += length; TRACE("Volume::FreeBlocks(): number of free blocks (after): %llu\n", fFreeBlocks); return WriteSuperBlock(transaction); }
status_t Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, uint32& blockGroup, fsblock_t& start, uint32& length) { TRACE("Volume::AllocateBlocks()\n"); if (IsReadOnly()) return B_READ_ONLY_DEVICE; TRACE("Volume::AllocateBlocks(): Calling the block allocator\n"); status_t status = fBlockAllocator->AllocateBlocks(transaction, minimum, maximum, blockGroup, start, length); if (status != B_OK) return status; TRACE("Volume::AllocateBlocks(): Allocated %lu blocks\n", length); fFreeBlocks -= length; return WriteSuperBlock(transaction); }
status_t Volume::RemoveOrphan(Transaction& transaction, ino_t id) { ino_t currentID = fSuperBlock.LastOrphan(); TRACE("Volume::RemoveOrphan(): ID: %d\n", (int)id); if (currentID == 0) return B_OK; CachedBlock cached(this); off_t blockNum; status_t status = GetInodeBlock(currentID, blockNum); if (status != B_OK) return status; uint8* block = cached.SetToWritable(transaction, blockNum); if (block == NULL) return B_IO_ERROR; ext2_inode* inode = (ext2_inode*)(block + InodeBlockIndex(currentID) * InodeSize()); if (currentID == id) { TRACE("Volume::RemoveOrphan(): First entry. Updating head to: %d\n", (int)inode->NextOrphan()); fSuperBlock.SetLastOrphan(inode->NextOrphan()); return WriteSuperBlock(transaction); } currentID = inode->NextOrphan(); if (currentID == 0) return B_OK; do { off_t lastBlockNum = blockNum; status = GetInodeBlock(currentID, blockNum); if (status != B_OK) return status; if (blockNum != lastBlockNum) { block = cached.SetToWritable(transaction, blockNum); if (block == NULL) return B_IO_ERROR; } ext2_inode* inode = (ext2_inode*)(block + InodeBlockIndex(currentID) * InodeSize()); currentID = inode->NextOrphan(); if (currentID == 0) return B_OK; } while(currentID != id); CachedBlock cachedRemoved(this); status = GetInodeBlock(id, blockNum); if (status != B_OK) return status; uint8* removedBlock = cachedRemoved.SetToWritable(transaction, blockNum); if (removedBlock == NULL) return B_IO_ERROR; ext2_inode* removedInode = (ext2_inode*)(removedBlock + InodeBlockIndex(id) * InodeSize()); // Next orphan is stored inside deletion time inode->deletion_time = removedInode->deletion_time; TRACE("Volume::RemoveOrphan(): Updated pointer to %d\n", (int)inode->NextOrphan()); return status; }
status_t Volume::Initialize(int fd, const char* name, uint32 blockSize, uint32 flags) { // although there is no really good reason for it, we won't // accept '/' in disk names (mkbfs does this, too - and since // Tracker names mounted volumes like their name) if (strchr(name, '/') != NULL) return B_BAD_VALUE; if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096 && blockSize != 8192) return B_BAD_VALUE; DeviceOpener opener(fd, O_RDWR); if (opener.Device() < B_OK) return B_BAD_VALUE; if (opener.IsReadOnly()) return B_READ_ONLY_DEVICE; fDevice = opener.Device(); uint32 deviceBlockSize; off_t deviceSize; if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK) return B_ERROR; off_t numBlocks = deviceSize / blockSize; // create valid superblock fSuperBlock.Initialize(name, numBlocks, blockSize); // initialize short hands to the superblock (to save byte swapping) fBlockSize = fSuperBlock.BlockSize(); fBlockShift = fSuperBlock.BlockShift(); fAllocationGroupShift = fSuperBlock.AllocationGroupShift(); // determine log size depending on the size of the volume off_t logSize = 2048; if (numBlocks <= 20480) logSize = 512; if (deviceSize > 1LL * 1024 * 1024 * 1024) logSize = 4096; // since the allocator has not been initialized yet, we // cannot use BlockAllocator::BitmapSize() here off_t bitmapBlocks = (numBlocks + blockSize * 8 - 1) / (blockSize * 8); fSuperBlock.log_blocks = ToBlockRun(bitmapBlocks + 1); fSuperBlock.log_blocks.length = HOST_ENDIAN_TO_BFS_INT16(logSize); fSuperBlock.log_start = fSuperBlock.log_end = HOST_ENDIAN_TO_BFS_INT64( ToBlock(Log())); // set the current log pointers, so that journaling will work correctly fLogStart = fSuperBlock.LogStart(); fLogEnd = fSuperBlock.LogEnd(); if (!IsValidSuperBlock()) RETURN_ERROR(B_ERROR); if ((fBlockCache = opener.InitCache(NumBlocks(), fBlockSize)) == NULL) return B_ERROR; fJournal = new(std::nothrow) Journal(this); if (fJournal == NULL || fJournal->InitCheck() < B_OK) RETURN_ERROR(B_ERROR); // ready to write data to disk Transaction transaction(this, 0); if (fBlockAllocator.InitializeAndClearBitmap(transaction) < B_OK) RETURN_ERROR(B_ERROR); off_t id; status_t status = Inode::Create(transaction, NULL, NULL, S_DIRECTORY | 0755, 0, 0, NULL, &id, &fRootNode); if (status < B_OK) RETURN_ERROR(status); fSuperBlock.root_dir = ToBlockRun(id); if ((flags & VOLUME_NO_INDICES) == 0) { // The indices root directory will be created automatically // when the standard indices are created (or any other). Index index(this); status = index.Create(transaction, "name", B_STRING_TYPE); if (status < B_OK) return status; status = index.Create(transaction, "BEOS:APP_SIG", B_STRING_TYPE); if (status < B_OK) return status; status = index.Create(transaction, "last_modified", B_INT64_TYPE); if (status < B_OK) return status; status = index.Create(transaction, "size", B_INT64_TYPE); if (status < B_OK) return status; } status = CreateVolumeID(transaction); if (status < B_OK) return status; status = _EraseUnusedBootBlock(); if (status < B_OK) return status; status = WriteSuperBlock(); if (status < B_OK) return status; status = transaction.Done(); if (status < B_OK) return status; Sync(); opener.RemoveCache(true); return B_OK; }