1/* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "Volume.h" 8 9#include <errno.h> 10#include <fcntl.h> 11#include <stdio.h> 12#include <stdlib.h> 13#include <string.h> 14#include <sys/stat.h> 15 16#include <new> 17 18#include <fs_cache.h> 19 20#include <AutoDeleter.h> 21#include <util/AutoLock.h> 22 23#include "Block.h" 24#include "BlockAllocator.h" 25#include "checksumfs.h" 26#include "checksumfs_private.h" 27#include "DebugSupport.h" 28#include "Directory.h" 29#include "File.h" 30#include "SuperBlock.h" 31#include "SymLink.h" 32#include "Transaction.h" 33 34 35Volume::Volume(uint32 flags) 36 : 37 fFSVolume(NULL), 38 fFD(-1), 39 fFlags(B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME 40 | ((flags & B_FS_IS_READONLY) != 0 ? B_FS_IS_READONLY : 0)), 41 fBlockCache(NULL), 42 fTotalBlocks(0), 43 fName(NULL), 44 fBlockAllocator(NULL), 45 fRootDirectory(NULL) 46{ 47 mutex_init(&fLock, "checksumfs volume"); 48 mutex_init(&fTransactionLock, "checksumfs transaction"); 49} 50 51 52Volume::~Volume() 53{ 54 delete fRootDirectory; 55 delete fBlockAllocator; 56 57 if (fBlockCache != NULL) 58 block_cache_delete(fBlockCache, false); 59 60 if (fFD >= 0) 61 close(fFD); 62 63 free(fName); 64 65 mutex_destroy(&fTransactionLock); 66 mutex_destroy(&fLock); 67} 68 69 70status_t 71Volume::Init(const char* device) 72{ 73 // open the device 74 if (!IsReadOnly()) { 75 fFD = open(device, O_RDWR); 76 77 // If opening read-write fails, we retry read-only. 78 if (fFD < 0) 79 fFlags |= B_FS_IS_READONLY; 80 } 81 82 if (IsReadOnly()) 83 fFD = open(device, O_RDONLY); 84 85 if (fFD < 0) 86 return errno; 87 88 // get the size 89 struct stat st; 90 if (fstat(fFD, &st) < 0) 91 return errno; 92 93 off_t size; 94 switch (st.st_mode & S_IFMT) { 95 case S_IFREG: 96 size = st.st_size; 97 break; 98 case S_IFCHR: 99 case S_IFBLK: 100 { 101 device_geometry geometry; 102 if (ioctl(fFD, B_GET_GEOMETRY, &geometry, sizeof(geometry)) < 0) 103 return errno; 104 105 size = (off_t)geometry.bytes_per_sector * geometry.sectors_per_track 106 * geometry.cylinder_count * geometry.head_count; 107 break; 108 } 109 default: 110 return B_BAD_VALUE; 111 } 112 113 return _Init(size / B_PAGE_SIZE); 114} 115 116 117status_t 118Volume::Init(int fd, uint64 totalBlocks) 119{ 120 fFD = dup(fd); 121 if (fFD < 0) 122 RETURN_ERROR(errno); 123 124 return _Init(totalBlocks); 125} 126 127 128status_t 129Volume::Mount(fs_volume* fsVolume) 130{ 131 fFSVolume = fsVolume; 132 133 // load the superblock 134 Block block; 135 if (!block.GetReadable(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE)) 136 RETURN_ERROR(B_ERROR); 137 138 SuperBlock* superBlock = (SuperBlock*)block.Data(); 139 if (!superBlock->Check(fTotalBlocks)) 140 RETURN_ERROR(B_BAD_DATA); 141 142 // copy the volume name 143 fName = strdup(superBlock->Name()); 144 if (fName == NULL) 145 RETURN_ERROR(B_NO_MEMORY); 146 147 // init the block allocator 148 status_t error = fBlockAllocator->Init(superBlock->BlockBitmap(), 149 superBlock->FreeBlocks()); 150 if (error != B_OK) 151 RETURN_ERROR(error); 152 153 // load the root directory 154 Node* rootNode; 155 error = ReadNode(superBlock->RootDirectory(), rootNode); 156 if (error != B_OK) 157 RETURN_ERROR(error); 158 159 fRootDirectory = dynamic_cast<Directory*>(rootNode); 160 if (fRootDirectory == NULL) { 161 delete rootNode; 162 RETURN_ERROR(B_BAD_DATA); 163 } 164 165 error = PublishNode(fRootDirectory, 0); 166 if (error != B_OK) { 167 delete fRootDirectory; 168 fRootDirectory = NULL; 169 return error; 170 } 171 172 return B_OK; 173} 174 175 176void 177Volume::Unmount() 178{ 179 status_t error = block_cache_sync(fBlockCache); 180 if (error != B_OK) { 181 dprintf("checksumfs: Error: Failed to sync block cache when " 182 "unmounting!\n"); 183 } 184} 185 186 187status_t 188Volume::Initialize(const char* name) 189{ 190 fName = strdup(name); 191 if (fName == NULL) 192 RETURN_ERROR(B_NO_MEMORY); 193 194 Transaction transaction(this); 195 status_t error = transaction.Start(); 196 if (error != B_OK) 197 RETURN_ERROR(error); 198 199 error = fBlockAllocator->Initialize(transaction); 200 if (error != B_OK) 201 RETURN_ERROR(error); 202 203 // create the root directory 204 error = CreateDirectory(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, 205 transaction, fRootDirectory); 206 if (error != B_OK) 207 RETURN_ERROR(error); 208 209 transaction.KeepNode(fRootDirectory); 210 fRootDirectory->SetHardLinks(1); 211 212 // write the superblock 213 Block block; 214 if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE, 215 transaction)) { 216 RETURN_ERROR(B_ERROR); 217 } 218 219 SuperBlock* superBlock = (SuperBlock*)block.Data(); 220 superBlock->Initialize(this); 221 222 block.Put(); 223 224 // commit the transaction and flush the block cache 225 error = transaction.Commit(); 226 if (error != B_OK) 227 RETURN_ERROR(error); 228 229 return block_cache_sync(fBlockCache); 230} 231 232 233void 234Volume::GetInfo(fs_info& info) 235{ 236 MutexLocker locker(fLock); 237 238 info.flags = fFlags; 239 info.block_size = B_PAGE_SIZE; 240 info.io_size = B_PAGE_SIZE * 16; // random value 241 info.total_blocks = fTotalBlocks; 242 info.free_blocks = fBlockAllocator->FreeBlocks(); 243 info.total_nodes = 1; // phew, who cares? 244 info.free_nodes = info.free_blocks; 245 strlcpy(info.volume_name, fName, sizeof(info.volume_name)); 246} 247 248 249status_t 250Volume::NewNode(Node* node) 251{ 252 return new_vnode(fFSVolume, node->BlockIndex(), node, &gCheckSumFSVnodeOps); 253} 254 255 256status_t 257Volume::PublishNode(Node* node, uint32 flags) 258{ 259 return publish_vnode(fFSVolume, node->BlockIndex(), node, 260 &gCheckSumFSVnodeOps, node->Mode(), flags); 261} 262 263 264status_t 265Volume::GetNode(uint64 blockIndex, Node*& _node) 266{ 267 return get_vnode(fFSVolume, blockIndex, (void**)&_node); 268} 269 270 271status_t 272Volume::PutNode(Node* node) 273{ 274 return put_vnode(fFSVolume, node->BlockIndex()); 275} 276 277 278status_t 279Volume::RemoveNode(Node* node) 280{ 281 return remove_vnode(fFSVolume, node->BlockIndex()); 282} 283 284 285status_t 286Volume::UnremoveNode(Node* node) 287{ 288 return unremove_vnode(fFSVolume, node->BlockIndex()); 289} 290 291 292status_t 293Volume::ReadNode(uint64 blockIndex, Node*& _node) 294{ 295 if (blockIndex == 0 || blockIndex >= fTotalBlocks) 296 return B_BAD_VALUE; 297 298 // load the node's block 299 Block block; 300 if (!block.GetReadable(this, blockIndex)) 301 return B_ERROR; 302 303 checksumfs_node* nodeData = (checksumfs_node*)block.Data(); 304 305 // create the Node object 306 Node* node; 307 switch (nodeData->mode & S_IFMT) { 308 // TODO: Don't directly access mode! 309 case S_IFDIR: 310 node = new(std::nothrow) Directory(this, blockIndex, *nodeData); 311 break; 312 case S_IFREG: 313 node = new(std::nothrow) File(this, blockIndex, *nodeData); 314 break; 315 case S_IFLNK: 316 node = new(std::nothrow) SymLink(this, blockIndex, *nodeData); 317 break; 318 default: 319 node = new(std::nothrow) Node(this, blockIndex, *nodeData); 320 break; 321 } 322 323 if (node == NULL) 324 return B_NO_MEMORY; 325 326 // TODO: Sanity check the node! 327 328 _node = node; 329 return B_OK; 330} 331 332 333status_t 334Volume::CreateDirectory(mode_t mode, Transaction& transaction, 335 Directory*& _directory) 336{ 337 Directory* directory = new(std::nothrow) Directory(this, 338 (mode & S_IUMSK) | S_IFDIR); 339 340 status_t error = _CreateNode(directory, transaction); 341 if (error != B_OK) 342 return error; 343 344 _directory = directory; 345 return B_OK; 346} 347 348 349status_t 350Volume::CreateFile(mode_t mode, Transaction& transaction, File*& _file) 351{ 352 File* file = new(std::nothrow) File(this, (mode & S_IUMSK) | S_IFREG); 353 354 status_t error = _CreateNode(file, transaction); 355 if (error != B_OK) 356 return error; 357 358 _file = file; 359 return B_OK; 360} 361 362 363status_t 364Volume::CreateSymLink(mode_t mode, Transaction& transaction, SymLink*& _symLink) 365{ 366 SymLink* symLink = new(std::nothrow) SymLink(this, 367 (mode & S_IUMSK) | S_IFLNK); 368 369 status_t error = _CreateNode(symLink, transaction); 370 if (error != B_OK) 371 return error; 372 373 _symLink = symLink; 374 return B_OK; 375} 376 377 378status_t 379Volume::DeleteNode(Node* node) 380{ 381 // let the node delete data associated with it 382 node->DeletingNode(); 383 384 uint64 blockIndex = node->BlockIndex(); 385 386 // delete the node itself 387 Transaction transaction(this); 388 status_t error = transaction.Start(); 389 if (error == B_OK) { 390 error = fBlockAllocator->Free(node->BlockIndex(), 1, transaction); 391 if (error == B_OK) { 392 error = transaction.Commit(); 393 if (error != B_OK) { 394 ERROR("Failed to commit transaction for deleting node at %" 395 B_PRIu64 "\n", blockIndex); 396 } 397 } else { 398 ERROR("Failed to free block for node at %" B_PRIu64 "\n", 399 blockIndex); 400 } 401 } else { 402 ERROR("Failed to start transaction for deleting node at %" B_PRIu64 403 "\n", blockIndex); 404 } 405 406 transaction.Abort(); 407 408 delete node; 409 410 return error; 411} 412 413 414status_t 415Volume::SetName(const char* name) 416{ 417 if (name == NULL || strlen(name) > kCheckSumFSNameLength) 418 return B_BAD_VALUE; 419 420 // clone the name 421 char* newName = strdup(name); 422 if (newName == NULL) 423 return B_NO_MEMORY; 424 MemoryDeleter newNameDeleter(newName); 425 426 // start a transaction 427 Transaction transaction(this); 428 status_t error = transaction.Start(); 429 if (error != B_OK) 430 return error; 431 432 // we lock the volume now, to keep the locking order (transaction -> volume) 433 MutexLocker locker(fLock); 434 435 // update the superblock 436 Block block; 437 if (!block.GetWritable(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE, 438 transaction)) { 439 return B_ERROR; 440 } 441 442 SuperBlock* superBlock = (SuperBlock*)block.Data(); 443 superBlock->SetName(newName); 444 445 block.Put(); 446 447 // commit the transaction 448 error = transaction.Commit(); 449 if (error != B_OK) 450 return error; 451 452 // Everything went fine. We can replace the name. Since we still have the 453 // volume lock, there's no race condition. 454 free(fName); 455 fName = (char*)newNameDeleter.Detach(); 456 457 return B_OK; 458} 459 460 461status_t 462Volume::_Init(uint64 totalBlocks) 463{ 464 fTotalBlocks = totalBlocks; 465 if (fTotalBlocks * B_PAGE_SIZE < kCheckSumFSMinSize) 466 RETURN_ERROR(B_BAD_VALUE); 467 468 // create a block cache 469 fBlockCache = block_cache_create(fFD, fTotalBlocks, B_PAGE_SIZE, 470 IsReadOnly()); 471 if (fBlockCache == NULL) 472 RETURN_ERROR(B_NO_MEMORY); 473 474 // create the block allocator 475 fBlockAllocator = new(std::nothrow) BlockAllocator(this); 476 if (fBlockAllocator == NULL) 477 RETURN_ERROR(B_NO_MEMORY); 478 479 return B_OK; 480} 481 482 483status_t 484Volume::_CreateNode(Node* node, Transaction& transaction) 485{ 486 if (node == NULL) 487 return B_NO_MEMORY; 488 489 ObjectDeleter<Node> nodeDeleter(node); 490 491 // allocate a free block 492 AllocatedBlock allocatedBlock(fBlockAllocator, transaction); 493 status_t error = allocatedBlock.Allocate(); 494 if (error != B_OK) 495 return error; 496 497 // clear the block 498 { 499 Block block; 500 if (!block.GetZero(this, allocatedBlock.Index(), transaction)) 501 return B_ERROR; 502 } 503 504 node->SetBlockIndex(allocatedBlock.Index()); 505 506 // attach the node to the transaction 507 error = transaction.AddNode(node, TRANSACTION_DELETE_NODE); 508 if (error != B_OK) 509 return error; 510 511 allocatedBlock.Detach(); 512 nodeDeleter.Detach(); 513 514 return B_OK; 515} 516