| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdlib.h> |
| #include <utility> |
| |
| #include <minfs/block-txn.h> |
| |
| #include "inode-manager.h" |
| |
| namespace minfs { |
| |
| InodeManager::InodeManager(Bcache* bc, blk_t start_block) : |
| bc_(bc), start_block_(start_block) {} |
| InodeManager::~InodeManager() = default; |
| |
| zx_status_t InodeManager::Create(Bcache* bc, SuperblockManager* sb, fs::ReadTxn* txn, |
| AllocatorMetadata metadata, |
| blk_t start_block, size_t inodes, |
| fbl::unique_ptr<InodeManager>* out) { |
| |
| auto mgr = fbl::unique_ptr<InodeManager>(new InodeManager(bc, start_block)); |
| InodeManager* mgr_raw = mgr.get(); |
| |
| auto grow_cb = [mgr_raw](uint32_t pool_size) { |
| return mgr_raw->Grow(pool_size); |
| }; |
| |
| zx_status_t status; |
| fbl::unique_ptr<PersistentStorage> storage(new PersistentStorage(bc, sb, kMinfsInodeSize, |
| std::move(grow_cb), |
| std::move(metadata))); |
| if ((status = Allocator::Create(txn, std::move(storage), &mgr->inode_allocator_)) != ZX_OK) { |
| return status; |
| } |
| |
| #ifdef __Fuchsia__ |
| uint32_t inoblks = (static_cast<uint32_t>(inodes) + kMinfsInodesPerBlock - 1) / |
| kMinfsInodesPerBlock; |
| if ((status = mgr->inode_table_.CreateAndMap(inoblks * kMinfsBlockSize, "minfs-inode-table")) |
| != ZX_OK) { |
| return status; |
| } |
| |
| fuchsia_hardware_block_VmoID vmoid; |
| if ((status = bc->AttachVmo(mgr->inode_table_.vmo(), &vmoid)) != ZX_OK) { |
| return status; |
| } |
| txn->Enqueue(vmoid.id, 0, start_block, inoblks); |
| #endif |
| *out = std::move(mgr); |
| return ZX_OK; |
| } |
| |
| void InodeManager::Update(WriteTxn* txn, ino_t ino, const Inode* inode) { |
| // Obtain the offset of the inode within its containing block |
| const uint32_t off_of_ino = (ino % kMinfsInodesPerBlock) * kMinfsInodeSize; |
| const blk_t inoblock_rel = ino / kMinfsInodesPerBlock; |
| const blk_t inoblock_abs = inoblock_rel + start_block_; |
| assert(inoblock_abs < kFVMBlockDataStart); |
| #ifdef __Fuchsia__ |
| void* inodata = (void*)((uintptr_t)(inode_table_.start()) + |
| (uintptr_t)(inoblock_rel * kMinfsBlockSize)); |
| memcpy((void*)((uintptr_t)inodata + off_of_ino), inode, kMinfsInodeSize); |
| txn->Enqueue(inode_table_.vmo().get(), inoblock_rel, inoblock_abs, 1); |
| #else |
| // Since host-side tools don't have "mapped vmos", just read / update / |
| // write the single absolute inode block. |
| uint8_t inodata[kMinfsBlockSize]; |
| bc_->Readblk(inoblock_abs, inodata); |
| memcpy((void*)((uintptr_t)inodata + off_of_ino), inode, kMinfsInodeSize); |
| bc_->Writeblk(inoblock_abs, inodata); |
| #endif |
| } |
| |
| void InodeManager::Load(ino_t ino, Inode* out) const { |
| // obtain the block of the inode table we need |
| uint32_t off_of_ino = (ino % kMinfsInodesPerBlock) * kMinfsInodeSize; |
| #ifdef __Fuchsia__ |
| void* inodata = (void*)((uintptr_t)(inode_table_.start()) + |
| (uintptr_t)((ino / kMinfsInodesPerBlock) * kMinfsBlockSize)); |
| #else |
| uint8_t inodata[kMinfsBlockSize]; |
| bc_->Readblk(start_block_ + (ino / kMinfsInodesPerBlock), inodata); |
| #endif |
| const Inode* inode = reinterpret_cast<const Inode*>((uintptr_t)inodata + |
| off_of_ino); |
| memcpy(out, inode, kMinfsInodeSize); |
| } |
| |
| zx_status_t InodeManager::Grow(size_t inodes) { |
| #ifdef __Fuchsia__ |
| uint32_t inoblks = (static_cast<uint32_t>(inodes) + kMinfsInodesPerBlock - 1) / |
| kMinfsInodesPerBlock; |
| if (inode_table_.Grow(inoblks * kMinfsBlockSize) != ZX_OK) { |
| return ZX_ERR_NO_SPACE; |
| } |
| return ZX_OK; |
| #else |
| return ZX_ERR_NO_SPACE; |
| #endif |
| } |
| |
| } // namespace minfs |