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 50 class LabelVisitor : public EntryVisitor { 51 public: 52 LabelVisitor(Volume* volume); 53 bool VisitLabel(struct exfat_entry*); 54 private: 55 Volume* fVolume; 56 }; 57 58 59 LabelVisitor::LabelVisitor(Volume* volume) 60 : 61 fVolume(volume) 62 { 63 } 64 65 66 bool 67 LabelVisitor::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 83 bool 84 exfat_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 103 Volume::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 117 Volume::~Volume() 118 { 119 TRACE("Volume destructor.\n"); 120 delete fInodesClusterTree; 121 delete fInodesInoTree; 122 } 123 124 125 bool 126 Volume::IsValidSuperBlock() 127 { 128 return fSuperBlock.IsValid(); 129 } 130 131 132 const char* 133 Volume::Name() const 134 { 135 return fName; 136 } 137 138 139 status_t 140 Volume::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 220 status_t 221 Volume::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 237 status_t 238 Volume::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 253 status_t 254 Volume::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 271 cluster_t 272 Volume::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 286 Inode* 287 Volume::FindInode(ino_t id) 288 { 289 return fInodesInoTree->Lookup(id); 290 } 291 292 293 Inode* 294 Volume::FindInode(cluster_t cluster) 295 { 296 return fInodesClusterTree->Lookup(cluster); 297 } 298 299 300 ino_t 301 Volume::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 326 struct node_key* 327 Volume::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 343 Volume::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