1/* 2 * Copyright 2008-2010, Axel D��rfler, axeld@pinc-software.de. 3 * Copyright 2011-2019, J��r��me Duval, jerome.duval@gmail.com. 4 * Copyright 2014 Haiku, Inc. All rights reserved. 5 * 6 * Distributed under the terms of the MIT License. 7 * 8 * Authors: 9 * Axel D��rfler, axeld@pinc-software.de 10 * J��r��me Duval, korli@users.berlios.de 11 * John Scipione, jscipione@gmail.com 12 */ 13 14 15//! Superblock, mounting, etc. 16 17 18#include "Volume.h" 19 20#include <errno.h> 21#include <unistd.h> 22#include <new> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26 27#include <fs_cache.h> 28#include <fs_volume.h> 29 30#include <util/AutoLock.h> 31 32#include "CachedBlock.h" 33#include "DeviceOpener.h" 34#include "Inode.h" 35#include "Utility.h" 36 37 38//#define TRACE_EXFAT 39#ifdef TRACE_EXFAT 40# define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x) 41#else 42# define TRACE(x...) ; 43#endif 44# define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x) 45 46 47// #pragma mark - LabelVisitor 48 49 50class LabelVisitor : public EntryVisitor { 51public: 52 LabelVisitor(Volume* volume); 53 bool VisitLabel(struct exfat_entry*); 54private: 55 Volume* fVolume; 56}; 57 58 59LabelVisitor::LabelVisitor(Volume* volume) 60 : 61 fVolume(volume) 62{ 63} 64 65 66bool 67LabelVisitor::VisitLabel(struct exfat_entry* entry) 68{ 69 TRACE("LabelVisitor::VisitLabel()\n"); 70 char name[B_FILE_NAME_LENGTH]; 71 status_t result = get_volume_name(entry, name, sizeof(name)); 72 if (result != B_OK) 73 return false; 74 75 fVolume->SetName(name); 76 return true; 77} 78 79 80// #pragma mark - exfat_super_block::IsValid() 81 82 83bool 84exfat_super_block::IsValid() 85{ 86 // TODO: check some more values! 87 if (strncmp(filesystem, EXFAT_SUPER_BLOCK_MAGIC, sizeof(filesystem)) != 0) 88 return false; 89 if (signature != 0xaa55) 90 return false; 91 if (jump_boot[0] != 0xeb || jump_boot[1] != 0x76 || jump_boot[2] != 0x90) 92 return false; 93 if (version_minor != 0 || version_major != 1) 94 return false; 95 96 return true; 97} 98 99 100// #pragma mark - Volume 101 102 103Volume::Volume(fs_volume* volume) 104 : 105 fFSVolume(volume), 106 fFlags(0), 107 fRootNode(NULL), 108 fNextId(1) 109{ 110 mutex_init(&fLock, "exfat volume"); 111 fInodesClusterTree = new InodesClusterTree; 112 fInodesInoTree = new InodesInoTree; 113 memset(fName, 0, sizeof(fName)); 114} 115 116 117Volume::~Volume() 118{ 119 TRACE("Volume destructor.\n"); 120 delete fInodesClusterTree; 121 delete fInodesInoTree; 122} 123 124 125bool 126Volume::IsValidSuperBlock() 127{ 128 return fSuperBlock.IsValid(); 129} 130 131 132const char* 133Volume::Name() const 134{ 135 return fName; 136} 137 138 139status_t 140Volume::Mount(const char* deviceName, uint32 flags) 141{ 142 flags |= B_MOUNT_READ_ONLY; 143 // we only support read-only for now 144 145 if ((flags & B_MOUNT_READ_ONLY) != 0) { 146 TRACE("Volume::Mount(): Read only\n"); 147 } else { 148 TRACE("Volume::Mount(): Read write\n"); 149 } 150 151 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 152 ? O_RDONLY : O_RDWR); 153 fDevice = opener.Device(); 154 if (fDevice < B_OK) { 155 ERROR("Volume::Mount(): couldn't open device\n"); 156 return fDevice; 157 } 158 159 if (opener.IsReadOnly()) 160 fFlags |= VOLUME_READ_ONLY; 161 162 // read the superblock 163 status_t status = Identify(fDevice, &fSuperBlock); 164 if (status != B_OK) { 165 ERROR("Volume::Mount(): Identify() failed\n"); 166 return status; 167 } 168 169 fBlockSize = 1 << fSuperBlock.BlockShift(); 170 TRACE("block size %" B_PRIu32 "\n", fBlockSize); 171 fEntriesPerBlock = (fBlockSize / sizeof(struct exfat_entry)); 172 173 // check that the device is large enough to hold the partition 174 off_t deviceSize; 175 status = opener.GetSize(&deviceSize); 176 if (status != B_OK) 177 return status; 178 179 off_t partitionSize = (off_t)fSuperBlock.NumBlocks() 180 << fSuperBlock.BlockShift(); 181 if (deviceSize < partitionSize) 182 return B_BAD_VALUE; 183 184 fBlockCache = opener.InitCache(fSuperBlock.NumBlocks(), fBlockSize); 185 if (fBlockCache == NULL) 186 return B_ERROR; 187 188 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache); 189 190 ino_t rootIno; 191 // ready 192 { 193 Inode rootNode(this, fSuperBlock.RootDirCluster(), 0); 194 rootIno = rootNode.ID(); 195 } 196 197 status = get_vnode(fFSVolume, rootIno, (void**)&fRootNode); 198 if (status != B_OK) { 199 ERROR("could not create root node: get_vnode() failed!\n"); 200 return status; 201 } 202 203 TRACE("Volume::Mount(): Found root node: %" B_PRIdINO " (%s)\n", 204 fRootNode->ID(), strerror(fRootNode->InitCheck())); 205 206 // all went fine 207 opener.Keep(); 208 209 DirectoryIterator iterator(fRootNode); 210 LabelVisitor visitor(this); 211 iterator.Iterate(visitor); 212 213 if (fName[0] == '\0') 214 get_default_volume_name(partitionSize, fName, sizeof(fName)); 215 216 return B_OK; 217} 218 219 220status_t 221Volume::Unmount() 222{ 223 TRACE("Volume::Unmount()\n"); 224 225 TRACE("Volume::Unmount(): Putting root node\n"); 226 put_vnode(fFSVolume, RootNode()->ID()); 227 TRACE("Volume::Unmount(): Deleting the block cache\n"); 228 block_cache_delete(fBlockCache, !IsReadOnly()); 229 TRACE("Volume::Unmount(): Closing device\n"); 230 close(fDevice); 231 232 TRACE("Volume::Unmount(): Done\n"); 233 return B_OK; 234} 235 236 237status_t 238Volume::LoadSuperBlock() 239{ 240 CachedBlock cached(this); 241 const uint8* block = cached.SetTo(EXFAT_SUPER_BLOCK_OFFSET / fBlockSize); 242 243 if (block == NULL) 244 return B_IO_ERROR; 245 246 memcpy(&fSuperBlock, block + EXFAT_SUPER_BLOCK_OFFSET % fBlockSize, 247 sizeof(fSuperBlock)); 248 249 return B_OK; 250} 251 252 253status_t 254Volume::ClusterToBlock(cluster_t cluster, fsblock_t &block) 255{ 256 if ((cluster - EXFAT_FIRST_DATA_CLUSTER) >= SuperBlock().ClusterCount() 257 || cluster < EXFAT_FIRST_DATA_CLUSTER) { 258 return B_BAD_VALUE; 259 } 260 block = ((fsblock_t)(cluster - EXFAT_FIRST_DATA_CLUSTER) 261 << SuperBlock().BlocksPerClusterShift()) 262 + SuperBlock().FirstDataBlock(); 263 TRACE("Volume::ClusterToBlock() cluster %" B_PRIu32 " %u %" B_PRIu32 ": %" 264 B_PRIu64 ", %" B_PRIu32 "\n", cluster, 265 SuperBlock().BlocksPerClusterShift(), SuperBlock().FirstDataBlock(), 266 block, SuperBlock().FirstFatBlock()); 267 return B_OK; 268} 269 270 271cluster_t 272Volume::NextCluster(cluster_t _cluster) 273{ 274 uint32 clusterPerBlock = fBlockSize / sizeof(cluster_t); 275 CachedBlock block(this); 276 fsblock_t blockNum = SuperBlock().FirstFatBlock() 277 + _cluster / clusterPerBlock; 278 cluster_t *cluster = (cluster_t *)block.SetTo(blockNum); 279 cluster += _cluster % clusterPerBlock; 280 TRACE("Volume::NextCluster() cluster %" B_PRIu32 " next %" B_PRIu32 "\n", 281 _cluster, *cluster); 282 return *cluster; 283} 284 285 286Inode* 287Volume::FindInode(ino_t id) 288{ 289 return fInodesInoTree->Lookup(id); 290} 291 292 293Inode* 294Volume::FindInode(cluster_t cluster) 295{ 296 return fInodesClusterTree->Lookup(cluster); 297} 298 299 300ino_t 301Volume::GetIno(cluster_t cluster, uint32 offset, ino_t parent) 302{ 303 struct node_key key; 304 key.cluster = cluster; 305 key.offset = offset; 306 307 MutexLocker locker(fLock); 308 struct node* node = fNodeTree.Lookup(key); 309 if (node != NULL) { 310 TRACE("Volume::GetIno() cached cluster %" B_PRIu32 " offset %" B_PRIu32 311 " ino %" B_PRIdINO "\n", cluster, offset, node->ino); 312 return node->ino; 313 } 314 node = new struct node(); 315 node->key = key; 316 node->ino = _NextID(); 317 node->parent = parent; 318 fNodeTree.Insert(node); 319 fInoTree.Insert(node); 320 TRACE("Volume::GetIno() new cluster %" B_PRIu32 " offset %" B_PRIu32 321 " ino %" B_PRIdINO "\n", cluster, offset, node->ino); 322 return node->ino; 323} 324 325 326struct node_key* 327Volume::GetNode(ino_t ino, ino_t &parent) 328{ 329 MutexLocker locker(fLock); 330 struct node* node = fInoTree.Lookup(ino); 331 if (node != NULL) { 332 parent = node->parent; 333 return &node->key; 334 } 335 return NULL; 336} 337 338 339// #pragma mark - Disk scanning and initialization 340 341 342/*static*/ status_t 343Volume::Identify(int fd, exfat_super_block* superBlock) 344{ 345 if (read_pos(fd, EXFAT_SUPER_BLOCK_OFFSET, superBlock, 346 sizeof(exfat_super_block)) != sizeof(exfat_super_block)) 347 return B_IO_ERROR; 348 349 if (!superBlock->IsValid()) { 350 ERROR("invalid superblock!\n"); 351 return B_BAD_VALUE; 352 } 353 354 return B_OK; 355} 356