1 /* 2 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 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 "Inode.h" 34 #include "Utility.h" 35 36 37 //#define TRACE_EXFAT 38 #ifdef TRACE_EXFAT 39 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x) 40 #else 41 # define TRACE(x...) ; 42 #endif 43 # define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x) 44 45 46 class DeviceOpener { 47 public: 48 DeviceOpener(int fd, int mode); 49 DeviceOpener(const char* device, int mode); 50 ~DeviceOpener(); 51 52 int Open(const char* device, int mode); 53 int Open(int fd, int mode); 54 void* InitCache(off_t numBlocks, uint32 blockSize); 55 void RemoveCache(bool allowWrites); 56 57 void Keep(); 58 59 int Device() const { return fDevice; } 60 int Mode() const { return fMode; } 61 bool IsReadOnly() const 62 { return _IsReadOnly(fMode); } 63 64 status_t GetSize(off_t* _size, 65 uint32* _blockSize = NULL); 66 67 private: 68 static bool _IsReadOnly(int mode) 69 { return (mode & O_RWMASK) == O_RDONLY;} 70 static bool _IsReadWrite(int mode) 71 { return (mode & O_RWMASK) == O_RDWR;} 72 73 int fDevice; 74 int fMode; 75 void* fBlockCache; 76 }; 77 78 79 // #pragma mark - DeviceOpener 80 81 82 DeviceOpener::DeviceOpener(const char* device, int mode) 83 : 84 fBlockCache(NULL) 85 { 86 Open(device, mode); 87 } 88 89 90 DeviceOpener::DeviceOpener(int fd, int mode) 91 : 92 fBlockCache(NULL) 93 { 94 Open(fd, mode); 95 } 96 97 98 DeviceOpener::~DeviceOpener() 99 { 100 if (fDevice >= 0) { 101 RemoveCache(false); 102 close(fDevice); 103 } 104 } 105 106 107 int 108 DeviceOpener::Open(const char* device, int mode) 109 { 110 fDevice = open(device, mode | O_NOCACHE); 111 if (fDevice < 0) 112 fDevice = errno; 113 114 if (fDevice < 0 && _IsReadWrite(mode)) { 115 // try again to open read-only (don't rely on a specific error code) 116 return Open(device, O_RDONLY | O_NOCACHE); 117 } 118 119 if (fDevice >= 0) { 120 // opening succeeded 121 fMode = mode; 122 if (_IsReadWrite(mode)) { 123 // check out if the device really allows for read/write access 124 device_geometry geometry; 125 if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) { 126 if (geometry.read_only) { 127 // reopen device read-only 128 close(fDevice); 129 return Open(device, O_RDONLY | O_NOCACHE); 130 } 131 } 132 } 133 } 134 135 return fDevice; 136 } 137 138 139 int 140 DeviceOpener::Open(int fd, int mode) 141 { 142 fDevice = dup(fd); 143 if (fDevice < 0) 144 return errno; 145 146 fMode = mode; 147 148 return fDevice; 149 } 150 151 152 void* 153 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize) 154 { 155 return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize, 156 IsReadOnly()); 157 } 158 159 160 void 161 DeviceOpener::RemoveCache(bool allowWrites) 162 { 163 if (fBlockCache == NULL) 164 return; 165 166 block_cache_delete(fBlockCache, allowWrites); 167 fBlockCache = NULL; 168 } 169 170 171 void 172 DeviceOpener::Keep() 173 { 174 fDevice = -1; 175 } 176 177 178 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY 179 to compute the size, or fstat() if that failed. 180 */ 181 status_t 182 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize) 183 { 184 device_geometry geometry; 185 if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) { 186 // maybe it's just a file 187 struct stat stat; 188 if (fstat(fDevice, &stat) < 0) 189 return B_ERROR; 190 191 if (_size) 192 *_size = stat.st_size; 193 if (_blockSize) // that shouldn't cause us any problems 194 *_blockSize = 512; 195 196 return B_OK; 197 } 198 199 if (_size) { 200 *_size = 1ULL * geometry.head_count * geometry.cylinder_count 201 * geometry.sectors_per_track * geometry.bytes_per_sector; 202 } 203 if (_blockSize) 204 *_blockSize = geometry.bytes_per_sector; 205 206 return B_OK; 207 } 208 209 210 // #pragma mark - LabelVisitor 211 212 213 class LabelVisitor : public EntryVisitor { 214 public: 215 LabelVisitor(Volume* volume); 216 bool VisitLabel(struct exfat_entry*); 217 private: 218 Volume* fVolume; 219 }; 220 221 222 LabelVisitor::LabelVisitor(Volume* volume) 223 : 224 fVolume(volume) 225 { 226 } 227 228 229 bool 230 LabelVisitor::VisitLabel(struct exfat_entry* entry) 231 { 232 TRACE("LabelVisitor::VisitLabel()\n"); 233 char name[B_FILE_NAME_LENGTH]; 234 status_t result = get_volume_name(entry, name, sizeof(name)); 235 if (result != B_OK) 236 return false; 237 238 fVolume->SetName(name); 239 return true; 240 } 241 242 243 // #pragma mark - exfat_super_block::IsValid() 244 245 246 bool 247 exfat_super_block::IsValid() 248 { 249 // TODO: check some more values! 250 if (strncmp(filesystem, EXFAT_SUPER_BLOCK_MAGIC, sizeof(filesystem)) != 0) 251 return false; 252 if (signature != 0xaa55) 253 return false; 254 if (jump_boot[0] != 0xeb || jump_boot[1] != 0x76 || jump_boot[2] != 0x90) 255 return false; 256 if (version_minor != 0 || version_major != 1) 257 return false; 258 259 return true; 260 } 261 262 263 // #pragma mark - Volume 264 265 266 Volume::Volume(fs_volume* volume) 267 : 268 fFSVolume(volume), 269 fFlags(0), 270 fRootNode(NULL), 271 fNextId(1) 272 { 273 mutex_init(&fLock, "exfat volume"); 274 fInodesClusterTree = new InodesClusterTree; 275 fInodesInoTree = new InodesInoTree; 276 memset(fName, 0, sizeof(fName)); 277 } 278 279 280 Volume::~Volume() 281 { 282 TRACE("Volume destructor.\n"); 283 delete fInodesClusterTree; 284 delete fInodesInoTree; 285 } 286 287 288 bool 289 Volume::IsValidSuperBlock() 290 { 291 return fSuperBlock.IsValid(); 292 } 293 294 295 const char* 296 Volume::Name() const 297 { 298 return fName; 299 } 300 301 302 status_t 303 Volume::Mount(const char* deviceName, uint32 flags) 304 { 305 flags |= B_MOUNT_READ_ONLY; 306 // we only support read-only for now 307 308 if ((flags & B_MOUNT_READ_ONLY) != 0) { 309 TRACE("Volume::Mount(): Read only\n"); 310 } else { 311 TRACE("Volume::Mount(): Read write\n"); 312 } 313 314 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 315 ? O_RDONLY : O_RDWR); 316 fDevice = opener.Device(); 317 if (fDevice < B_OK) { 318 ERROR("Volume::Mount(): couldn't open device\n"); 319 return fDevice; 320 } 321 322 if (opener.IsReadOnly()) 323 fFlags |= VOLUME_READ_ONLY; 324 325 // read the superblock 326 status_t status = Identify(fDevice, &fSuperBlock); 327 if (status != B_OK) { 328 ERROR("Volume::Mount(): Identify() failed\n"); 329 return status; 330 } 331 332 fBlockSize = 1 << fSuperBlock.BlockShift(); 333 TRACE("block size %" B_PRIu32 "\n", fBlockSize); 334 fEntriesPerBlock = (fBlockSize / sizeof(struct exfat_entry)); 335 336 // check that the device is large enough to hold the partition 337 off_t deviceSize; 338 status = opener.GetSize(&deviceSize); 339 if (status != B_OK) 340 return status; 341 342 off_t partitionSize = (off_t)fSuperBlock.NumBlocks() 343 << fSuperBlock.BlockShift(); 344 if (deviceSize < partitionSize) 345 return B_BAD_VALUE; 346 347 fBlockCache = opener.InitCache(fSuperBlock.NumBlocks(), fBlockSize); 348 if (fBlockCache == NULL) 349 return B_ERROR; 350 351 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache); 352 353 ino_t rootIno; 354 // ready 355 { 356 Inode rootNode(this, fSuperBlock.RootDirCluster(), 0); 357 rootIno = rootNode.ID(); 358 } 359 360 status = get_vnode(fFSVolume, rootIno, (void**)&fRootNode); 361 if (status != B_OK) { 362 ERROR("could not create root node: get_vnode() failed!\n"); 363 return status; 364 } 365 366 TRACE("Volume::Mount(): Found root node: %" B_PRIdINO " (%s)\n", 367 fRootNode->ID(), strerror(fRootNode->InitCheck())); 368 369 // all went fine 370 opener.Keep(); 371 372 DirectoryIterator iterator(fRootNode); 373 LabelVisitor visitor(this); 374 iterator.Iterate(visitor); 375 376 if (fName[0] == '\0') 377 get_default_volume_name(partitionSize, fName, sizeof(fName)); 378 379 return B_OK; 380 } 381 382 383 status_t 384 Volume::Unmount() 385 { 386 TRACE("Volume::Unmount()\n"); 387 388 TRACE("Volume::Unmount(): Putting root node\n"); 389 put_vnode(fFSVolume, RootNode()->ID()); 390 TRACE("Volume::Unmount(): Deleting the block cache\n"); 391 block_cache_delete(fBlockCache, !IsReadOnly()); 392 TRACE("Volume::Unmount(): Closing device\n"); 393 close(fDevice); 394 395 TRACE("Volume::Unmount(): Done\n"); 396 return B_OK; 397 } 398 399 400 status_t 401 Volume::LoadSuperBlock() 402 { 403 CachedBlock cached(this); 404 const uint8* block = cached.SetTo(EXFAT_SUPER_BLOCK_OFFSET / fBlockSize); 405 406 if (block == NULL) 407 return B_IO_ERROR; 408 409 memcpy(&fSuperBlock, block + EXFAT_SUPER_BLOCK_OFFSET % fBlockSize, 410 sizeof(fSuperBlock)); 411 412 return B_OK; 413 } 414 415 416 status_t 417 Volume::ClusterToBlock(cluster_t cluster, fsblock_t &block) 418 { 419 if (cluster < 2) 420 return B_BAD_VALUE; 421 block = ((cluster - 2) << SuperBlock().BlocksPerClusterShift()) 422 + SuperBlock().FirstDataBlock(); 423 TRACE("Volume::ClusterToBlock() cluster %" B_PRIu32 " %u %" B_PRIu32 ": %" 424 B_PRIu64 ", %" B_PRIu32 "\n", cluster, 425 SuperBlock().BlocksPerClusterShift(), SuperBlock().FirstDataBlock(), 426 block, SuperBlock().FirstFatBlock()); 427 return B_OK; 428 } 429 430 431 cluster_t 432 Volume::NextCluster(cluster_t _cluster) 433 { 434 uint32 clusterPerBlock = fBlockSize / sizeof(cluster_t); 435 CachedBlock block(this); 436 fsblock_t blockNum = SuperBlock().FirstFatBlock() 437 + _cluster / clusterPerBlock; 438 cluster_t *cluster = (cluster_t *)block.SetTo(blockNum); 439 cluster += _cluster % clusterPerBlock; 440 TRACE("Volume::NextCluster() cluster %" B_PRIu32 " next %" B_PRIu32 "\n", 441 _cluster, *cluster); 442 return *cluster; 443 } 444 445 446 Inode* 447 Volume::FindInode(ino_t id) 448 { 449 return fInodesInoTree->Lookup(id); 450 } 451 452 453 Inode* 454 Volume::FindInode(cluster_t cluster) 455 { 456 return fInodesClusterTree->Lookup(cluster); 457 } 458 459 460 ino_t 461 Volume::GetIno(cluster_t cluster, uint32 offset, ino_t parent) 462 { 463 struct node_key key; 464 key.cluster = cluster; 465 key.offset = offset; 466 467 MutexLocker locker(fLock); 468 struct node* node = fNodeTree.Lookup(key); 469 if (node != NULL) { 470 TRACE("Volume::GetIno() cached cluster %" B_PRIu32 " offset %" B_PRIu32 471 " ino %" B_PRIdINO "\n", cluster, offset, node->ino); 472 return node->ino; 473 } 474 node = new struct node(); 475 node->key = key; 476 node->ino = _NextID(); 477 node->parent = parent; 478 fNodeTree.Insert(node); 479 fInoTree.Insert(node); 480 TRACE("Volume::GetIno() new cluster %" B_PRIu32 " offset %" B_PRIu32 481 " ino %" B_PRIdINO "\n", cluster, offset, node->ino); 482 return node->ino; 483 } 484 485 486 struct node_key* 487 Volume::GetNode(ino_t ino, ino_t &parent) 488 { 489 MutexLocker locker(fLock); 490 struct node* node = fInoTree.Lookup(ino); 491 if (node != NULL) { 492 parent = node->parent; 493 return &node->key; 494 } 495 return NULL; 496 } 497 498 499 // #pragma mark - Disk scanning and initialization 500 501 502 /*static*/ status_t 503 Volume::Identify(int fd, exfat_super_block* superBlock) 504 { 505 if (read_pos(fd, EXFAT_SUPER_BLOCK_OFFSET, superBlock, 506 sizeof(exfat_super_block)) != sizeof(exfat_super_block)) 507 return B_IO_ERROR; 508 509 if (!superBlock->IsValid()) { 510 ERROR("invalid superblock!\n"); 511 return B_BAD_VALUE; 512 } 513 514 return B_OK; 515 } 516