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