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