1 /* kernel_interface - file system interface to BeOS' vnode layer 2 ** 3 ** Initial version by Axel Dörfler, axeld@pinc-software.de 4 ** This file may be used under the terms of the OpenBeOS License. 5 */ 6 7 8 #include "Debug.h" 9 #include "cpp.h" 10 #include "Volume.h" 11 #include "Inode.h" 12 #include "Index.h" 13 #include "BPlusTree.h" 14 #include "Query.h" 15 #include "bfs_control.h" 16 17 #include <string.h> 18 #include <stdio.h> 19 20 // BeOS vnode layer stuff 21 #include <KernelExport.h> 22 #ifndef _IMPEXP_KERNEL 23 # define _IMPEXP_KERNEL 24 #endif 25 26 extern "C" { 27 #include <fsproto.h> 28 #include <lock.h> 29 #include <cache.h> 30 } 31 #include <fs_index.h> 32 #include <fs_query.h> 33 34 35 #ifdef USER 36 # define dprintf printf 37 #endif 38 39 40 extern "C" { 41 static int bfs_mount(nspace_id nsid, const char *device, ulong flags, 42 void *parms, size_t len, void **data, vnode_id *vnid); 43 static int bfs_unmount(void *_ns); 44 static int bfs_read_fs_stat(void *_ns, struct fs_info *); 45 static int bfs_write_fs_stat(void *ns, struct fs_info *, long mode); 46 static int bfs_initialize(const char *devname, void *parms, size_t len); 47 48 static int bfs_sync(void *ns); 49 50 static int bfs_read_vnode(void *_ns, vnode_id vnid, char r, void **node); 51 static int bfs_release_vnode(void *_ns, void *_node, char r); 52 static int bfs_remove_vnode(void *ns, void *node, char r); 53 54 static int bfs_walk(void *_ns, void *_base, const char *file, 55 char **newpath, vnode_id *vnid); 56 57 static int bfs_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf,size_t len); 58 static int bfs_setflags(void *ns, void *node, void *cookie, int flags); 59 60 static int bfs_select(void *ns, void *node, void *cookie, uint8 event, 61 uint32 ref, selectsync *sync); 62 static int bfs_deselect(void *ns, void *node, void *cookie, uint8 event, 63 selectsync *sync); 64 static int bfs_fsync(void *ns,void *node); 65 66 static int bfs_create(void *ns, void *dir, const char *name, 67 int perms, int omode, vnode_id *vnid, void **cookie); 68 static int bfs_symlink(void *ns, void *dir, const char *name, 69 const char *path); 70 static int bfs_link(void *ns, void *dir, const char *name, void *node); 71 static int bfs_unlink(void *ns, void *dir, const char *name); 72 static int bfs_rename(void *ns, void *oldDir, const char *oldName, void *newDir, const char *newName); 73 74 static int bfs_read_stat(void *_ns, void *_node, struct stat *st); 75 static int bfs_write_stat(void *ns, void *node, struct stat *st, long mask); 76 77 static int bfs_open(void *_ns, void *_node, int omode, void **cookie); 78 static int bfs_read(void *_ns, void *_node, void *cookie, off_t pos, 79 void *buf, size_t *len); 80 static int bfs_write(void *ns, void *node, void *cookie, off_t pos, 81 const void *buf, size_t *len); 82 static int bfs_free_cookie(void *ns, void *node, void *cookie); 83 static int bfs_close(void *ns, void *node, void *cookie); 84 85 static int bfs_access(void *_ns, void *_node, int mode); 86 static int bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize); 87 88 // directory functions 89 static int bfs_mkdir(void *ns, void *dir, const char *name, int perms); 90 static int bfs_rmdir(void *ns, void *dir, const char *name); 91 static int bfs_open_dir(void *_ns, void *_node, void **cookie); 92 static int bfs_read_dir(void *_ns, void *_node, void *cookie, 93 long *num, struct dirent *dirent, size_t bufferSize); 94 static int bfs_rewind_dir(void *_ns, void *_node, void *cookie); 95 static int bfs_close_dir(void *_ns, void *_node, void *cookie); 96 static int bfs_free_dir_cookie(void *_ns, void *_node, void *cookie); 97 98 // attribute support 99 static int bfs_open_attrdir(void *ns, void *node, void **cookie); 100 static int bfs_close_attrdir(void *ns, void *node, void *cookie); 101 static int bfs_free_attrdir_cookie(void *ns, void *node, void *cookie); 102 static int bfs_rewind_attrdir(void *ns, void *node, void *cookie); 103 static int bfs_read_attrdir(void *ns, void *node, void *cookie, long *num, 104 struct dirent *buf, size_t bufferSize); 105 static int bfs_remove_attr(void *ns, void *node, const char *name); 106 static int bfs_rename_attr(void *ns, void *node, const char *oldname, 107 const char *newname); 108 static int bfs_stat_attr(void *ns, void *node, const char *name, 109 struct attr_info *buf); 110 static int bfs_write_attr(void *ns, void *node, const char *name, int type, 111 const void *buf, size_t *len, off_t pos); 112 static int bfs_read_attr(void *ns, void *node, const char *name, int type, 113 void *buf, size_t *len, off_t pos); 114 115 // index support 116 static int bfs_open_indexdir(void *ns, void **cookie); 117 static int bfs_close_indexdir(void *ns, void *cookie); 118 static int bfs_free_indexdir_cookie(void *ns, void *node, void *cookie); 119 static int bfs_rewind_indexdir(void *ns, void *cookie); 120 static int bfs_read_indexdir(void *ns, void *cookie, long *num,struct dirent *dirent, 121 size_t bufferSize); 122 static int bfs_create_index(void *ns, const char *name, int type, int flags); 123 static int bfs_remove_index(void *ns, const char *name); 124 static int bfs_rename_index(void *ns, const char *oldname, const char *newname); 125 static int bfs_stat_index(void *ns, const char *name, struct index_info *indexInfo); 126 127 // query support 128 static int bfs_open_query(void *ns, const char *query, ulong flags, 129 port_id port, long token, void **cookie); 130 static int bfs_close_query(void *ns, void *cookie); 131 static int bfs_free_query_cookie(void *ns, void *node, void *cookie); 132 static int bfs_read_query(void *ns, void *cookie, long *num, 133 struct dirent *buf, size_t bufsize); 134 135 // Dano compatibility (required for booting) 136 static int bfs_wake_vnode(void *ns, void *node); 137 static int bfs_suspend_vnode(void *ns, void *node); 138 } // extern "C" 139 140 141 /* vnode_ops struct. Fill this in to tell the kernel how to call 142 functions in your driver. 143 */ 144 145 vnode_ops fs_entry = { 146 &bfs_read_vnode, // read_vnode 147 &bfs_release_vnode, // write_vnode 148 &bfs_remove_vnode, // remove_vnode 149 NULL, // secure_vnode (not needed) 150 &bfs_walk, // walk 151 &bfs_access, // access 152 &bfs_create, // create 153 &bfs_mkdir, // mkdir 154 &bfs_symlink, // symlink 155 &bfs_link, // link 156 &bfs_rename, // rename 157 &bfs_unlink, // unlink 158 &bfs_rmdir, // rmdir 159 &bfs_read_link, // readlink 160 &bfs_open_dir, // opendir 161 &bfs_close_dir, // closedir 162 &bfs_free_dir_cookie, // free_dircookie 163 &bfs_rewind_dir, // rewinddir 164 &bfs_read_dir, // readdir 165 &bfs_open, // open file 166 &bfs_close, // close file 167 &bfs_free_cookie, // free cookie 168 &bfs_read, // read file 169 &bfs_write, // write file 170 NULL, // readv 171 NULL, // writev 172 &bfs_ioctl, // ioctl 173 &bfs_setflags, // setflags file 174 &bfs_read_stat, // read stat 175 &bfs_write_stat, // write stat 176 &bfs_fsync, // fsync 177 &bfs_initialize, // initialize 178 &bfs_mount, // mount 179 &bfs_unmount, // unmount 180 &bfs_sync, // sync 181 &bfs_read_fs_stat, // read fs stat 182 &bfs_write_fs_stat, // write fs stat 183 &bfs_select, // select 184 &bfs_deselect, // deselect 185 186 &bfs_open_indexdir, // open index dir 187 &bfs_close_indexdir, // close index dir 188 &bfs_free_indexdir_cookie, // free index dir cookie 189 &bfs_rewind_indexdir, // rewind index dir 190 &bfs_read_indexdir, // read index dir 191 &bfs_create_index, // create index 192 &bfs_remove_index, // remove index 193 &bfs_rename_index, // rename index 194 &bfs_stat_index, // stat index 195 196 &bfs_open_attrdir, // open attr dir 197 &bfs_close_attrdir, // close attr dir 198 &bfs_free_attrdir_cookie, // free attr dir cookie 199 &bfs_rewind_attrdir, // rewind attr dir 200 &bfs_read_attrdir, // read attr dir 201 &bfs_write_attr, // write attr 202 &bfs_read_attr, // read attr 203 &bfs_remove_attr, // remove attr 204 &bfs_rename_attr, // rename attr 205 &bfs_stat_attr, // stat attr 206 207 &bfs_open_query, // open query 208 &bfs_close_query, // close query 209 &bfs_free_query_cookie, // free query cookie 210 &bfs_read_query, // read query 211 212 &bfs_wake_vnode, // these two are added for Dano compatibility; 213 &bfs_suspend_vnode // they do nothing. 214 }; 215 216 #define BFS_IO_SIZE 65536 217 218 // ToDo: has to change to "bfs" later 219 #ifdef COMPILE_FOR_R5 220 # define BFS_NAME "bfs" 221 #else 222 # define BFS_NAME "obfs" 223 #endif 224 225 int32 api_version = B_CUR_FS_API_VERSION; 226 227 228 static int 229 bfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms, 230 size_t len, void **data, vnode_id *rootID) 231 { 232 FUNCTION(); 233 234 #ifndef USER 235 // If you can't build the file system because of this line, you can either 236 // add the prototype: 237 // extern int load_driver_symbols(const char *driver_name); 238 // to your KernelExport.h include (since it's missing there in some releases 239 // of BeOS R5), or just comment out the line, it won't do any harm and is 240 // used only for debugging purposes. 241 load_driver_symbols(BFS_NAME); 242 #endif 243 244 Volume *volume = new Volume(nsid); 245 if (volume == NULL) 246 return B_NO_MEMORY; 247 248 status_t status; 249 if ((status = volume->Mount(device, flags)) == B_OK) { 250 *data = volume; 251 *rootID = volume->ToVnode(volume->Root()); 252 INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n", 253 volume->Name(), *rootID, device)); 254 } 255 else 256 delete volume; 257 258 RETURN_ERROR(status); 259 } 260 261 262 static int 263 bfs_unmount(void *ns) 264 { 265 FUNCTION(); 266 Volume* volume = (Volume *)ns; 267 268 status_t status = volume->Unmount(); 269 delete volume; 270 271 RETURN_ERROR(status); 272 } 273 274 275 /** Fill in bfs_info struct for device. 276 */ 277 278 static int 279 bfs_read_fs_stat(void *_ns, struct fs_info *info) 280 { 281 FUNCTION(); 282 if (_ns == NULL || info == NULL) 283 return B_BAD_VALUE; 284 285 Volume *volume = (Volume *)_ns; 286 287 // File system flags. 288 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME | B_FS_HAS_QUERY | 289 (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 290 291 info->io_size = BFS_IO_SIZE; 292 // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now 293 294 info->block_size = volume->BlockSize(); 295 info->total_blocks = volume->NumBlocks(); 296 info->free_blocks = volume->FreeBlocks(); 297 298 // Volume name 299 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 300 301 // File system name 302 strlcpy(info->fsh_name, BFS_NAME, sizeof(info->fsh_name)); 303 304 return B_NO_ERROR; 305 } 306 307 308 static int 309 bfs_write_fs_stat(void *_ns, struct fs_info *info, long mask) 310 { 311 FUNCTION_START(("mask = %ld\n",mask)); 312 Volume *volume = (Volume *)_ns; 313 disk_super_block &superBlock = volume->SuperBlock(); 314 315 Locker locker(volume->Lock()); 316 317 status_t status = B_BAD_VALUE; 318 319 if (mask & WFSSTAT_NAME) { 320 strncpy(superBlock.name, info->volume_name, sizeof(superBlock.name) - 1); 321 superBlock.name[sizeof(superBlock.name) - 1] = '\0'; 322 323 status = volume->WriteSuperBlock(); 324 } 325 return status; 326 } 327 328 329 int 330 bfs_initialize(const char *deviceName, void *parms, size_t len) 331 { 332 FUNCTION_START(("deviceName = %s, parameter len = %ld\n", deviceName, len)); 333 334 // ToDo: implement bfs_initialize()! 335 336 return B_ERROR; 337 } 338 339 340 int 341 bfs_sync(void *_ns) 342 { 343 FUNCTION(); 344 if (_ns == NULL) 345 return B_BAD_VALUE; 346 347 Volume *volume = (Volume *)_ns; 348 349 return volume->Sync(); 350 } 351 352 353 // #pragma mark - 354 355 356 /** Using vnode id, read in vnode information into fs-specific struct, 357 * and return it in node. the reenter flag tells you if this function 358 * is being called via some other fs routine, so that things like 359 * double-locking can be avoided. 360 */ 361 362 static int 363 bfs_read_vnode(void *_ns, vnode_id id, char reenter, void **_node) 364 { 365 FUNCTION_START(("vnode_id = %Ld\n", id)); 366 Volume *volume = (Volume *)_ns; 367 368 if (id < 0 || id > volume->NumBlocks()) { 369 FATAL(("inode at %Ld requested!\n", id)); 370 return B_ERROR; 371 } 372 373 Inode *inode = new Inode(volume, id, false, reenter); 374 if (inode == NULL) 375 return B_NO_MEMORY; 376 377 status_t status; 378 int32 tries = 0; 379 380 while (true) { 381 if ((status = inode->InitCheck()) == B_OK) { 382 *_node = (void *)inode; 383 return B_OK; 384 } 385 386 // if "status" is B_BUSY, we wait a bit and try again 387 388 if (status == B_BUSY) { 389 // wait for half a second at maximum 390 if (tries++ < 100) { 391 snooze(5000); 392 continue; 393 } 394 FATAL(("inode is not becoming unbusy (id = %Ld)\n", id)); 395 } 396 break; 397 } 398 399 delete inode; 400 RETURN_ERROR(status); 401 } 402 403 404 static int 405 bfs_release_vnode(void *ns, void *_node, char reenter) 406 { 407 //FUNCTION_START(("node = %p\n",_node)); 408 Inode *inode = (Inode *)_node; 409 410 delete inode; 411 412 return B_NO_ERROR; 413 } 414 415 416 int 417 bfs_remove_vnode(void *_ns, void *_node, char reenter) 418 { 419 FUNCTION(); 420 421 if (_ns == NULL || _node == NULL) 422 return B_BAD_VALUE; 423 424 Volume *volume = (Volume *)_ns; 425 Inode *inode = (Inode *)_node; 426 427 // The "chkbfs" functionality uses this flag to prevent the space used 428 // up by the inode from being freed - this flag is set only in situations 429 // where this is a good idea... (the block bitmap will get fixed anyway 430 // in this case). 431 if (inode->Flags() & INODE_DONT_FREE_SPACE) { 432 delete inode; 433 return B_OK; 434 } 435 436 // If the inode isn't in use anymore, we were called before 437 // bfs_unlink() returns - in this case, we can just use the 438 // transaction which has already deleted the inode. 439 Transaction localTransaction, *transaction = &localTransaction; 440 Journal *journal = volume->GetJournal(volume->ToBlock(inode->Parent())); 441 442 if (journal != NULL && journal->CurrentThread() == find_thread(NULL)) 443 transaction = journal->CurrentTransaction(); 444 else 445 localTransaction.Start(volume, inode->BlockNumber()); 446 447 status_t status = inode->Free(transaction); 448 if (status == B_OK) { 449 if (transaction == &localTransaction) 450 localTransaction.Done(); 451 452 delete inode; 453 } 454 455 return status; 456 } 457 458 459 static int 460 bfs_wake_vnode(void *_ns, void *_node) 461 { 462 return B_OK; 463 } 464 465 466 static int 467 bfs_suspend_vnode(void *_ns, void *_node) 468 { 469 return B_OK; 470 } 471 472 473 // #pragma mark - 474 475 476 /** the walk function just "walks" through a directory looking for the 477 * specified file. It calls get_vnode() on its vnode-id to init it 478 * for the kernel. 479 */ 480 481 static int 482 bfs_walk(void *_ns, void *_directory, const char *file, char **_resolvedPath, vnode_id *_vnodeID) 483 { 484 //FUNCTION_START(("file = %s\n",file)); 485 486 if (_ns == NULL || _directory == NULL || file == NULL || _vnodeID == NULL) 487 return B_BAD_VALUE; 488 489 Volume *volume = (Volume *)_ns; 490 Inode *directory = (Inode *)_directory; 491 492 // check access permissions 493 status_t status = directory->CheckPermissions(R_OK); 494 if (status < B_OK) 495 RETURN_ERROR(status); 496 497 BPlusTree *tree; 498 if (directory->GetTree(&tree) != B_OK) 499 RETURN_ERROR(B_BAD_VALUE); 500 501 if ((status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID)) < B_OK) { 502 PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status))); 503 return status; 504 } 505 506 Inode *inode; 507 if ((status = get_vnode(volume->ID(), *_vnodeID, (void **)&inode)) != B_OK) { 508 REPORT_ERROR(status); 509 return B_ENTRY_NOT_FOUND; 510 } 511 512 // Is inode a symlink? Then resolve it, if we should 513 514 if (inode->IsSymLink() && _resolvedPath != NULL) { 515 status_t status = B_OK; 516 char *newPath = NULL; 517 518 // Symbolic links can store their target in the data stream (for links 519 // that take more than 144 bytes of storage [the size of the data_stream 520 // structure]), or directly instead of the data_stream class 521 // So we have to deal with both cases here. 522 523 // Note: we would naturally call bfs_read_link() here, but the API of the 524 // vnode layer would require us to always reserve a large chunk of memory 525 // for the path, so we're not going to do that 526 527 if (inode->Flags() & INODE_LONG_SYMLINK) { 528 size_t readBytes = inode->Node()->data.size; 529 char *data = (char *)malloc(readBytes); 530 if (data != NULL) { 531 status = inode->ReadAt(0, (uint8 *)data, &readBytes); 532 if (status == B_OK && readBytes == inode->Node()->data.size) 533 status = new_path(data, &newPath); 534 535 free(data); 536 } else 537 status = B_NO_MEMORY; 538 } else 539 status = new_path((char *)&inode->Node()->short_symlink, &newPath); 540 541 put_vnode(volume->ID(), inode->ID()); 542 if (status == B_OK) 543 *_resolvedPath = newPath; 544 545 RETURN_ERROR(status); 546 } 547 548 return B_OK; 549 } 550 551 552 int 553 bfs_ioctl(void *_ns, void *_node, void *_cookie, int cmd, void *buffer, size_t bufferLength) 554 { 555 FUNCTION_START(("node = %p, cmd = %d, buf = %p, len = %ld\n", _node, cmd, buffer, bufferLength)); 556 557 if (_ns == NULL) 558 return B_BAD_VALUE; 559 560 Volume *volume = (Volume *)_ns; 561 Inode *inode = (Inode *)_node; 562 563 switch (cmd) { 564 case IOCTL_FILE_UNCACHED_IO: 565 { 566 if (inode == NULL) 567 return B_BAD_VALUE; 568 569 // if the inode is already set up for uncached access, bail out 570 if (inode->Flags() & INODE_NO_CACHE) { 571 FATAL(("File %Ld is already uncached\n", inode->ID())); 572 return B_ERROR; 573 } 574 575 PRINT(("uncached access to inode %Ld\n", inode->ID())); 576 577 // ToDo: sync the cache for this file! 578 // Unfortunately, we can't remove any blocks from the cache in BeOS, 579 // that means we can't guarantee consistency for the file contents 580 // spanning over both access modes. 581 582 // request buffers for being able to access the file without 583 // using the cache or allocating memory 584 status_t status = volume->Pool().RequestBuffers(volume->BlockSize()); 585 if (status == B_OK) 586 inode->Node()->flags |= INODE_NO_CACHE; 587 return status; 588 } 589 case IOCTL_CREATE_TIME: 590 { 591 if (inode == NULL || buffer == NULL) 592 return B_BAD_VALUE; 593 594 off_t *creationTime = (off_t *)buffer; 595 *creationTime = inode->Node()->create_time; 596 return B_OK; 597 } 598 case IOCTL_MODIFIED_TIME: 599 { 600 if (inode == NULL || buffer == NULL) 601 return B_BAD_VALUE; 602 603 off_t *modifiedTime = (off_t *)buffer; 604 *modifiedTime = inode->LastModified(); 605 return B_OK; 606 } 607 case BFS_IOCTL_VERSION: 608 { 609 uint32 *version = (uint32 *)buffer; 610 611 *version = 0x10000; 612 return B_OK; 613 } 614 case BFS_IOCTL_START_CHECKING: 615 { 616 // start checking 617 BlockAllocator &allocator = volume->Allocator(); 618 check_control *control = (check_control *)buffer; 619 620 status_t status = allocator.StartChecking(control); 621 if (status == B_OK && inode != NULL) 622 inode->Node()->flags |= INODE_CHKBFS_RUNNING; 623 624 return status; 625 } 626 case BFS_IOCTL_STOP_CHECKING: 627 { 628 // stop checking 629 BlockAllocator &allocator = volume->Allocator(); 630 check_control *control = (check_control *)buffer; 631 632 status_t status = allocator.StopChecking(control); 633 if (status == B_OK && inode != NULL) 634 inode->Node()->flags &= ~INODE_CHKBFS_RUNNING; 635 636 return status; 637 } 638 case BFS_IOCTL_CHECK_NEXT_NODE: 639 { 640 // check next 641 BlockAllocator &allocator = volume->Allocator(); 642 check_control *control = (check_control *)buffer; 643 644 return allocator.CheckNextNode(control); 645 } 646 #ifdef DEBUG 647 case 56742: 648 { 649 // allocate all free blocks and zero them out (a test for the BlockAllocator)! 650 BlockAllocator &allocator = volume->Allocator(); 651 Transaction transaction(volume, 0); 652 CachedBlock cached(volume); 653 block_run run; 654 while (allocator.AllocateBlocks(&transaction, 8, 0, 64, 1, run) == B_OK) { 655 PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, run.start, run.length)); 656 for (int32 i = 0;i < run.length;i++) { 657 uint8 *block = cached.SetTo(run); 658 if (block != NULL) { 659 memset(block, 0, volume->BlockSize()); 660 cached.WriteBack(&transaction); 661 } 662 } 663 } 664 return B_OK; 665 } 666 case 56743: 667 dump_super_block(&volume->SuperBlock()); 668 return B_OK; 669 case 56744: 670 if (inode != NULL) 671 dump_inode(inode->Node()); 672 return B_OK; 673 case 56745: 674 if (inode != NULL) 675 dump_block((const char *)inode->Node(), volume->BlockSize()); 676 return B_OK; 677 #endif 678 } 679 return B_BAD_VALUE; 680 } 681 682 683 int 684 bfs_setflags(void *ns, void *node, void *cookie, int flags) 685 { 686 FUNCTION_START(("node = %p, flags = %d", node, flags)); 687 688 // ToDo: implement bfs_setflags()! 689 INFORM(("setflags not yet implemented...\n")); 690 691 return B_OK; 692 } 693 694 695 int 696 bfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync) 697 { 698 FUNCTION_START(("event = %d, ref = %lu, sync = %p\n", event, ref, sync)); 699 notify_select_event(sync, ref); 700 701 return B_OK; 702 } 703 704 705 int 706 bfs_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync) 707 { 708 FUNCTION(); 709 return B_OK; 710 } 711 712 713 int 714 bfs_fsync(void *_ns, void *_node) 715 { 716 FUNCTION(); 717 if (_node == NULL) 718 return B_BAD_VALUE; 719 720 Inode *inode = (Inode *)_node; 721 return inode->Sync(); 722 } 723 724 725 /** Fills in the stat struct for a node 726 */ 727 728 static int 729 bfs_read_stat(void *_ns, void *_node, struct stat *st) 730 { 731 FUNCTION(); 732 733 Volume *volume = (Volume *)_ns; 734 Inode *inode = (Inode *)_node; 735 bfs_inode *node = inode->Node(); 736 737 st->st_dev = volume->ID(); 738 st->st_ino = inode->ID(); 739 st->st_nlink = 1; 740 st->st_blksize = BFS_IO_SIZE; 741 742 st->st_uid = node->uid; 743 st->st_gid = node->gid; 744 st->st_mode = node->mode; 745 st->st_size = node->data.size; 746 747 st->st_atime = time(NULL); 748 st->st_mtime = st->st_ctime = (time_t)(node->last_modified_time >> INODE_TIME_SHIFT); 749 st->st_crtime = (time_t)(node->create_time >> INODE_TIME_SHIFT); 750 751 return B_NO_ERROR; 752 } 753 754 755 int 756 bfs_write_stat(void *_ns, void *_node, struct stat *stat, long mask) 757 { 758 FUNCTION(); 759 760 if (_ns == NULL || _node == NULL || stat == NULL) 761 RETURN_ERROR(B_BAD_VALUE); 762 763 Volume *volume = (Volume *)_ns; 764 Inode *inode = (Inode *)_node; 765 766 // that may be incorrect here - I don't think we need write access to 767 // change most of the stat... 768 // we should definitely check a bit more if the new stats are correct and valid... 769 770 status_t status = inode->CheckPermissions(W_OK); 771 if (status < B_OK) 772 RETURN_ERROR(status); 773 774 WriteLocked locked(inode->Lock()); 775 if (locked.IsLocked() < B_OK) 776 RETURN_ERROR(B_ERROR); 777 778 Transaction transaction(volume, inode->BlockNumber()); 779 780 bfs_inode *node = inode->Node(); 781 782 if (mask & WSTAT_SIZE) { 783 // Since WSTAT_SIZE is the only thing that can fail directly, we 784 // do it first, so that the inode state will still be consistent 785 // with the on-disk version 786 if (inode->IsDirectory()) 787 return B_IS_A_DIRECTORY; 788 789 if (inode->Size() != stat->st_size) { 790 status = inode->SetFileSize(&transaction, stat->st_size); 791 if (status < B_OK) 792 return status; 793 794 // fill the new blocks (if any) with zeros 795 inode->FillGapWithZeros(inode->OldSize(), inode->Size()); 796 797 Index index(volume); 798 index.UpdateSize(&transaction, inode); 799 800 if ((mask & WSTAT_MTIME) == 0) 801 index.UpdateLastModified(&transaction, inode); 802 } 803 } 804 805 if (mask & WSTAT_MODE) { 806 PRINT(("original mode = %ld, stat->st_mode = %ld\n", node->mode, stat->st_mode)); 807 node->mode = node->mode & ~S_IUMSK | stat->st_mode & S_IUMSK; 808 } 809 810 if (mask & WSTAT_UID) 811 node->uid = stat->st_uid; 812 if (mask & WSTAT_GID) 813 node->gid = stat->st_gid; 814 815 if (mask & WSTAT_MTIME) { 816 // Index::UpdateLastModified() will set the new time in the inode 817 Index index(volume); 818 index.UpdateLastModified(&transaction,inode,(bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); 819 } 820 if (mask & WSTAT_CRTIME) { 821 node->create_time = (bigtime_t)stat->st_crtime << INODE_TIME_SHIFT; 822 } 823 824 if ((status = inode->WriteBack(&transaction)) == B_OK) 825 transaction.Done(); 826 827 notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); 828 829 return status; 830 } 831 832 833 int 834 bfs_create(void *_ns, void *_directory, const char *name, int omode, int mode, 835 vnode_id *vnodeID, void **_cookie) 836 { 837 FUNCTION_START(("name = \"%s\", perms = %ld, omode = %ld\n", name, mode, omode)); 838 839 if (_ns == NULL || _directory == NULL || _cookie == NULL 840 || name == NULL || *name == '\0') 841 RETURN_ERROR(B_BAD_VALUE); 842 843 Volume *volume = (Volume *)_ns; 844 Inode *directory = (Inode *)_directory; 845 846 if (!directory->IsDirectory()) 847 RETURN_ERROR(B_BAD_TYPE); 848 849 status_t status = directory->CheckPermissions(W_OK); 850 if (status < B_OK) 851 RETURN_ERROR(status); 852 853 // We are creating the cookie at this point, so that we don't have 854 // to remove the inode if we don't have enough free memory later... 855 file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); 856 if (cookie == NULL) 857 RETURN_ERROR(B_NO_MEMORY); 858 859 // initialize the cookie 860 cookie->open_mode = omode; 861 cookie->last_size = 0; 862 cookie->last_notification = system_time(); 863 864 Transaction transaction(volume, directory->BlockNumber()); 865 866 status = Inode::Create(&transaction, directory, name, S_FILE | (mode & S_IUMSK), 867 omode, 0, vnodeID); 868 869 if (status >= B_OK) { 870 transaction.Done(); 871 872 // register the cookie 873 *_cookie = cookie; 874 875 notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, *vnodeID, name); 876 } else 877 free(cookie); 878 879 return status; 880 } 881 882 883 int 884 bfs_symlink(void *_ns, void *_directory, const char *name, const char *path) 885 { 886 FUNCTION(); 887 888 if (_ns == NULL || _directory == NULL || path == NULL 889 || name == NULL || *name == '\0') 890 RETURN_ERROR(B_BAD_VALUE); 891 892 Volume *volume = (Volume *)_ns; 893 Inode *directory = (Inode *)_directory; 894 895 if (!directory->IsDirectory()) 896 RETURN_ERROR(B_BAD_TYPE); 897 898 status_t status = directory->CheckPermissions(W_OK); 899 if (status < B_OK) 900 RETURN_ERROR(status); 901 902 Transaction transaction(volume, directory->BlockNumber()); 903 904 Inode *link; 905 off_t id; 906 status = Inode::Create(&transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link); 907 if (status < B_OK) 908 RETURN_ERROR(status); 909 910 size_t length = strlen(path); 911 if (length < SHORT_SYMLINK_NAME_LENGTH) { 912 strcpy(link->Node()->short_symlink, path); 913 status = link->WriteBack(&transaction); 914 } else { 915 link->Node()->flags |= INODE_LONG_SYMLINK | INODE_LOGGED; 916 // The following call will have to write the inode back, so 917 // we don't have to do that here... 918 status = link->WriteAt(&transaction, 0, (const uint8 *)path, &length); 919 } 920 // ToDo: would be nice if Inode::Create() would let the INODE_NOT_READY 921 // flag set until here, so that it can be accessed directly 922 923 // Inode::Create() left the inode locked in memory 924 put_vnode(volume->ID(), id); 925 926 if (status == B_OK) { 927 transaction.Done(); 928 929 notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); 930 } 931 932 return status; 933 } 934 935 936 int 937 bfs_link(void *ns, void *dir, const char *name, void *node) 938 { 939 FUNCTION_START(("name = \"%s\"\n", name)); 940 941 // ToDo: implement bfs_link()?!? 942 943 return B_ERROR; 944 } 945 946 947 int 948 bfs_unlink(void *_ns, void *_directory, const char *name) 949 { 950 FUNCTION_START(("name = \"%s\"\n",name)); 951 952 if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') 953 return B_BAD_VALUE; 954 if (!strcmp(name, "..") || !strcmp(name, ".")) 955 return B_NOT_ALLOWED; 956 957 Volume *volume = (Volume *)_ns; 958 Inode *directory = (Inode *)_directory; 959 960 status_t status = directory->CheckPermissions(W_OK); 961 if (status < B_OK) 962 return status; 963 964 Transaction transaction(volume, directory->BlockNumber()); 965 966 off_t id; 967 if ((status = directory->Remove(&transaction, name, &id)) == B_OK) { 968 transaction.Done(); 969 970 notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); 971 } 972 return status; 973 } 974 975 976 int 977 bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const char *newName) 978 { 979 FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = \"%s\"\n", _oldDir, oldName, _newDir, newName)); 980 981 // there might be some more tests needed?! 982 if (_ns == NULL || _oldDir == NULL || _newDir == NULL 983 || oldName == NULL || *oldName == '\0' 984 || newName == NULL || *newName == '\0' 985 || !strcmp(oldName, ".") || !strcmp(oldName, "..") 986 || !strcmp(newName, ".") || !strcmp(newName, "..") 987 || strchr(newName, '/') != NULL) 988 RETURN_ERROR(B_BAD_VALUE); 989 990 Volume *volume = (Volume *)_ns; 991 Inode *oldDirectory = (Inode *)_oldDir; 992 Inode *newDirectory = (Inode *)_newDir; 993 994 // get the directory's tree, and a pointer to the inode which should be changed 995 BPlusTree *tree; 996 status_t status = oldDirectory->GetTree(&tree); 997 if (status < B_OK) 998 RETURN_ERROR(status); 999 1000 off_t id; 1001 status = tree->Find((const uint8 *)oldName, strlen(oldName), &id); 1002 if (status < B_OK) 1003 RETURN_ERROR(status); 1004 1005 Vnode vnode(volume, id); 1006 Inode *inode; 1007 if (vnode.Get(&inode) < B_OK) 1008 return B_IO_ERROR; 1009 1010 // Don't move a directory into one of its children - we soar up 1011 // from the newDirectory to either the root node or the old 1012 // directory, whichever comes first. 1013 // If we meet our inode on that way, we have to bail out. 1014 1015 if (oldDirectory != newDirectory) { 1016 vnode_id parent = volume->ToVnode(newDirectory->Parent()); 1017 vnode_id root = volume->RootNode()->ID(); 1018 while (true) 1019 { 1020 if (parent == id) 1021 return B_BAD_VALUE; 1022 else if (parent == root || parent == oldDirectory->ID()) 1023 break; 1024 1025 Vnode vnode(volume,parent); 1026 Inode *parentNode; 1027 if (vnode.Get(&parentNode) < B_OK) 1028 return B_ERROR; 1029 1030 parent = volume->ToVnode(parentNode->Parent()); 1031 } 1032 } 1033 1034 // Everything okay? Then lets get to work... 1035 1036 Transaction transaction(volume, oldDirectory->BlockNumber()); 1037 1038 // First, try to make sure there is nothing that will stop us in 1039 // the target directory - since this is the only non-critical 1040 // failure, we will test this case first 1041 BPlusTree *newTree = tree; 1042 if (newDirectory != oldDirectory) { 1043 status = newDirectory->GetTree(&newTree); 1044 if (status < B_OK) 1045 RETURN_ERROR(status); 1046 } 1047 1048 status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); 1049 if (status == B_NAME_IN_USE) { 1050 // If there is already a file with that name, we have to remove 1051 // it, as long it's not a directory with files in it 1052 off_t clobber; 1053 if (newTree->Find((const uint8 *)newName, strlen(newName), &clobber) < B_OK) 1054 return B_NAME_IN_USE; 1055 if (clobber == id) 1056 return B_BAD_VALUE; 1057 1058 Vnode vnode(volume,clobber); 1059 Inode *other; 1060 if (vnode.Get(&other) < B_OK) 1061 return B_NAME_IN_USE; 1062 1063 status = newDirectory->Remove(&transaction, newName, NULL, other->IsDirectory()); 1064 if (status < B_OK) 1065 return status; 1066 1067 notify_listener(B_ENTRY_REMOVED, volume->ID(), newDirectory->ID(), 0, clobber, NULL); 1068 1069 status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); 1070 } 1071 if (status < B_OK) 1072 return status; 1073 1074 // If anything fails now, we have to remove the inode from the 1075 // new directory in any case to restore the previous state 1076 status_t bailStatus = B_OK; 1077 1078 // update the name only when they differ 1079 bool nameUpdated = false; 1080 if (strcmp(oldName, newName)) { 1081 status = inode->SetName(&transaction, newName); 1082 if (status == B_OK) { 1083 Index index(volume); 1084 index.UpdateName(&transaction, oldName, newName, inode); 1085 nameUpdated = true; 1086 } 1087 } 1088 1089 if (status == B_OK) { 1090 status = tree->Remove(&transaction, (const uint8 *)oldName, strlen(oldName), id); 1091 if (status == B_OK) { 1092 inode->Node()->parent = newDirectory->BlockRun(); 1093 1094 // if it's a directory, update the parent directory pointer 1095 // in its tree if necessary 1096 BPlusTree *movedTree = NULL; 1097 if (oldDirectory != newDirectory 1098 && inode->IsDirectory() 1099 && (status = inode->GetTree(&movedTree)) == B_OK) 1100 status = movedTree->Replace(&transaction, (const uint8 *)"..", 2, newDirectory->ID()); 1101 1102 if (status == B_OK) { 1103 status = inode->WriteBack(&transaction); 1104 if (status == B_OK) { 1105 transaction.Done(); 1106 1107 notify_listener(B_ENTRY_MOVED, volume->ID(), oldDirectory->ID(), 1108 newDirectory->ID(), id, newName); 1109 return B_OK; 1110 } 1111 } 1112 // Those better don't fail, or we switch to a read-only 1113 // device for safety reasons (Volume::Panic() does this 1114 // for us) 1115 // Anyway, if we overwrote a file in the target directory 1116 // this is lost now (only in-memory, not on-disk)... 1117 bailStatus = tree->Insert(&transaction, (const uint8 *)oldName, strlen(oldName), id); 1118 if (movedTree != NULL) 1119 movedTree->Replace(&transaction, (const uint8 *)"..", 2, oldDirectory->ID()); 1120 } 1121 } 1122 if (bailStatus == B_OK && nameUpdated) 1123 bailStatus = inode->SetName(&transaction, oldName); 1124 1125 if (bailStatus == B_OK) 1126 bailStatus = newTree->Remove(&transaction, (const uint8 *)newName, strlen(newName), id); 1127 1128 if (bailStatus < B_OK) 1129 volume->Panic(); 1130 1131 return status; 1132 } 1133 1134 1135 /** Opens the file with the specified mode. 1136 */ 1137 1138 static int 1139 bfs_open(void *_ns, void *_node, int omode, void **_cookie) 1140 { 1141 FUNCTION(); 1142 if (_ns == NULL || _node == NULL || _cookie == NULL) 1143 RETURN_ERROR(B_BAD_VALUE); 1144 1145 Volume *volume = (Volume *)_ns; 1146 Inode *inode = (Inode *)_node; 1147 1148 // we can't open a file which uses uncached access twice 1149 if (inode->Flags() & INODE_NO_CACHE) 1150 return B_BUSY; 1151 1152 // opening a directory read-only is allowed, although you can't read 1153 // any data from it. 1154 if (inode->IsDirectory() && omode & O_RWMASK) { 1155 omode = omode & ~O_RWMASK; 1156 // ToDo: for compatibility reasons, we don't return an error here... 1157 // e.g. "copyattr" tries to do that 1158 //return B_IS_A_DIRECTORY; 1159 } 1160 1161 status_t status = inode->CheckPermissions(oModeToAccess(omode)); 1162 if (status < B_OK) 1163 RETURN_ERROR(status); 1164 1165 // we could actually use the cookie to keep track of: 1166 // - the last block_run 1167 // - the location in the data_stream (indirect, double indirect, 1168 // position in block_run array) 1169 // 1170 // This could greatly speed up continuous reads of big files, especially 1171 // in the indirect block section. 1172 1173 file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); 1174 if (cookie == NULL) 1175 RETURN_ERROR(B_NO_MEMORY); 1176 1177 // initialize the cookie 1178 cookie->open_mode = omode; 1179 // needed by e.g. bfs_write() for O_APPEND 1180 cookie->last_size = inode->Size(); 1181 cookie->last_notification = system_time(); 1182 1183 // Should we truncate the file? 1184 if (omode & O_TRUNC) { 1185 Transaction transaction(volume, inode->BlockNumber()); 1186 WriteLocked locked(inode->Lock()); 1187 1188 status_t status = inode->SetFileSize(&transaction, 0); 1189 if (status < B_OK) { 1190 // bfs_free_cookie() is only called if this function is successful 1191 free(cookie); 1192 return status; 1193 } 1194 1195 transaction.Done(); 1196 } 1197 1198 *_cookie = cookie; 1199 return B_OK; 1200 } 1201 1202 1203 /** Read a file specified by node, using information in cookie 1204 * and at offset specified by pos. read len bytes into buffer buf. 1205 */ 1206 1207 static int 1208 bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length) 1209 { 1210 //FUNCTION(); 1211 Inode *inode = (Inode *)_node; 1212 1213 if (!inode->HasUserAccessableStream()) { 1214 *_length = 0; 1215 RETURN_ERROR(B_BAD_VALUE); 1216 } 1217 1218 ReadLocked locked(inode->Lock()); 1219 return inode->ReadAt(pos, (uint8 *)buffer, _length); 1220 } 1221 1222 1223 int 1224 bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) 1225 { 1226 //FUNCTION(); 1227 // uncomment to be more robust against a buggy vnode layer ;-) 1228 //if (_ns == NULL || _node == NULL || _cookie == NULL) 1229 // return B_BAD_VALUE; 1230 1231 Volume *volume = (Volume *)_ns; 1232 Inode *inode = (Inode *)_node; 1233 1234 if (!inode->HasUserAccessableStream()) { 1235 *_length = 0; 1236 RETURN_ERROR(B_BAD_VALUE); 1237 } 1238 1239 file_cookie *cookie = (file_cookie *)_cookie; 1240 1241 if (cookie->open_mode & O_APPEND) 1242 pos = inode->Size(); 1243 1244 WriteLocked locked(inode->Lock()); 1245 if (locked.IsLocked() < B_OK) 1246 RETURN_ERROR(B_ERROR); 1247 1248 Transaction transaction; 1249 // We are not starting the transaction here, since 1250 // it might not be needed at all (the contents of 1251 // regular files aren't logged) 1252 1253 status_t status = inode->WriteAt(&transaction, pos, (const uint8 *)buffer, _length); 1254 1255 if (status == B_OK) 1256 transaction.Done(); 1257 1258 if ((inode->Flags() & INODE_NO_CACHE) == 0) { 1259 // uncached files don't cause notifications during access, and 1260 // never want to write back any cached blocks 1261 1262 // periodically notify if the file size has changed 1263 // ToDo: should we better test for a change in the last_modified time only? 1264 if (cookie->last_size != inode->Size() 1265 && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { 1266 notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); 1267 cookie->last_size = inode->Size(); 1268 cookie->last_notification = system_time(); 1269 } 1270 1271 // This will flush the dirty blocks to disk from time to time. 1272 // It's done here and not in Inode::WriteAt() so that it won't 1273 // add to the duration of a transaction - it might even be a 1274 // good idea to offload those calls to another thread 1275 volume->WriteCachedBlocksIfNecessary(); 1276 } 1277 1278 return status; 1279 } 1280 1281 1282 /** Do whatever is necessary to close a file, EXCEPT for freeing 1283 * the cookie! 1284 */ 1285 1286 static int 1287 bfs_close(void *_ns, void *_node, void *_cookie) 1288 { 1289 FUNCTION(); 1290 if (_ns == NULL || _node == NULL || _cookie == NULL) 1291 return B_BAD_VALUE; 1292 1293 file_cookie *cookie = (file_cookie *)_cookie; 1294 1295 Volume *volume = (Volume *)_ns; 1296 Inode *inode = (Inode *)_node; 1297 1298 if (cookie->open_mode & O_RWMASK) { 1299 ReadLocked locked(inode->Lock()); 1300 1301 // trim the preallocated blocks and update the size, 1302 // and last_modified indices if needed 1303 1304 Transaction transaction(volume, inode->BlockNumber()); 1305 status_t status = B_OK; 1306 bool changed = false; 1307 Index index(volume); 1308 1309 if (inode->OldSize() != inode->Size()) { 1310 status = inode->Trim(&transaction); 1311 if (status < B_OK) 1312 FATAL(("Could not trim preallocated blocks!")); 1313 1314 index.UpdateSize(&transaction, inode); 1315 changed = true; 1316 } 1317 if (inode->OldLastModified() != inode->LastModified()) { 1318 index.UpdateLastModified(&transaction, inode, inode->LastModified()); 1319 changed = true; 1320 1321 // updating the index doesn't write back the inode 1322 inode->WriteBack(&transaction); 1323 } 1324 1325 if (status == B_OK) 1326 transaction.Done(); 1327 1328 if (changed) 1329 notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); 1330 } 1331 1332 return B_OK; 1333 } 1334 1335 1336 static int 1337 bfs_free_cookie(void *_ns, void *_node, void *_cookie) 1338 { 1339 FUNCTION(); 1340 1341 if (_ns == NULL || _node == NULL || _cookie == NULL) 1342 return B_BAD_VALUE; 1343 1344 file_cookie *cookie = (file_cookie *)_cookie; 1345 1346 Volume *volume = (Volume *)_ns; 1347 Inode *inode = (Inode *)_node; 1348 1349 if (cookie != NULL) 1350 free(cookie); 1351 1352 if (inode->Flags() & INODE_NO_CACHE) { 1353 volume->Pool().ReleaseBuffers(); 1354 inode->Node()->flags &= ~INODE_NO_CACHE; 1355 // We don't need to save the inode, because INODE_NO_CACHE is a 1356 // non-permanent flag which will be removed when the inode is loaded 1357 // into memory. 1358 } 1359 if (inode->Flags() & INODE_CHKBFS_RUNNING) { 1360 // "chkbfs" exited abnormally, so we have to stop it here... 1361 FATAL(("check process was aborted!\n")); 1362 volume->Allocator().StopChecking(NULL); 1363 } 1364 1365 return B_OK; 1366 } 1367 1368 1369 /** Checks access permissions, return B_NOT_ALLOWED if the action 1370 * is not allowed. 1371 */ 1372 1373 static int 1374 bfs_access(void *_ns, void *_node, int accessMode) 1375 { 1376 FUNCTION(); 1377 1378 if (_ns == NULL || _node == NULL) 1379 return B_BAD_VALUE; 1380 1381 Inode *inode = (Inode *)_node; 1382 status_t status = inode->CheckPermissions(accessMode); 1383 if (status < B_OK) 1384 RETURN_ERROR(status); 1385 1386 return B_OK; 1387 } 1388 1389 1390 static int 1391 bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize) 1392 { 1393 FUNCTION(); 1394 1395 Inode *inode = (Inode *)_node; 1396 1397 if (!inode->IsSymLink()) 1398 RETURN_ERROR(B_BAD_VALUE); 1399 1400 if (inode->Flags() & INODE_LONG_SYMLINK) { 1401 status_t status = inode->ReadAt(0, (uint8 *)buffer, bufferSize); 1402 if (status < B_OK) 1403 RETURN_ERROR(status); 1404 1405 *bufferSize = inode->Size(); 1406 return B_OK; 1407 } 1408 1409 size_t numBytes = strlen((char *)&inode->Node()->short_symlink); 1410 uint32 bytes = numBytes; 1411 if (bytes > *bufferSize) 1412 bytes = *bufferSize; 1413 1414 memcpy(buffer, inode->Node()->short_symlink, bytes); 1415 *bufferSize = numBytes; 1416 1417 return B_OK; 1418 } 1419 1420 1421 // #pragma mark - 1422 // Directory functions 1423 1424 1425 int 1426 bfs_mkdir(void *_ns, void *_directory, const char *name, int mode) 1427 { 1428 FUNCTION_START(("name = \"%s\", perms = %ld\n", name, mode)); 1429 1430 if (_ns == NULL || _directory == NULL 1431 || name == NULL || *name == '\0') 1432 RETURN_ERROR(B_BAD_VALUE); 1433 1434 Volume *volume = (Volume *)_ns; 1435 Inode *directory = (Inode *)_directory; 1436 1437 if (!directory->IsDirectory()) 1438 RETURN_ERROR(B_BAD_TYPE); 1439 1440 status_t status = directory->CheckPermissions(W_OK); 1441 if (status < B_OK) 1442 RETURN_ERROR(status); 1443 1444 Transaction transaction(volume, directory->BlockNumber()); 1445 1446 // Inode::Create() locks the inode if we pass the "id" parameter, but we 1447 // need it anyway 1448 off_t id; 1449 status = Inode::Create(&transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id); 1450 if (status == B_OK) { 1451 put_vnode(volume->ID(), id); 1452 transaction.Done(); 1453 1454 notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); 1455 } 1456 1457 return status; 1458 } 1459 1460 1461 int 1462 bfs_rmdir(void *_ns, void *_directory, const char *name) 1463 { 1464 FUNCTION_START(("name = \"%s\"\n", name)); 1465 1466 if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') 1467 return B_BAD_VALUE; 1468 1469 Volume *volume = (Volume *)_ns; 1470 Inode *directory = (Inode *)_directory; 1471 1472 Transaction transaction(volume, directory->BlockNumber()); 1473 1474 off_t id; 1475 status_t status = directory->Remove(&transaction, name, &id, true); 1476 if (status == B_OK) { 1477 transaction.Done(); 1478 1479 notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); 1480 } 1481 1482 return status; 1483 } 1484 1485 1486 /** creates fs-specific "cookie" struct that keeps track of where 1487 * you are at in reading through directory entries in bfs_readdir. 1488 */ 1489 1490 static int 1491 bfs_open_dir(void *_ns, void *_node, void **_cookie) 1492 { 1493 FUNCTION(); 1494 1495 if (_ns == NULL || _node == NULL || _cookie == NULL) 1496 RETURN_ERROR(B_BAD_VALUE); 1497 1498 Inode *inode = (Inode *)_node; 1499 1500 // we don't ask here for directories only, because the bfs_open_index_dir() 1501 // function utilizes us (so we must be able to open indices as well) 1502 if (!inode->IsContainer()) 1503 RETURN_ERROR(B_BAD_VALUE); 1504 1505 BPlusTree *tree; 1506 if (inode->GetTree(&tree) != B_OK) 1507 RETURN_ERROR(B_BAD_VALUE); 1508 1509 TreeIterator *iterator = new TreeIterator(tree); 1510 if (iterator == NULL) 1511 RETURN_ERROR(B_NO_MEMORY); 1512 1513 *_cookie = iterator; 1514 return B_OK; 1515 } 1516 1517 1518 static int 1519 bfs_read_dir(void *_ns, void *_node, void *_cookie, long *num, 1520 struct dirent *dirent, size_t bufferSize) 1521 { 1522 FUNCTION(); 1523 1524 TreeIterator *iterator = (TreeIterator *)_cookie; 1525 if (iterator == NULL) 1526 RETURN_ERROR(B_BAD_VALUE); 1527 1528 uint16 length; 1529 vnode_id id; 1530 status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id); 1531 if (status == B_ENTRY_NOT_FOUND) { 1532 *num = 0; 1533 return B_OK; 1534 } else if (status != B_OK) 1535 RETURN_ERROR(status); 1536 1537 Volume *volume = (Volume *)_ns; 1538 1539 dirent->d_dev = volume->ID(); 1540 dirent->d_ino = id; 1541 1542 #ifdef KEEP_WRONG_DIRENT_RECLEN 1543 dirent->d_reclen = length; 1544 #else 1545 dirent->d_reclen = sizeof(struct dirent) + length; 1546 #endif 1547 1548 *num = 1; 1549 return B_OK; 1550 } 1551 1552 1553 /** Sets the TreeIterator back to the beginning of the directory 1554 */ 1555 1556 static int 1557 bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie) 1558 { 1559 FUNCTION(); 1560 TreeIterator *iterator = (TreeIterator *)_cookie; 1561 1562 if (iterator == NULL) 1563 RETURN_ERROR(B_BAD_VALUE); 1564 1565 return iterator->Rewind(); 1566 } 1567 1568 1569 static int 1570 bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/) 1571 { 1572 FUNCTION(); 1573 // Do whatever you need to to close a directory, but DON'T free the cookie! 1574 return B_OK; 1575 } 1576 1577 1578 static int 1579 bfs_free_dir_cookie(void *ns, void *node, void *_cookie) 1580 { 1581 TreeIterator *iterator = (TreeIterator *)_cookie; 1582 1583 if (iterator == NULL) 1584 RETURN_ERROR(B_BAD_VALUE); 1585 1586 delete iterator; 1587 return B_OK; 1588 } 1589 1590 1591 // #pragma mark - 1592 // Attribute functions 1593 1594 1595 int 1596 bfs_open_attrdir(void *_ns, void *_node, void **cookie) 1597 { 1598 FUNCTION(); 1599 1600 Inode *inode = (Inode *)_node; 1601 if (inode == NULL || inode->Node() == NULL) 1602 RETURN_ERROR(B_ERROR); 1603 1604 AttributeIterator *iterator = new AttributeIterator(inode); 1605 if (iterator == NULL) 1606 RETURN_ERROR(B_NO_MEMORY); 1607 1608 *cookie = iterator; 1609 return B_OK; 1610 } 1611 1612 1613 int 1614 bfs_close_attrdir(void *ns, void *node, void *cookie) 1615 { 1616 FUNCTION(); 1617 return B_OK; 1618 } 1619 1620 1621 int 1622 bfs_free_attrdir_cookie(void *ns, void *node, void *_cookie) 1623 { 1624 FUNCTION(); 1625 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1626 1627 if (iterator == NULL) 1628 RETURN_ERROR(B_BAD_VALUE); 1629 1630 delete iterator; 1631 return B_OK; 1632 } 1633 1634 1635 int 1636 bfs_rewind_attrdir(void *_ns, void *_node, void *_cookie) 1637 { 1638 FUNCTION(); 1639 1640 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1641 if (iterator == NULL) 1642 RETURN_ERROR(B_BAD_VALUE); 1643 1644 RETURN_ERROR(iterator->Rewind()); 1645 } 1646 1647 1648 int 1649 bfs_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent *dirent, size_t bufsize) 1650 { 1651 FUNCTION(); 1652 AttributeIterator *iterator = (AttributeIterator *)_cookie; 1653 1654 if (iterator == NULL) 1655 RETURN_ERROR(B_BAD_VALUE); 1656 1657 uint32 type; 1658 size_t length; 1659 status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino); 1660 if (status == B_ENTRY_NOT_FOUND) { 1661 *num = 0; 1662 return B_OK; 1663 } else if (status != B_OK) 1664 RETURN_ERROR(status); 1665 1666 Volume *volume = (Volume *)_ns; 1667 1668 dirent->d_dev = volume->ID(); 1669 #ifdef KEEP_WRONG_DIRENT_RECLEN 1670 dirent->d_reclen = length; 1671 #else 1672 dirent->d_reclen = sizeof(struct dirent) + length; 1673 #endif 1674 1675 *num = 1; 1676 return B_OK; 1677 } 1678 1679 1680 int 1681 bfs_remove_attr(void *_ns, void *_node, const char *name) 1682 { 1683 FUNCTION_START(("name = \"%s\"\n",name)); 1684 1685 if (_ns == NULL || _node == NULL || name == NULL) 1686 return B_BAD_VALUE; 1687 1688 Volume *volume = (Volume *)_ns; 1689 Inode *inode = (Inode *)_node; 1690 1691 status_t status = inode->CheckPermissions(W_OK); 1692 if (status < B_OK) 1693 return status; 1694 1695 Transaction transaction(volume, inode->BlockNumber()); 1696 1697 status = inode->RemoveAttribute(&transaction, name); 1698 if (status == B_OK) { 1699 transaction.Done(); 1700 1701 notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); 1702 } 1703 1704 RETURN_ERROR(status); 1705 } 1706 1707 1708 int 1709 bfs_rename_attr(void *ns, void *node, const char *oldname, const char *newname) 1710 { 1711 FUNCTION_START(("name = \"%s\",to = \"%s\"\n", oldname, newname)); 1712 1713 // ToDo: implement bfs_rename_attr()! 1714 // Does anybody need this? :-) 1715 1716 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1717 } 1718 1719 1720 int 1721 bfs_stat_attr(void *ns, void *_node, const char *name, struct attr_info *attrInfo) 1722 { 1723 FUNCTION_START(("name = \"%s\"\n",name)); 1724 1725 Inode *inode = (Inode *)_node; 1726 if (inode == NULL || inode->Node() == NULL) 1727 RETURN_ERROR(B_ERROR); 1728 1729 small_data *smallData = NULL; 1730 if (inode->SmallDataLock().Lock() == B_OK) 1731 { 1732 if ((smallData = inode->FindSmallData((const char *)name)) != NULL) { 1733 attrInfo->type = smallData->type; 1734 attrInfo->size = smallData->data_size; 1735 } 1736 inode->SmallDataLock().Unlock(); 1737 } 1738 if (smallData != NULL) 1739 return B_OK; 1740 1741 // search in the attribute directory 1742 Inode *attribute; 1743 status_t status = inode->GetAttribute(name, &attribute); 1744 if (status == B_OK) { 1745 attrInfo->type = attribute->Node()->type; 1746 attrInfo->size = attribute->Node()->data.size; 1747 1748 inode->ReleaseAttribute(attribute); 1749 return B_OK; 1750 } 1751 1752 RETURN_ERROR(status); 1753 } 1754 1755 1756 int 1757 bfs_write_attr(void *_ns, void *_node, const char *name, int type, const void *buffer, 1758 size_t *_length, off_t pos) 1759 { 1760 FUNCTION_START(("name = \"%s\"\n",name)); 1761 1762 if (_ns == NULL || _node == NULL || name == NULL || *name == '\0') 1763 RETURN_ERROR(B_BAD_VALUE); 1764 1765 // Writing the name attribute using this function is not allowed, 1766 // also using the reserved indices name, last_modified, and size 1767 // shouldn't be allowed. 1768 if (name[0] == FILE_NAME_NAME && name[1] == '\0' 1769 || !strcmp(name,"name") 1770 || !strcmp(name,"last_modified") 1771 || !strcmp(name,"size")) 1772 RETURN_ERROR(B_NOT_ALLOWED); 1773 1774 Volume *volume = (Volume *)_ns; 1775 Inode *inode = (Inode *)_node; 1776 1777 status_t status = inode->CheckPermissions(W_OK); 1778 if (status < B_OK) 1779 return status; 1780 1781 Transaction transaction(volume, inode->BlockNumber()); 1782 1783 status = inode->WriteAttribute(&transaction, name, type, pos, (const uint8 *)buffer, _length); 1784 if (status == B_OK) { 1785 transaction.Done(); 1786 1787 notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); 1788 } 1789 1790 return status; 1791 } 1792 1793 1794 int 1795 bfs_read_attr(void *_ns, void *_node, const char *name, int type, void *buffer, 1796 size_t *_length, off_t pos) 1797 { 1798 FUNCTION(); 1799 Inode *inode = (Inode *)_node; 1800 1801 if (inode == NULL || name == NULL || *name == '\0' || buffer == NULL) 1802 RETURN_ERROR(B_BAD_VALUE); 1803 1804 status_t status = inode->CheckPermissions(R_OK); 1805 if (status < B_OK) 1806 return status; 1807 1808 return inode->ReadAttribute(name, type, pos, (uint8 *)buffer, _length); 1809 } 1810 1811 1812 // #pragma mark - 1813 // Index functions 1814 1815 1816 int 1817 bfs_open_indexdir(void *_ns, void **_cookie) 1818 { 1819 FUNCTION(); 1820 1821 if (_ns == NULL || _cookie == NULL) 1822 RETURN_ERROR(B_BAD_VALUE); 1823 1824 Volume *volume = (Volume *)_ns; 1825 1826 if (volume->IndicesNode() == NULL) 1827 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1828 1829 // Since the indices root node is just a directory, and we are storing 1830 // a pointer to it in our Volume object, we can just use the directory 1831 // traversal functions. 1832 // In fact we're storing it in the Volume object for that reason. 1833 1834 RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie)); 1835 } 1836 1837 1838 int 1839 bfs_close_indexdir(void *_ns, void *_cookie) 1840 { 1841 FUNCTION(); 1842 if (_ns == NULL || _cookie == NULL) 1843 RETURN_ERROR(B_BAD_VALUE); 1844 1845 Volume *volume = (Volume *)_ns; 1846 RETURN_ERROR(bfs_close_dir(_ns, volume->IndicesNode(), _cookie)); 1847 } 1848 1849 1850 int 1851 bfs_free_indexdir_cookie(void *_ns, void *_node, void *_cookie) 1852 { 1853 FUNCTION(); 1854 if (_ns == NULL || _cookie == NULL) 1855 RETURN_ERROR(B_BAD_VALUE); 1856 1857 Volume *volume = (Volume *)_ns; 1858 RETURN_ERROR(bfs_free_dir_cookie(_ns, volume->IndicesNode(), _cookie)); 1859 } 1860 1861 1862 int 1863 bfs_rewind_indexdir(void *_ns, void *_cookie) 1864 { 1865 FUNCTION(); 1866 if (_ns == NULL || _cookie == NULL) 1867 RETURN_ERROR(B_BAD_VALUE); 1868 1869 Volume *volume = (Volume *)_ns; 1870 RETURN_ERROR(bfs_rewind_dir(_ns, volume->IndicesNode(), _cookie)); 1871 } 1872 1873 1874 int 1875 bfs_read_indexdir(void *_ns, void *_cookie, long *num, struct dirent *dirent, size_t bufferSize) 1876 { 1877 FUNCTION(); 1878 if (_ns == NULL || _cookie == NULL) 1879 RETURN_ERROR(B_BAD_VALUE); 1880 1881 Volume *volume = (Volume *)_ns; 1882 RETURN_ERROR(bfs_read_dir(_ns, volume->IndicesNode(), _cookie, num, dirent, bufferSize)); 1883 } 1884 1885 1886 int 1887 bfs_create_index(void *_ns, const char *name, int type, int flags) 1888 { 1889 FUNCTION_START(("name = \"%s\", type = %ld, flags = %ld\n", name, type, flags)); 1890 if (_ns == NULL || name == NULL || *name == '\0') 1891 return B_BAD_VALUE; 1892 1893 Volume *volume = (Volume *)_ns; 1894 1895 if (volume->IsReadOnly()) 1896 return B_READ_ONLY_DEVICE; 1897 1898 // only root users are allowed to create indices 1899 if (geteuid() != 0) 1900 return B_NOT_ALLOWED; 1901 1902 Transaction transaction(volume, volume->Indices()); 1903 1904 Index index(volume); 1905 status_t status = index.Create(&transaction, name, type); 1906 1907 if (status == B_OK) 1908 transaction.Done(); 1909 1910 RETURN_ERROR(status); 1911 } 1912 1913 1914 int 1915 bfs_remove_index(void *_ns, const char *name) 1916 { 1917 FUNCTION(); 1918 if (_ns == NULL || name == NULL || *name == '\0') 1919 return B_BAD_VALUE; 1920 1921 Volume *volume = (Volume *)_ns; 1922 1923 if (volume->IsReadOnly()) 1924 return B_READ_ONLY_DEVICE; 1925 1926 // only root users are allowed to remove indices 1927 if (geteuid() != 0) 1928 return B_NOT_ALLOWED; 1929 1930 Inode *indices; 1931 if ((indices = volume->IndicesNode()) == NULL) 1932 return B_ENTRY_NOT_FOUND; 1933 1934 Transaction transaction(volume, volume->Indices()); 1935 1936 status_t status = indices->Remove(&transaction, name); 1937 if (status == B_OK) 1938 transaction.Done(); 1939 1940 RETURN_ERROR(status); 1941 } 1942 1943 1944 int 1945 bfs_rename_index(void *ns, const char *oldname, const char *newname) 1946 { 1947 FUNCTION_START(("from = %s, to = %s\n", oldname, newname)); 1948 1949 // ToDo: implement bfs_rename_index()?! 1950 // Well, renaming an index doesn't make that much sense, as you 1951 // would also need to remove every file in it (or the index 1952 // would contain wrong data) 1953 // But in that case, you can better remove the old one, and 1954 // create a new one... 1955 // There is also no way to call this function from a userland 1956 // application. 1957 1958 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1959 } 1960 1961 1962 int 1963 bfs_stat_index(void *_ns, const char *name, struct index_info *indexInfo) 1964 { 1965 FUNCTION_START(("name = %s\n",name)); 1966 if (_ns == NULL || name == NULL || indexInfo == NULL) 1967 RETURN_ERROR(B_BAD_VALUE); 1968 1969 Volume *volume = (Volume *)_ns; 1970 Index index(volume); 1971 status_t status = index.SetTo(name); 1972 if (status < B_OK) 1973 RETURN_ERROR(status); 1974 1975 bfs_inode *node = index.Node()->Node(); 1976 1977 indexInfo->type = index.Type(); 1978 indexInfo->size = node->data.size; 1979 indexInfo->modification_time = (time_t)(node->last_modified_time >> INODE_TIME_SHIFT); 1980 indexInfo->creation_time = (time_t)(node->create_time >> INODE_TIME_SHIFT); 1981 indexInfo->uid = node->uid; 1982 indexInfo->gid = node->gid; 1983 1984 return B_OK; 1985 } 1986 1987 1988 // #pragma mark - 1989 // Query functions 1990 1991 1992 int 1993 bfs_open_query(void *_ns, const char *queryString, ulong flags, port_id port, 1994 long token, void **cookie) 1995 { 1996 FUNCTION(); 1997 if (_ns == NULL || queryString == NULL || cookie == NULL) 1998 RETURN_ERROR(B_BAD_VALUE); 1999 2000 PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token)); 2001 2002 Volume *volume = (Volume *)_ns; 2003 2004 Expression *expression = new Expression((char *)queryString); 2005 if (expression == NULL) 2006 RETURN_ERROR(B_NO_MEMORY); 2007 2008 if (expression->InitCheck() < B_OK) { 2009 FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position())); 2010 delete expression; 2011 RETURN_ERROR(B_BAD_VALUE); 2012 } 2013 2014 Query *query = new Query(volume, expression); 2015 if (query == NULL) { 2016 delete expression; 2017 RETURN_ERROR(B_NO_MEMORY); 2018 } 2019 2020 if (flags & B_LIVE_QUERY) 2021 query->SetLiveMode(port, token); 2022 2023 *cookie = (void *)query; 2024 2025 return B_OK; 2026 } 2027 2028 2029 int 2030 bfs_close_query(void *ns, void *cookie) 2031 { 2032 FUNCTION(); 2033 return B_OK; 2034 } 2035 2036 2037 int 2038 bfs_free_query_cookie(void *ns, void *node, void *cookie) 2039 { 2040 FUNCTION(); 2041 if (cookie == NULL) 2042 RETURN_ERROR(B_BAD_VALUE); 2043 2044 Query *query = (Query *)cookie; 2045 Expression *expression = query->GetExpression(); 2046 delete query; 2047 delete expression; 2048 2049 return B_OK; 2050 } 2051 2052 2053 int 2054 bfs_read_query(void */*ns*/, void *cookie, long *num, struct dirent *dirent, size_t bufferSize) 2055 { 2056 FUNCTION(); 2057 Query *query = (Query *)cookie; 2058 if (query == NULL) 2059 RETURN_ERROR(B_BAD_VALUE); 2060 2061 status_t status = query->GetNextEntry(dirent, bufferSize); 2062 if (status == B_OK) 2063 *num = 1; 2064 else if (status == B_ENTRY_NOT_FOUND) 2065 *num = 0; 2066 else 2067 return status; 2068 2069 return B_OK; 2070 } 2071 2072