1/* 2 * Copyright 2020 Suhel Mehta, mehtasuhel@gmail.com 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6#include <string.h> 7 8#ifdef FS_SHELL 9#include "fssh_api_wrapper.h" 10#endif 11#include <file_systems/fs_ops_support.h> 12 13#include "DirectoryIterator.h" 14#include "Inode.h" 15#include "system_dependencies.h" 16#include "ufs2.h" 17#include "Volume.h" 18 19#define TRACE_UFS2 20#ifdef TRACE_UFS2 21#define TRACE(x...) dprintf("\33[34mufs2:\33[0m " x) 22#else 23#define TRACE(x...) ; 24#endif 25#define ERROR(x...) dprintf("\33[34mufs2:\33[0m " x) 26 27 28struct identify_cookie 29{ 30 ufs2_super_block super_block; 31 int cookie; 32}; 33 34 35// #pragma mark - Scanning 36static float 37ufs2_identify_partition(int fd, partition_data *partition, void **_cookie) 38{ 39 ufs2_super_block superBlock; 40 status_t status = Volume::Identify(fd, &superBlock); 41 if (status != B_OK) 42 return -1; 43 44 identify_cookie* cookie = new identify_cookie; 45 memcpy(&cookie->super_block, &superBlock, sizeof(ufs2_super_block)); 46 *_cookie = cookie; 47 48 return 0.8f; 49} 50 51 52static status_t 53ufs2_scan_partition(int fd, partition_data *partition, void *_cookie) 54{ 55 identify_cookie* cookie = (identify_cookie*)_cookie; 56 57 partition->status = B_PARTITION_VALID; 58 partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY; 59 partition->block_size = cookie->super_block.fs_bsize; 60 partition->content_size = cookie->super_block.fs_fsize 61 * cookie->super_block.fs_size; 62 partition->content_name = strdup(cookie->super_block.fs_volname); 63 if (partition->content_name == NULL) 64 return B_NO_MEMORY; 65 66 return B_OK; 67} 68 69 70static void 71ufs2_free_identify_partition_cookie(partition_data *partition, void *_cookie) 72{ 73 identify_cookie* cookie = (identify_cookie*)_cookie; 74 delete cookie; 75 return; 76} 77 78 79// #pragma mark - 80static status_t 81ufs2_mount(fs_volume *_volume, const char *device, uint32 flags, 82 const char *args, ino_t *_rootID) 83{ 84 TRACE("Tracing mount()\n"); 85 Volume* volume = new(std::nothrow) Volume(_volume); 86 if (volume == NULL) 87 return B_NO_MEMORY; 88 89 _volume->private_volume = volume; 90 _volume->ops = &gUfs2VolumeOps; 91 *_rootID = UFS2_ROOT; 92 status_t status = volume->Mount(device, flags); 93 if (status != B_OK){ 94 ERROR("Failed mounting the volume. Error: %s\n", strerror(status)); 95 delete volume; 96 return status; 97 } 98 return B_OK; 99} 100 101 102static status_t 103ufs2_unmount(fs_volume *_volume) 104{ 105 Volume* volume = (Volume *)_volume->private_volume; 106 107 status_t status = volume->Unmount(); 108 delete volume; 109 110 return status; 111} 112 113 114static status_t 115ufs2_read_fs_info(fs_volume *_volume, struct fs_info *info) 116{ 117 Volume* volume = (Volume*)_volume->private_volume; 118 119 // File system flags 120 info->flags = B_FS_IS_PERSISTENT 121 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 122 info->io_size = 65536; 123 info->block_size = volume->SuperBlock().fs_fsize; 124 info->total_blocks = volume->SuperBlock().fs_size; 125 info->free_blocks = volume->SuperBlock().fs_cstotal.cs_nbfree; 126 127 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 128 strlcpy(info->fsh_name, "UFS2", sizeof(info->fsh_name)); 129 130 return B_OK; 131} 132 133 134// #pragma mark - 135 136static status_t 137ufs2_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_node, int *_type, 138 uint32 *_flags, bool reenter) 139{ 140 Volume* volume = (Volume*)_volume->private_volume; 141 142 Inode* inode = new(std::nothrow) Inode(volume, id); 143 if (inode == NULL) 144 return B_NO_MEMORY; 145 146 status_t status = inode->InitCheck(); 147 if (status != B_OK) { 148 delete inode; 149 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status)); 150 return status; 151 } 152 _node->private_node = inode; 153 _node->ops = &gufs2VnodeOps; 154 *_type = inode->Mode(); 155 *_flags = 0; 156 157 return B_OK; 158 159} 160 161 162static status_t 163ufs2_put_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter) 164{ 165 return B_NOT_SUPPORTED; 166} 167 168 169static bool 170ufs2_can_page(fs_volume *_volume, fs_vnode *_node, void *_cookie) 171{ 172 return false; 173} 174 175 176static status_t 177ufs2_read_pages(fs_volume *_volume, fs_vnode *_node, void *_cookie, 178 off_t pos, const iovec *vecs, size_t count, size_t *_numBytes) 179{ 180 return B_NOT_SUPPORTED; 181} 182 183 184static status_t 185ufs2_io(fs_volume *_volume, fs_vnode *_node, void *_cookie, 186 io_request *request) 187{ 188 return B_NOT_SUPPORTED; 189} 190 191 192static status_t 193ufs2_get_file_map(fs_volume *_volume, fs_vnode *_node, off_t offset, 194 size_t size, struct file_io_vec *vecs, size_t *_count) 195{ 196 return B_NOT_SUPPORTED; 197} 198 199 200// #pragma mark - 201 202static status_t 203ufs2_lookup(fs_volume *_volume, fs_vnode *_directory, const char *name, 204 ino_t *_vnodeID) 205{ 206 Volume* volume = (Volume*)_volume->private_volume; 207 Inode* directory = (Inode*)_directory->private_node; 208 209 // check access permissions 210 status_t status = directory->CheckPermissions(X_OK); 211 if (status < B_OK) 212 return status; 213 214 status = DirectoryIterator(directory).Lookup(name, _vnodeID); 215 if (status != B_OK) 216 return status; 217 218 status = get_vnode(volume->FSVolume(), *_vnodeID, NULL); 219 return status; 220} 221 222 223static status_t 224ufs2_ioctl(fs_volume *_volume, fs_vnode *_node, void *_cookie, uint32 cmd, 225 void *buffer, size_t bufferLength) 226{ 227 return B_NOT_SUPPORTED; 228} 229 230 231static status_t 232ufs2_read_stat(fs_volume *_volume, fs_vnode *_node, struct stat *stat) 233{ 234 Inode* inode = (Inode*)_node->private_node; 235 stat->st_dev = inode->GetVolume()->ID(); 236 stat->st_ino = inode->ID(); 237 stat->st_nlink = inode->LinkCount(); 238 stat->st_blksize = 65536; 239 240 stat->st_uid = inode->UserID(); 241 stat->st_gid = inode->GroupID(); 242 stat->st_mode = inode->Mode(); 243 stat->st_type = 0; 244 245 inode->GetAccessTime(stat->st_atim); 246 inode->GetModificationTime(stat->st_mtim); 247 inode->GetChangeTime(stat->st_ctim); 248 inode->GetCreationTime(stat->st_crtim); 249 250 stat->st_size = inode->Size(); 251 stat->st_blocks = inode->NumBlocks(); 252 253 return B_OK; 254} 255 256 257static status_t 258ufs2_open(fs_volume * _volume, fs_vnode *_node, int openMode, 259 void **_cookie) 260{ 261 //Volume* volume = (Volume*)_volume->private_volume; 262 Inode* inode = (Inode*)_node->private_node; 263 264 // opening a directory read-only is allowed, although you can't read 265 // any data from it. 266 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 267 return B_IS_A_DIRECTORY; 268 269 status_t status = inode->CheckPermissions(open_mode_to_access(openMode) 270 | (openMode & O_TRUNC ? W_OK : 0)); 271 if (status != B_OK) 272 return status; 273 274 file_cookie* cookie = new(std::nothrow) file_cookie; 275 if (cookie == NULL) 276 return B_NO_MEMORY; 277 ObjectDeleter<file_cookie> cookieDeleter(cookie); 278 279 cookie->last_size = inode->Size(); 280 cookie->last_notification = system_time(); 281 282// fileCacheEnabler.Detach(); 283 cookieDeleter.Detach(); 284 *_cookie = cookie; 285 return B_OK; 286} 287 288 289static status_t 290ufs2_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, 291 void *buffer, size_t *_length) 292{ 293 Inode* inode = (Inode*)_node->private_node; 294 295 if (!inode->IsFile()) { 296 *_length = 0; 297 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 298 } 299 300 return inode->ReadAt(pos, (uint8*)buffer, _length); 301} 302 303 304static status_t 305ufs2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 306{ 307 return B_OK; 308} 309 310 311static status_t 312ufs2_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 313{ 314 file_cookie* cookie = (file_cookie*)_cookie; 315 316 delete cookie; 317 return B_OK; 318} 319 320static status_t 321ufs2_access(fs_volume *_volume, fs_vnode *_node, int accessMode) 322{ 323 Inode* inode = (Inode*)_node->private_node; 324 return inode->CheckPermissions(accessMode); 325} 326 327 328static status_t 329ufs2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 330 size_t *_bufferSize) 331{ 332 Inode* inode = (Inode*)_node->private_node; 333 334 return inode->ReadLink(buffer, _bufferSize); 335} 336 337 338status_t 339ufs2_unlink(fs_volume *_volume, fs_vnode *_directory, const char *name) 340{ 341 return B_NOT_SUPPORTED; 342} 343 344 345// #pragma mark - Directory functions 346 347static status_t 348ufs2_create_dir(fs_volume *_volume, fs_vnode *_directory, const char *name, 349 int mode) 350{ 351 return B_NOT_SUPPORTED; 352} 353 354 355static status_t 356ufs2_remove_dir(fs_volume *_volume, fs_vnode *_directory, const char *name) 357{ 358 return B_NOT_SUPPORTED; 359} 360 361 362static status_t 363ufs2_open_dir(fs_volume * /*_volume*/, fs_vnode *_node, void **_cookie) 364{ 365 Inode* inode = (Inode*)_node->private_node; 366 367 status_t status = inode->CheckPermissions(R_OK); 368 if (status < B_OK) 369 return status; 370 371 if (!inode->IsDirectory()) 372 return B_NOT_A_DIRECTORY; 373 374 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 375 if (iterator == NULL) 376 return B_NO_MEMORY; 377 378 *_cookie = iterator; 379 return B_OK; 380} 381 382 383static status_t 384ufs2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 385 struct dirent *dirent, size_t bufferSize, uint32 *_num) 386{ 387 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 388 Volume* volume = (Volume*)_volume->private_volume; 389 390 uint32 maxCount = *_num; 391 uint32 count = 0; 392 393 while (count < maxCount 394 && (bufferSize >= sizeof(struct dirent) + B_FILE_NAME_LENGTH)) { 395 size_t length = bufferSize - offsetof(struct dirent, d_name); 396 ino_t iNodeNo; 397 398 status_t status = iterator->GetNext(dirent->d_name, &length, &iNodeNo); 399 if (status == B_ENTRY_NOT_FOUND) 400 break; 401 if (status == B_BUFFER_OVERFLOW) { 402 if (count == 0) 403 return status; 404 break; 405 } 406 if (status != B_OK) 407 return status; 408 409 dirent->d_dev = volume->ID(); 410 dirent->d_ino = iNodeNo; 411 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1; 412 bufferSize -= dirent->d_reclen; 413 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 414 count++; 415 } 416 417 *_num = count; 418 return B_OK; 419 420} 421 422 423static status_t 424ufs2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 425{ 426 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 427 return iterator->Rewind(); 428} 429 430 431static status_t 432ufs2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, 433 void * /*_cookie*/) 434{ 435 return B_OK; 436} 437 438 439static status_t 440ufs2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 441{ 442 delete (DirectoryIterator*)_cookie; 443 return B_OK; 444} 445 446 447static status_t 448ufs2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 449{ 450 return B_NOT_SUPPORTED; 451} 452 453 454static status_t 455ufs2_close_attr_dir(fs_volume *_volume, fs_vnode *_node, void *cookie) 456{ 457 return B_NOT_SUPPORTED; 458} 459 460 461static status_t 462ufs2_free_attr_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 463{ 464 return B_NOT_SUPPORTED; 465} 466 467 468static status_t 469ufs2_read_attr_dir(fs_volume *_volume, fs_vnode *_node, 470 void *_cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num) 471{ 472 return B_NOT_SUPPORTED; 473} 474 475 476static status_t 477ufs2_rewind_attr_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie) 478{ 479 return B_NOT_SUPPORTED; 480} 481 482 483/* attribute operations */ 484static status_t 485ufs2_create_attr(fs_volume *_volume, fs_vnode *_node, 486 const char *name, uint32 type, int openMode, void **_cookie) 487{ 488 return B_NOT_SUPPORTED; 489} 490 491 492static status_t 493ufs2_open_attr(fs_volume *_volume, fs_vnode *_node, const char *name, 494 int openMode, void **_cookie) 495{ 496 return B_NOT_SUPPORTED; 497} 498 499 500static status_t 501ufs2_close_attr(fs_volume *_volume, fs_vnode *_node, 502 void *cookie) 503{ 504 return B_NOT_SUPPORTED; 505} 506 507 508static status_t 509ufs2_free_attr_cookie(fs_volume *_volume, fs_vnode *_node, 510 void *cookie) 511{ 512 return B_NOT_SUPPORTED; 513} 514 515 516static status_t 517ufs2_read_attr(fs_volume *_volume, fs_vnode *_node, void *_cookie, 518 off_t pos, void *buffer, size_t *_length) 519{ 520 return B_NOT_SUPPORTED; 521} 522 523 524static status_t 525ufs2_write_attr(fs_volume *_volume, fs_vnode *_node, void *cookie, 526 off_t pos, const void *buffer, size_t *length) 527{ 528 return B_NOT_SUPPORTED; 529} 530 531 532static status_t 533ufs2_read_attr_stat(fs_volume *_volume, fs_vnode *_node, 534 void *_cookie, struct stat *stat) 535{ 536 return B_NOT_SUPPORTED; 537} 538 539 540static status_t 541ufs2_write_attr_stat(fs_volume *_volume, fs_vnode *_node, 542 void *cookie, const struct stat *stat, int statMask) 543{ 544 return B_NOT_SUPPORTED; 545} 546 547 548static status_t 549ufs2_rename_attr(fs_volume *_volume, fs_vnode *fromVnode, 550 const char *fromName, fs_vnode *toVnode, const char *toName) 551{ 552 return B_NOT_SUPPORTED; 553} 554 555 556static status_t 557ufs2_remove_attr(fs_volume *_volume, fs_vnode *vnode, 558 const char *name) 559{ 560 return B_NOT_SUPPORTED; 561} 562 563 564// #pragma mark - 565 566static status_t 567ufs2_std_ops(int32 op, ...) 568{ 569 switch (op) 570 { 571 case B_MODULE_INIT: 572#ifdef ufs2_DEBUGGER_COMMANDS 573 // Perform nothing at the moment 574 // add_debugger_commands(); 575#endif 576 return B_OK; 577 case B_MODULE_UNINIT: 578#ifdef ufs2_DEBUGGER_COMMANDS 579 // Perform nothing at the moment 580 // remove_debugger_commands(); 581#endif 582 return B_OK; 583 584 default: 585 return B_ERROR; 586 } 587} 588 589fs_volume_ops gUfs2VolumeOps = { 590 &ufs2_unmount, 591 &ufs2_read_fs_info, 592 NULL, //write_fs_info() 593 NULL, // fs_sync, 594 &ufs2_get_vnode, 595}; 596 597fs_vnode_ops gufs2VnodeOps = { 598 /* vnode operations */ 599 &ufs2_lookup, 600 NULL, // ufs2_get_vnode_name - optional, and we can't do better than the 601 // fallback implementation, so leave as NULL. 602 &ufs2_put_vnode, 603 NULL, // ufs2_remove_vnode, 604 605 /* VM file access */ 606 &ufs2_can_page, 607 &ufs2_read_pages, 608 NULL, // ufs2_write_pages, 609 610 &ufs2_io, // io() 611 NULL, // cancel_io() 612 613 &ufs2_get_file_map, 614 615 &ufs2_ioctl, 616 NULL, 617 NULL, // fs_select 618 NULL, // fs_deselect 619 NULL, // fs_fsync, 620 621 &ufs2_read_link, 622 NULL, // fs_create_symlink, 623 624 NULL, // fs_link, 625 &ufs2_unlink, 626 NULL, // fs_rename, 627 628 &ufs2_access, 629 &ufs2_read_stat, 630 NULL, // fs_write_stat, 631 NULL, // fs_preallocate 632 633 /* file operations */ 634 NULL, // fs_create, 635 &ufs2_open, 636 &ufs2_close, 637 &ufs2_free_cookie, 638 &ufs2_read, 639 NULL, // fs_write, 640 641 /* directory operations */ 642 &ufs2_create_dir, 643 &ufs2_remove_dir, 644 &ufs2_open_dir, 645 &ufs2_close_dir, 646 &ufs2_free_dir_cookie, 647 &ufs2_read_dir, 648 &ufs2_rewind_dir, 649 650 /* attribute directory operations */ 651 &ufs2_open_attr_dir, 652 &ufs2_close_attr_dir, 653 &ufs2_free_attr_dir_cookie, 654 &ufs2_read_attr_dir, 655 &ufs2_rewind_attr_dir, 656 657 /* attribute operations */ 658 &ufs2_create_attr, 659 &ufs2_open_attr, 660 &ufs2_close_attr, 661 &ufs2_free_attr_cookie, 662 &ufs2_read_attr, 663 &ufs2_write_attr, 664 &ufs2_read_attr_stat, 665 &ufs2_write_attr_stat, 666 &ufs2_rename_attr, 667 &ufs2_remove_attr, 668}; 669 670 671static file_system_module_info sufs2FileSystem = { 672 { 673 "file_systems/ufs2" B_CURRENT_FS_API_VERSION, 674 0, 675 ufs2_std_ops, 676 }, 677 678 "ufs2", // short_name 679 "Unix Filesystem 2", // pretty_name 680 681 // DDM flags 682 B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 683 // | B_DISK_SYSTEM_SUPPORTS_WRITING 684 , 685 686 // scanning 687 ufs2_identify_partition, 688 ufs2_scan_partition, 689 ufs2_free_identify_partition_cookie, 690 NULL, // free_partition_content_cookie() 691 692 &ufs2_mount, 693}; 694 695module_info *modules[] = { 696 (module_info *)&sufs2FileSystem, 697 NULL, 698}; 699