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].length += blockLength; 263 } else { 264 if (index >= max) { 265 // we're out of file_io_vecs; let's bail out 266 *_count = index; 267 return B_BUFFER_OVERFLOW; 268 } 269 270 vecs[index].offset = blockOffset; 271 vecs[index].length = blockLength; 272 index++; 273 } 274 275 offset += blockLength; 276 277 if (size <= vecs[index - 1].length || offset >= inode->Size()) { 278 // We're done! 279 *_count = index; 280 return B_OK; 281 } 282 } 283 284 // can never get here 285 return B_ERROR; 286 } 287 288 289 // #pragma mark - 290 291 292 static status_t 293 ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 294 ino_t* _vnodeID) 295 { 296 Volume* volume = (Volume*)_volume->private_volume; 297 Inode* directory = (Inode*)_directory->private_node; 298 299 // check access permissions 300 status_t status = directory->CheckPermissions(X_OK); 301 if (status < B_OK) 302 return status; 303 304 DirectoryIterator iterator(directory); 305 while (true) { 306 char buffer[B_FILE_NAME_LENGTH]; 307 size_t length = sizeof(buffer); 308 status = iterator.GetNext(buffer, &length, _vnodeID); 309 if (status != B_OK) 310 return status; 311 312 if (!strcmp(buffer, name)) 313 break; 314 } 315 316 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 317 } 318 319 320 static status_t 321 ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 322 { 323 Inode* inode = (Inode*)_node->private_node; 324 const ext2_inode& node = inode->Node(); 325 326 stat->st_dev = inode->GetVolume()->ID(); 327 stat->st_ino = inode->ID(); 328 stat->st_nlink = node.NumLinks(); 329 stat->st_blksize = EXT2_IO_SIZE; 330 331 stat->st_uid = node.UserID(); 332 stat->st_gid = node.GroupID(); 333 stat->st_mode = node.Mode(); 334 stat->st_type = 0; 335 336 stat->st_atime = node.AccessTime(); 337 stat->st_mtime = stat->st_ctime = node.ModificationTime(); 338 stat->st_crtime = node.CreationTime(); 339 340 stat->st_size = inode->Size(); 341 return B_OK; 342 } 343 344 345 static status_t 346 ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 347 { 348 Inode* inode = (Inode*)_node->private_node; 349 350 // opening a directory read-only is allowed, although you can't read 351 // any data from it. 352 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 353 return B_IS_A_DIRECTORY; 354 355 if ((openMode & O_TRUNC) != 0) 356 return B_READ_ONLY_DEVICE; 357 358 return inode->CheckPermissions(open_mode_to_access(openMode) 359 | (openMode & O_TRUNC ? W_OK : 0)); 360 } 361 362 363 static status_t 364 ext2_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, 365 void *buffer, size_t *_length) 366 { 367 Inode *inode = (Inode *)_node->private_node; 368 369 if (!inode->IsFile()) { 370 *_length = 0; 371 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 372 } 373 374 return inode->ReadAt(pos, (uint8 *)buffer, _length); 375 } 376 377 378 static status_t 379 ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 380 { 381 return B_OK; 382 } 383 384 385 static status_t 386 ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie) 387 { 388 return B_OK; 389 } 390 391 392 static status_t 393 ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 394 { 395 Inode* inode = (Inode*)_node->private_node; 396 return inode->CheckPermissions(accessMode); 397 } 398 399 400 static status_t 401 ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 402 size_t *_bufferSize) 403 { 404 Inode* inode = (Inode*)_node->private_node; 405 406 if (!inode->IsSymLink()) 407 return B_BAD_VALUE; 408 409 if (inode->Size() < *_bufferSize) 410 *_bufferSize = inode->Size(); 411 412 if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) 413 return inode->ReadAt(0, (uint8 *)buffer, _bufferSize); 414 415 memcpy(buffer, inode->Node().symlink, *_bufferSize); 416 return B_OK; 417 } 418 419 420 // #pragma mark - Directory functions 421 422 423 static status_t 424 ext2_open_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 425 { 426 Inode *inode = (Inode *)_node->private_node; 427 status_t status = inode->CheckPermissions(R_OK); 428 if (status < B_OK) 429 return status; 430 431 if (!inode->IsDirectory()) 432 return B_BAD_VALUE; 433 434 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 435 if (iterator == NULL) 436 return B_NO_MEMORY; 437 438 *_cookie = iterator; 439 return B_OK; 440 } 441 442 443 static status_t 444 ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 445 struct dirent *dirent, size_t bufferSize, uint32 *_num) 446 { 447 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 448 449 size_t length = bufferSize; 450 ino_t id; 451 status_t status = iterator->GetNext(dirent->d_name, &length, &id); 452 if (status == B_ENTRY_NOT_FOUND) { 453 *_num = 0; 454 return B_OK; 455 } else if (status != B_OK) 456 return status; 457 458 Volume* volume = (Volume*)_volume->private_volume; 459 460 dirent->d_dev = volume->ID(); 461 dirent->d_ino = id; 462 dirent->d_reclen = sizeof(struct dirent) + length; 463 464 *_num = 1; 465 return B_OK; 466 } 467 468 469 static status_t 470 ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 471 { 472 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 473 return iterator->Rewind(); 474 } 475 476 477 static status_t 478 ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 479 { 480 return B_OK; 481 } 482 483 484 static status_t 485 ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 486 { 487 delete (DirectoryIterator *)_cookie; 488 return B_OK; 489 } 490 491 492 fs_volume_ops gExt2VolumeOps = { 493 &ext2_unmount, 494 &ext2_read_fs_info, 495 NULL, // write_fs_info() 496 NULL, // sync() 497 &ext2_get_vnode, 498 }; 499 500 fs_vnode_ops gExt2VnodeOps = { 501 /* vnode operations */ 502 &ext2_lookup, 503 NULL, 504 &ext2_put_vnode, 505 NULL, 506 507 /* VM file access */ 508 &ext2_can_page, 509 &ext2_read_pages, 510 NULL, 511 512 NULL, // io() 513 NULL, // cancel_io() 514 515 &ext2_get_file_map, 516 517 NULL, 518 NULL, 519 NULL, // fs_select 520 NULL, // fs_deselect 521 NULL, 522 523 &ext2_read_link, 524 NULL, 525 526 NULL, 527 NULL, 528 NULL, 529 530 &ext2_access, 531 &ext2_read_stat, 532 NULL, 533 534 /* file operations */ 535 NULL, 536 &ext2_open, 537 &ext2_close, 538 &ext2_free_cookie, 539 &ext2_read, 540 NULL, 541 542 /* directory operations */ 543 NULL, 544 NULL, 545 &ext2_open_dir, 546 &ext2_close_dir, 547 &ext2_free_dir_cookie, 548 &ext2_read_dir, 549 &ext2_rewind_dir, 550 551 NULL, 552 }; 553 554 static file_system_module_info sExt2FileSystem = { 555 { 556 "file_systems/ext2" B_CURRENT_FS_API_VERSION, 557 0, 558 NULL, 559 }, 560 561 "ext2", // short_name 562 "Ext2 File System", // pretty_name 563 0, // DDM flags 564 565 // scanning 566 ext2_identify_partition, 567 ext2_scan_partition, 568 ext2_free_identify_partition_cookie, 569 NULL, // free_partition_content_cookie() 570 571 &ext2_mount, 572 573 NULL, 574 }; 575 576 module_info *modules[] = { 577 (module_info *)&sExt2FileSystem, 578 NULL, 579 }; 580