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