/* * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de. * Copyright 2011-2019, Jérôme Duval, jerome.duval@gmail.com. * Copyright 2014 Haiku, Inc. All rights reserved. * * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de * Jérôme Duval, korli@users.berlios.de * John Scipione, jscipione@gmail.com */ //! Superblock, mounting, etc. #include "Volume.h" #include #include #include #include #include #include #include #include #include #include "CachedBlock.h" #include "DeviceOpener.h" #include "Inode.h" #include "Utility.h" //#define TRACE_EXFAT #ifdef TRACE_EXFAT # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x) #else # define TRACE(x...) ; #endif # define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x) // #pragma mark - LabelVisitor class LabelVisitor : public EntryVisitor { public: LabelVisitor(Volume* volume); bool VisitLabel(struct exfat_entry*); private: Volume* fVolume; }; LabelVisitor::LabelVisitor(Volume* volume) : fVolume(volume) { } bool LabelVisitor::VisitLabel(struct exfat_entry* entry) { TRACE("LabelVisitor::VisitLabel()\n"); char name[B_FILE_NAME_LENGTH]; status_t result = get_volume_name(entry, name, sizeof(name)); if (result != B_OK) return false; fVolume->SetName(name); return true; } // #pragma mark - exfat_super_block::IsValid() bool exfat_super_block::IsValid() { // TODO: check some more values! if (strncmp(filesystem, EXFAT_SUPER_BLOCK_MAGIC, sizeof(filesystem)) != 0) return false; if (signature != 0xaa55) return false; if (jump_boot[0] != 0xeb || jump_boot[1] != 0x76 || jump_boot[2] != 0x90) return false; if (version_minor != 0 || version_major != 1) return false; return true; } // #pragma mark - Volume Volume::Volume(fs_volume* volume) : fFSVolume(volume), fFlags(0), fRootNode(NULL), fNextId(1) { mutex_init(&fLock, "exfat volume"); fInodesClusterTree = new InodesClusterTree; fInodesInoTree = new InodesInoTree; memset(fName, 0, sizeof(fName)); } Volume::~Volume() { TRACE("Volume destructor.\n"); delete fInodesClusterTree; delete fInodesInoTree; } bool Volume::IsValidSuperBlock() { return fSuperBlock.IsValid(); } const char* Volume::Name() const { return fName; } status_t Volume::Mount(const char* deviceName, uint32 flags) { flags |= B_MOUNT_READ_ONLY; // we only support read-only for now if ((flags & B_MOUNT_READ_ONLY) != 0) { TRACE("Volume::Mount(): Read only\n"); } else { TRACE("Volume::Mount(): Read write\n"); } DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 ? O_RDONLY : O_RDWR); fDevice = opener.Device(); if (fDevice < B_OK) { ERROR("Volume::Mount(): couldn't open device\n"); return fDevice; } if (opener.IsReadOnly()) fFlags |= VOLUME_READ_ONLY; // read the superblock status_t status = Identify(fDevice, &fSuperBlock); if (status != B_OK) { ERROR("Volume::Mount(): Identify() failed\n"); return status; } fBlockSize = 1 << fSuperBlock.BlockShift(); TRACE("block size %" B_PRIu32 "\n", fBlockSize); fEntriesPerBlock = (fBlockSize / sizeof(struct exfat_entry)); // check that the device is large enough to hold the partition off_t deviceSize; status = opener.GetSize(&deviceSize); if (status != B_OK) return status; off_t partitionSize = (off_t)fSuperBlock.NumBlocks() << fSuperBlock.BlockShift(); if (deviceSize < partitionSize) return B_BAD_VALUE; fBlockCache = opener.InitCache(fSuperBlock.NumBlocks(), fBlockSize); if (fBlockCache == NULL) return B_ERROR; TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache); ino_t rootIno; // ready { Inode rootNode(this, fSuperBlock.RootDirCluster(), 0); rootIno = rootNode.ID(); } status = get_vnode(fFSVolume, rootIno, (void**)&fRootNode); if (status != B_OK) { ERROR("could not create root node: get_vnode() failed!\n"); return status; } TRACE("Volume::Mount(): Found root node: %" B_PRIdINO " (%s)\n", fRootNode->ID(), strerror(fRootNode->InitCheck())); // all went fine opener.Keep(); DirectoryIterator iterator(fRootNode); LabelVisitor visitor(this); iterator.Iterate(visitor); if (fName[0] == '\0') get_default_volume_name(partitionSize, fName, sizeof(fName)); return B_OK; } status_t Volume::Unmount() { TRACE("Volume::Unmount()\n"); TRACE("Volume::Unmount(): Putting root node\n"); put_vnode(fFSVolume, RootNode()->ID()); TRACE("Volume::Unmount(): Deleting the block cache\n"); block_cache_delete(fBlockCache, !IsReadOnly()); TRACE("Volume::Unmount(): Closing device\n"); close(fDevice); TRACE("Volume::Unmount(): Done\n"); return B_OK; } status_t Volume::LoadSuperBlock() { CachedBlock cached(this); const uint8* block = cached.SetTo(EXFAT_SUPER_BLOCK_OFFSET / fBlockSize); if (block == NULL) return B_IO_ERROR; memcpy(&fSuperBlock, block + EXFAT_SUPER_BLOCK_OFFSET % fBlockSize, sizeof(fSuperBlock)); return B_OK; } status_t Volume::ClusterToBlock(cluster_t cluster, fsblock_t &block) { if ((cluster - EXFAT_FIRST_DATA_CLUSTER) >= SuperBlock().ClusterCount() || cluster < EXFAT_FIRST_DATA_CLUSTER) { return B_BAD_VALUE; } block = ((fsblock_t)(cluster - EXFAT_FIRST_DATA_CLUSTER) << SuperBlock().BlocksPerClusterShift()) + SuperBlock().FirstDataBlock(); TRACE("Volume::ClusterToBlock() cluster %" B_PRIu32 " %u %" B_PRIu32 ": %" B_PRIu64 ", %" B_PRIu32 "\n", cluster, SuperBlock().BlocksPerClusterShift(), SuperBlock().FirstDataBlock(), block, SuperBlock().FirstFatBlock()); return B_OK; } cluster_t Volume::NextCluster(cluster_t _cluster) { uint32 clusterPerBlock = fBlockSize / sizeof(cluster_t); CachedBlock block(this); fsblock_t blockNum = SuperBlock().FirstFatBlock() + _cluster / clusterPerBlock; cluster_t *cluster = (cluster_t *)block.SetTo(blockNum); cluster += _cluster % clusterPerBlock; TRACE("Volume::NextCluster() cluster %" B_PRIu32 " next %" B_PRIu32 "\n", _cluster, *cluster); return *cluster; } Inode* Volume::FindInode(ino_t id) { return fInodesInoTree->Lookup(id); } Inode* Volume::FindInode(cluster_t cluster) { return fInodesClusterTree->Lookup(cluster); } ino_t Volume::GetIno(cluster_t cluster, uint32 offset, ino_t parent) { struct node_key key; key.cluster = cluster; key.offset = offset; MutexLocker locker(fLock); struct node* node = fNodeTree.Lookup(key); if (node != NULL) { TRACE("Volume::GetIno() cached cluster %" B_PRIu32 " offset %" B_PRIu32 " ino %" B_PRIdINO "\n", cluster, offset, node->ino); return node->ino; } node = new struct node(); node->key = key; node->ino = _NextID(); node->parent = parent; fNodeTree.Insert(node); fInoTree.Insert(node); TRACE("Volume::GetIno() new cluster %" B_PRIu32 " offset %" B_PRIu32 " ino %" B_PRIdINO "\n", cluster, offset, node->ino); return node->ino; } struct node_key* Volume::GetNode(ino_t ino, ino_t &parent) { MutexLocker locker(fLock); struct node* node = fInoTree.Lookup(ino); if (node != NULL) { parent = node->parent; return &node->key; } return NULL; } // #pragma mark - Disk scanning and initialization /*static*/ status_t Volume::Identify(int fd, exfat_super_block* superBlock) { if (read_pos(fd, EXFAT_SUPER_BLOCK_OFFSET, superBlock, sizeof(exfat_super_block)) != sizeof(exfat_super_block)) return B_IO_ERROR; if (!superBlock->IsValid()) { ERROR("invalid superblock!\n"); return B_BAD_VALUE; } return B_OK; }