1 /* 2 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 #include <dirent.h> 8 #include <util/kernel_cpp.h> 9 #include <string.h> 10 11 #include <fs_cache.h> 12 #include <fs_info.h> 13 14 #include "DirectoryIterator.h" 15 #include "ext2.h" 16 #include "Inode.h" 17 18 19 #define EXT2_IO_SIZE 65536 20 21 22 struct identify_cookie { 23 ext2_super_block super_block; 24 }; 25 26 27 /*! Converts the open mode, the open flags given to bfs_open(), into 28 access modes, e.g. since O_RDONLY requires read access to the 29 file, it will be converted to R_OK. 30 */ 31 int 32 open_mode_to_access(int openMode) 33 { 34 openMode &= O_RWMASK; 35 if (openMode == O_RDONLY) 36 return R_OK; 37 else if (openMode == O_WRONLY) 38 return W_OK; 39 40 return R_OK | W_OK; 41 } 42 43 44 // #pragma mark - Scanning 45 46 47 static float 48 ext2_identify_partition(int fd, partition_data *partition, void **_cookie) 49 { 50 ext2_super_block superBlock; 51 status_t status = Volume::Identify(fd, &superBlock); 52 if (status != B_OK) 53 return status; 54 55 identify_cookie *cookie = new identify_cookie; 56 memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block)); 57 58 *_cookie = cookie; 59 return 0.8f; 60 } 61 62 63 static status_t 64 ext2_scan_partition(int fd, partition_data *partition, void *_cookie) 65 { 66 identify_cookie *cookie = (identify_cookie *)_cookie; 67 68 partition->status = B_PARTITION_VALID; 69 partition->flags |= B_PARTITION_FILE_SYSTEM; 70 partition->content_size = cookie->super_block.NumBlocks() 71 << cookie->super_block.BlockShift(); 72 partition->block_size = 1UL << cookie->super_block.BlockShift(); 73 partition->content_name = strdup(cookie->super_block.name); 74 if (partition->content_name == NULL) 75 return B_NO_MEMORY; 76 77 return B_OK; 78 } 79 80 81 static void 82 ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie) 83 { 84 delete (identify_cookie*)_cookie; 85 } 86 87 88 // #pragma mark - 89 90 91 static status_t 92 ext2_mount(fs_volume* _volume, const char* device, uint32 flags, 93 const char* args, ino_t* _rootID) 94 { 95 Volume* volume = new Volume(_volume); 96 if (volume == NULL) 97 return B_NO_MEMORY; 98 99 // TODO: this is a bit hacky: we can't use publish_vnode() to publish 100 // the root node, or else its file cache cannot be created (we could 101 // create it later, though). Therefore we're using get_vnode() in Mount(), 102 // but that requires us to export our volume data before calling it. 103 _volume->private_volume = volume; 104 _volume->ops = &gExt2VolumeOps; 105 106 status_t status = volume->Mount(device, flags); 107 if (status != B_OK) { 108 delete volume; 109 return status; 110 } 111 112 *_rootID = volume->RootNode()->ID(); 113 return B_OK; 114 } 115 116 117 static status_t 118 ext2_unmount(fs_volume *_volume) 119 { 120 Volume* volume = (Volume *)_volume->private_volume; 121 122 status_t status = volume->Unmount(); 123 delete volume; 124 125 return status; 126 } 127 128 129 static status_t 130 ext2_read_fs_info(fs_volume* _volume, struct fs_info* info) 131 { 132 Volume* volume = (Volume*)_volume->private_volume; 133 134 // File system flags 135 info->flags = B_FS_IS_PERSISTENT 136 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 137 info->io_size = EXT2_IO_SIZE; 138 info->block_size = volume->BlockSize(); 139 info->total_blocks = volume->NumBlocks(); 140 info->free_blocks = volume->FreeBlocks(); 141 142 // Volume name 143 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 144 145 // File system name 146 strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name)); 147 148 return B_OK; 149 } 150 151 152 // #pragma mark - 153 154 155 static status_t 156 ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, 157 uint32* _flags, bool reenter) 158 { 159 Volume* volume = (Volume*)_volume->private_volume; 160 161 if (id < 2 || id > volume->NumBlocks()) { 162 dprintf("ext2: inode at %Ld requested!\n", id); 163 return B_ERROR; 164 } 165 166 Inode* inode = new Inode(volume, id); 167 if (inode == NULL) 168 return B_NO_MEMORY; 169 170 status_t status = inode->InitCheck(); 171 if (status < B_OK) 172 delete inode; 173 174 if (status == B_OK) { 175 _node->private_node = inode; 176 _node->ops = &gExt2VnodeOps; 177 *_type = inode->Mode(); 178 *_flags = 0; 179 } 180 181 return status; 182 } 183 184 185 static status_t 186 ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 187 { 188 delete (Inode*)_node->private_node; 189 return B_OK; 190 } 191 192 193 static bool 194 ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie) 195 { 196 return true; 197 } 198 199 200 static status_t 201 ext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 202 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 203 { 204 Volume* volume = (Volume*)_volume->private_volume; 205 Inode* inode = (Inode*)_node->private_node; 206 207 if (inode->FileCache() == NULL) 208 return B_BAD_VALUE; 209 210 rw_lock_read_lock(inode->Lock()); 211 212 uint32 vecIndex = 0; 213 size_t vecOffset = 0; 214 size_t bytesLeft = *_numBytes; 215 status_t status; 216 217 while (true) { 218 file_io_vec fileVecs[8]; 219 uint32 fileVecCount = 8; 220 221 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 222 &fileVecCount, 0); 223 if (status != B_OK && status != B_BUFFER_OVERFLOW) 224 break; 225 226 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 227 228 size_t bytes = bytesLeft; 229 status = read_file_io_vec_pages(volume->Device(), fileVecs, 230 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 231 if (status != B_OK || !bufferOverflow) 232 break; 233 234 pos += bytes; 235 bytesLeft -= bytes; 236 } 237 238 rw_lock_read_unlock(inode->Lock()); 239 240 return status; 241 } 242 243 244 static status_t 245 ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, 246 size_t size, struct file_io_vec* vecs, size_t* _count) 247 { 248 Volume* volume = (Volume*)_volume->private_volume; 249 Inode* inode = (Inode*)_node->private_node; 250 size_t index = 0, max = *_count; 251 252 while (true) { 253 uint32 block; 254 status_t status = inode->FindBlock(offset, block); 255 if (status != B_OK) 256 return status; 257 258 off_t blockOffset = (off_t)block << volume->BlockShift(); 259 uint32 blockLength = volume->BlockSize(); 260 261 if (index > 0 && (vecs[index - 1].offset == blockOffset - blockLength 262 || (vecs[index - 1].offset == -1 && block == 0))) { 263 vecs[index - 1].length += blockLength; 264 } else { 265 if (index >= max) { 266 // we're out of file_io_vecs; let's bail out 267 *_count = index; 268 return B_BUFFER_OVERFLOW; 269 } 270 271 // 'block' is 0 for sparse blocks 272 if (block != 0) 273 vecs[index].offset = blockOffset; 274 else 275 vecs[index].offset = -1; 276 277 vecs[index].length = blockLength; 278 index++; 279 } 280 281 offset += blockLength; 282 283 if (size <= vecs[index - 1].length || offset >= inode->Size()) { 284 // We're done! 285 *_count = index; 286 return B_OK; 287 } 288 } 289 290 // can never get here 291 return B_ERROR; 292 } 293 294 295 // #pragma mark - 296 297 298 static status_t 299 ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 300 ino_t* _vnodeID) 301 { 302 Volume* volume = (Volume*)_volume->private_volume; 303 Inode* directory = (Inode*)_directory->private_node; 304 305 // check access permissions 306 status_t status = directory->CheckPermissions(X_OK); 307 if (status < B_OK) 308 return status; 309 310 DirectoryIterator iterator(directory); 311 while (true) { 312 char buffer[B_FILE_NAME_LENGTH]; 313 size_t length = sizeof(buffer); 314 status = iterator.GetNext(buffer, &length, _vnodeID); 315 if (status != B_OK) 316 return status; 317 318 if (!strcmp(buffer, name)) 319 break; 320 } 321 322 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 323 } 324 325 326 static status_t 327 ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 328 { 329 Inode* inode = (Inode*)_node->private_node; 330 const ext2_inode& node = inode->Node(); 331 332 stat->st_dev = inode->GetVolume()->ID(); 333 stat->st_ino = inode->ID(); 334 stat->st_nlink = node.NumLinks(); 335 stat->st_blksize = EXT2_IO_SIZE; 336 337 stat->st_uid = node.UserID(); 338 stat->st_gid = node.GroupID(); 339 stat->st_mode = node.Mode(); 340 stat->st_type = 0; 341 342 stat->st_atime = node.AccessTime(); 343 stat->st_mtime = stat->st_ctime = node.ModificationTime(); 344 stat->st_crtime = node.CreationTime(); 345 346 stat->st_size = inode->Size(); 347 stat->st_blocks = (inode->Size() + 511) / 512; 348 349 return B_OK; 350 } 351 352 353 static status_t 354 ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 355 { 356 Inode* inode = (Inode*)_node->private_node; 357 358 // opening a directory read-only is allowed, although you can't read 359 // any data from it. 360 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 361 return B_IS_A_DIRECTORY; 362 363 if ((openMode & O_TRUNC) != 0) 364 return B_READ_ONLY_DEVICE; 365 366 return inode->CheckPermissions(open_mode_to_access(openMode) 367 | (openMode & O_TRUNC ? W_OK : 0)); 368 } 369 370 371 static status_t 372 ext2_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, 373 void *buffer, size_t *_length) 374 { 375 Inode *inode = (Inode *)_node->private_node; 376 377 if (!inode->IsFile()) { 378 *_length = 0; 379 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 380 } 381 382 return inode->ReadAt(pos, (uint8 *)buffer, _length); 383 } 384 385 386 static status_t 387 ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 388 { 389 return B_OK; 390 } 391 392 393 static status_t 394 ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie) 395 { 396 return B_OK; 397 } 398 399 400 static status_t 401 ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 402 { 403 Inode* inode = (Inode*)_node->private_node; 404 return inode->CheckPermissions(accessMode); 405 } 406 407 408 static status_t 409 ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 410 size_t *_bufferSize) 411 { 412 Inode* inode = (Inode*)_node->private_node; 413 414 if (!inode->IsSymLink()) 415 return B_BAD_VALUE; 416 417 if (inode->Size() < *_bufferSize) 418 *_bufferSize = inode->Size(); 419 420 if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) 421 return inode->ReadAt(0, (uint8 *)buffer, _bufferSize); 422 423 memcpy(buffer, inode->Node().symlink, *_bufferSize); 424 return B_OK; 425 } 426 427 428 // #pragma mark - Directory functions 429 430 431 static status_t 432 ext2_open_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 433 { 434 Inode *inode = (Inode *)_node->private_node; 435 status_t status = inode->CheckPermissions(R_OK); 436 if (status < B_OK) 437 return status; 438 439 if (!inode->IsDirectory()) 440 return B_BAD_VALUE; 441 442 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 443 if (iterator == NULL) 444 return B_NO_MEMORY; 445 446 *_cookie = iterator; 447 return B_OK; 448 } 449 450 451 static status_t 452 ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 453 struct dirent *dirent, size_t bufferSize, uint32 *_num) 454 { 455 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 456 457 size_t length = bufferSize; 458 ino_t id; 459 status_t status = iterator->GetNext(dirent->d_name, &length, &id); 460 if (status == B_ENTRY_NOT_FOUND) { 461 *_num = 0; 462 return B_OK; 463 } else if (status != B_OK) 464 return status; 465 466 Volume* volume = (Volume*)_volume->private_volume; 467 468 dirent->d_dev = volume->ID(); 469 dirent->d_ino = id; 470 dirent->d_reclen = sizeof(struct dirent) + length; 471 472 *_num = 1; 473 return B_OK; 474 } 475 476 477 static status_t 478 ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 479 { 480 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 481 return iterator->Rewind(); 482 } 483 484 485 static status_t 486 ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 487 { 488 return B_OK; 489 } 490 491 492 static status_t 493 ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 494 { 495 delete (DirectoryIterator *)_cookie; 496 return B_OK; 497 } 498 499 500 fs_volume_ops gExt2VolumeOps = { 501 &ext2_unmount, 502 &ext2_read_fs_info, 503 NULL, // write_fs_info() 504 NULL, // sync() 505 &ext2_get_vnode, 506 }; 507 508 fs_vnode_ops gExt2VnodeOps = { 509 /* vnode operations */ 510 &ext2_lookup, 511 NULL, 512 &ext2_put_vnode, 513 NULL, 514 515 /* VM file access */ 516 &ext2_can_page, 517 &ext2_read_pages, 518 NULL, 519 520 NULL, // io() 521 NULL, // cancel_io() 522 523 &ext2_get_file_map, 524 525 NULL, 526 NULL, 527 NULL, // fs_select 528 NULL, // fs_deselect 529 NULL, 530 531 &ext2_read_link, 532 NULL, 533 534 NULL, 535 NULL, 536 NULL, 537 538 &ext2_access, 539 &ext2_read_stat, 540 NULL, 541 542 /* file operations */ 543 NULL, 544 &ext2_open, 545 &ext2_close, 546 &ext2_free_cookie, 547 &ext2_read, 548 NULL, 549 550 /* directory operations */ 551 NULL, 552 NULL, 553 &ext2_open_dir, 554 &ext2_close_dir, 555 &ext2_free_dir_cookie, 556 &ext2_read_dir, 557 &ext2_rewind_dir, 558 559 NULL, 560 }; 561 562 static file_system_module_info sExt2FileSystem = { 563 { 564 "file_systems/ext2" B_CURRENT_FS_API_VERSION, 565 0, 566 NULL, 567 }, 568 569 "ext2", // short_name 570 "Ext2 File System", // pretty_name 571 0, // DDM flags 572 573 // scanning 574 ext2_identify_partition, 575 ext2_scan_partition, 576 ext2_free_identify_partition_cookie, 577 NULL, // free_partition_content_cookie() 578 579 &ext2_mount, 580 581 NULL, 582 }; 583 584 module_info *modules[] = { 585 (module_info *)&sExt2FileSystem, 586 NULL, 587 }; 588