diff --git a/src/tests/add-ons/kernel/file_systems/bfs/Jamfile b/src/tests/add-ons/kernel/file_systems/bfs/Jamfile index 77c99dc7b8..77a04726e7 100644 --- a/src/tests/add-ons/kernel/file_systems/bfs/Jamfile +++ b/src/tests/add-ons/kernel/file_systems/bfs/Jamfile @@ -10,12 +10,8 @@ SimpleTest bfs_attribute_iterator_test : SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs array ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs bufferPool ; -SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs bfs_shell ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs btree ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs dump_log ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs fragmenter ; -SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs mkbfs ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs queries ; -SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs r5 ; -SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs rename ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs structureSizes ; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/Debug.h b/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/Debug.h deleted file mode 100644 index b27af6d2a8..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/Debug.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef DEBUG_H -#define DEBUG_H -/* Debug - debug stuff -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - -#ifdef DEBUG -# include -#endif - -#ifdef USER -# include -# define __out printf -#else -# define __out dprintf -#endif - -// Which debugger should be used when? -// The DEBUGGER() macro actually has no effect if DEBUG is not defined, -// use the DIE() macro if you really want to die. -#ifdef DEBUG -# ifdef USER -# define DEBUGGER(x) debugger x -# else -# define DEBUGGER(x) kernel_debugger x -# endif -#else -# define DEBUGGER(x) ; -#endif - -#ifdef USER -# define DIE(x) debugger x -#else -# define DIE(x) kernel_debugger x -#endif - -// Short overview over the debug output macros: -// PRINT() -// is for general messages that very unlikely should appear in a release build -// FATAL() -// this is for fatal messages, when something has really gone wrong -// INFORM() -// general information, as disk size, etc. -// REPORT_ERROR(status_t) -// prints out error information -// RETURN_ERROR(status_t) -// calls REPORT_ERROR() and return the value -// D() -// the statements in D() are only included if DEBUG is defined - -#include -#define kprintf printf -#define dprintf printf - -#ifdef DEBUG - #define PRINT(x) { __out("bfs@%ld: ", find_thread(NULL)); __out x; } - #define REPORT_ERROR(status) __out("bfs@%ld: %s:%s:%d: %s\n", find_thread(NULL), __FILE__, __FUNCTION__, __LINE__, strerror(status)); - #define RETURN_ERROR(err) { status_t _status = err; if (_status < B_OK) REPORT_ERROR(_status); return _status;} - #define FATAL(x) { __out("bfs@%ld: ", find_thread(NULL)); __out x; } - #define INFORM(x) { __out("bfs@%ld: ", find_thread(NULL)); __out x; } - #define FUNCTION() __out("bfs@%ld: %s:%s()\n", find_thread(NULL), __FILE__, __FUNCTION__); - #define FUNCTION_START(x) { __out("bfs@%ld: %s:%s() ", find_thread(NULL), __FILE__, __FUNCTION__); __out x; } -// #define FUNCTION() ; -// #define FUNCTION_START(x) ; - #define D(x) {x;}; - #define ASSERT(x) { if (!(x)) DEBUGGER(("bfs: assert failed: " #x "\n")); } -#else - #define PRINT(x) ; - #define REPORT_ERROR(status) ; - #define RETURN_ERROR(status) return status; - #define FATAL(x) { __out("bfs: "); __out x; } - #define INFORM(x) { __out("bfs: "); __out x; } - #define FUNCTION() ; - #define FUNCTION_START(x) ; - #define D(x) ; - #define ASSERT(x) ; -#endif - -#ifdef DEBUG - struct block_run; - struct bplustree_header; - struct bplustree_node; - struct data_stream; - struct bfs_inode; - struct disk_super_block; - class Volume; - - // some structure dump functions - extern void dump_block_run(const char *prefix, block_run &run); - extern void dump_super_block(disk_super_block *superBlock); - extern void dump_data_stream(data_stream *stream); - extern void dump_inode(bfs_inode *inode); - extern void dump_bplustree_header(bplustree_header *header); - extern void dump_bplustree_node(bplustree_node *node,bplustree_header *header = NULL,Volume *volume = NULL); - extern void dump_block(const char *buffer, int size); - - extern void add_debugger_commands(); - extern void remove_debugger_commands(); -#endif - -#endif /* DEBUG_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/Jamfile b/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/Jamfile deleted file mode 100644 index 2a0209c2fa..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/Jamfile +++ /dev/null @@ -1,89 +0,0 @@ -SubDir HAIKU_TOP src tests add-ons kernel file_systems bfs bfs_shell ; - -include [ FDirName $(HAIKU_TOP) src tests add-ons kernel file_systems fs_shell - FSShellRules ] ; - -local bfsSourceDir = [ FDirName $(HAIKU_TOP) src tests add-ons kernel - file_systems bfs r5 ] ; - -local oldSubDirHdrs = $(SUBDIRHDRS) ; -local oldSubDirSysHdrs = $(SUBDIRSYSHDRS) ; - - -# Compile additional_commands.c with the same environment as the fs_shell. That -# is all BeOS headers, but the platform POSIX and STL headers. - -SubDirHdrs $(HAIKU_TOP) src tests add-ons kernel file_systems fs_shell ; -SubDirHdrs $(bfsSourceDir) ; - -SetupFSShellIncludes ; - -ObjectDefines [ FGristFiles additional_commands.c ] - : _NO_INLINE_ASM _GNU_SOURCE ; - -BuildPlatformObjects [ FGristFiles additional_commands.c ] : false ; - - -# Reset the environment for the FS add-on sources. We build using all BFS -# headers, including the POSIX and STL headers. - -SUBDIRHDRS = $(oldSubDirHdrs) ; -SUBDIRSYSHDRS = $(oldSubDirSysHdrs) ; - -SubDirHdrs $(bfsSourceDir) ; -UsePrivateHeaders [ FDirName kernel ] ; # For kernel_cpp.cpp - -SYSHDRS = $(HAIKU_HDRS_$(HAIKU_PACKAGING_ARCH)) ; - - -{ - # set CHECK_MALLOC to something when doing tests against memory corruption - local malloc_debug_defines ; - local malloc_debug_flags ; - if $(CHECK_MALLOC) { - malloc_debug_defines = _NO_INLINE_ASM ; - malloc_debug_flags = -fcheck-memory-usage ; - } - - # We need to use a wrapper for some POSIX calls on non-BeOS systems. - local buildPlatformDefines ; - if $(OS) != BEOS { - buildPlatformDefines = - open=build_platform_open - close=build_platform_close - fstat=build_platform_fstat - read_pos=build_platform_read_pos - ioctl=build_platform_ioctl - MEAN_BFS_SHELL_SELECT_HACK=1 - ; - } - - local defines = [ FDefines USER - #DEBUG - USED_IN_FS_SHELL NO_FILE_UNCACHED_IO - UNSAFE_GET_VNODE - #BFS_BIG_ENDIAN_ONLY - $(malloc_debug_defines) - $(buildPlatformDefines) - ] ; - SubDirCcFlags $(defines) $(malloc_debug_flags) -nostdinc ; - SubDirC++Flags $(defines) -fno-exceptions -fno-rtti $(malloc_debug_flags) - -nostdinc - -include [ FDirName $(SUBDIR) Debug.h ] ; -} - -BuildPlatformMain bfs_shell : - Volume.cpp BPlusTree.cpp Inode.cpp Index.cpp Query.cpp Journal.cpp - BlockAllocator.cpp kernel_interface_r5.cpp Utility.cpp BufferPool.cpp - Debug.cpp - - [ FGristFiles additional_commands$(SUFOBJ) ] -; -LinkAgainst bfs_shell : libfs_shell.a $(HOST_LIBSTDC++) ; - -# Tell Jam where to find these sources -SEARCH on [ FGristFiles - Volume.cpp BPlusTree.cpp Inode.cpp Index.cpp Query.cpp Journal.cpp - BlockAllocator.cpp kernel_interface_r5.cpp Utility.cpp BufferPool.cpp - Debug.cpp cpp.cpp - ] = $(bfsSourceDir) ; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/additional_commands.c b/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/additional_commands.c deleted file mode 100644 index 44d0e8f37d..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/bfs_shell/additional_commands.c +++ /dev/null @@ -1,95 +0,0 @@ - -#include "compat.h" - -#include -#include - -#include "fsproto.h" -#include "kprotos.h" -#include "bfs_control.h" - -#include "additional_commands.h" - - -static int -do_chkbfs(int argc, char **argv) -{ - struct check_control result; - fs_off_t files = 0, directories = 0, indices = 0, attributeDirectories = 0, attributes = 0; - int counter = 0; - - int fd = sys_open(1, -1, "/myfs/.", O_RDONLY, S_IFREG, 0); - if (fd < 0) { - printf("chkbfs: error opening '.'\n"); - return fd; - } - - memset(&result, 0, sizeof(result)); - result.magic = BFS_IOCTL_CHECK_MAGIC; - result.flags = argc > 1 ? BFS_FIX_BITMAP_ERRORS : 0; - if (argc > 2) { - printf("will fix any severe errors!\n"); - result.flags |= BFS_REMOVE_WRONG_TYPES | BFS_REMOVE_INVALID; - } - - // start checking - if ((sys_ioctl(1, fd, BFS_IOCTL_START_CHECKING, &result, sizeof(result))) < 0) { - printf("chkbfs: error starting!\n"); - } - - // check all files and report errors - while (sys_ioctl(1, fd, BFS_IOCTL_CHECK_NEXT_NODE, &result, sizeof(result)) == FS_OK) { - if (++counter % 50 == 0) - printf(" %7d nodes processed\x1b[1A\n", counter); - - if (result.errors) { - printf("%s (inode = %Ld)", result.name, result.inode); - if (result.errors & BFS_MISSING_BLOCKS) - printf(", some blocks weren't allocated"); - if (result.errors & BFS_BLOCKS_ALREADY_SET) - printf(", has blocks already set"); - if (result.errors & BFS_INVALID_BLOCK_RUN) - printf(", has invalid block run(s)"); - if (result.errors & BFS_COULD_NOT_OPEN) - printf(", could not be opened"); - if (result.errors & BFS_WRONG_TYPE) - printf(", has wrong type"); - if (result.errors & BFS_NAMES_DONT_MATCH) - printf(", names don't match"); - putchar('\n'); - } - if ((result.mode & (MY_S_INDEX_DIR | 0777)) == MY_S_INDEX_DIR) - indices++; - else if (result.mode & MY_S_ATTR_DIR) - attributeDirectories++; - else if (result.mode & MY_S_ATTR) - attributes++; - else if (result.mode & MY_S_IFDIR) - directories++; - else - files++; - } - if (result.status != FS_ENTRY_NOT_FOUND) - printf("chkbfs: error occured during scan: %s\n", strerror(result.status)); - - // stop checking - if ((sys_ioctl(1, fd, BFS_IOCTL_STOP_CHECKING, &result, sizeof(result))) < 0) { - printf("chkbfs: error stopping!\n"); - } - - printf("checked %d nodes, %Ld blocks not allocated, %Ld blocks already set, %Ld blocks could be freed\n", - counter, result.stats.missing, result.stats.already_set, result.stats.freed); - printf("\tfiles\t\t%Ld\n\tdirectories\t%Ld\n\tattributes\t%Ld\n\tattr. dirs\t%Ld\n\tindices\t\t%Ld\n", - files, directories, attributes, attributeDirectories, indices); - if (result.flags & BFS_FIX_BITMAP_ERRORS) - printf("errors have been fixed\n"); - - sys_close(1, fd); - - return 0; -} - -cmd_entry additional_commands[] = { - { "chkbfs", do_chkbfs, "does a chkbfs on the volume" }, - { NULL, NULL } -}; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/Jamfile b/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/Jamfile deleted file mode 100644 index 1696d8e645..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/Jamfile +++ /dev/null @@ -1,58 +0,0 @@ -SubDir HAIKU_TOP src tests add-ons kernel file_systems bfs mkbfs ; - -SubDirHdrs $(HAIKU_TOP) src tests add-ons kernel file_systems fs_shell ; -SubDirHdrs $(HAIKU_TOP) src tests add-ons kernel file_systems bfs r5 ; -UsePrivateHeaders [ FDirName kernel ] ; - -{ - # set CHECK_MALLOC to something when doing tests against memory corruption - local malloc_debug_defines ; - local malloc_debug_flags ; - if $(CHECK_MALLOC) { - malloc_debug_defines = _NO_INLINE_ASM ; - malloc_debug_flags = -fcheck-memory-usage ; - } - - local defines = [ FDefines USER - UNSAFE_GET_VNODE - #BFS_BIG_ENDIAN_ONLY - $(malloc_debug_defines) ] ; - SubDirCcFlags $(defines) -fno-exceptions -fno-rtti $(malloc_debug_flags) ; - SubDirC++Flags $(defines) -fno-exceptions -fno-rtti $(malloc_debug_flags) ; -} - -SimpleTest mkbfs : - mkbfs.cpp - - cache.c - kernel.c - sl.c - sysdep.c - rootfs.c - errors.cpp - - Volume.cpp BPlusTree.cpp Inode.cpp Index.cpp Query.cpp Journal.cpp - BlockAllocator.cpp kernel_interface_r5.cpp Utility.cpp BufferPool.cpp - Debug.cpp - : - ; - -SimpleTest allocationGroups : - allocationGroups.cpp - ; - -# Tell Jam where to find these sources -SEARCH on [ FGristFiles - Volume.cpp BPlusTree.cpp Inode.cpp Index.cpp Query.cpp Journal.cpp - BlockAllocator.cpp kernel_interface_r5.cpp Utility.cpp BufferPool.cpp - Debug.cpp - ] = [ FDirName $(HAIKU_TOP) src tests add-ons kernel file_systems bfs r5 ] ; - -SEARCH on [ FGristFiles - fsh.c rootfs.c initfs.c kernel.c cache.c sl.c stub.c tracker.cpp - sysdep.c hexdump.c argv.c errors.cpp - ] = [ FDirName $(HAIKU_TOP) src tests add-ons kernel file_systems fs_shell ] ; - -SEARCH on [ FGristFiles - kernel_cpp.cpp - ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/allocationGroups.cpp b/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/allocationGroups.cpp deleted file mode 100644 index cacd9282d4..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/allocationGroups.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include - -#include -#include -#include -#include - - -static const uint32 kDesiredAllocationGroups = 56; - - -int -main(int argc, char **argv) -{ - if (argc != 3 || !isdigit(argv[1][0]) || !isdigit(argv[2][0])) { - const char *name = strrchr(argv[0], '/'); - name = name ? name + 1 : argv[0]; - - fprintf(stderr, "usage: %s \n", name); - return -1; - } - - off_t numBlocks = atoll(argv[1]); - uint32 blockSize = (uint32)atol(argv[2]); - - if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096 && blockSize != 8192) { - fprintf(stderr, "valid values for are: 1024, 2048, 4096, 8192\n"); - return -1; - } - - int32 blockShift = 9; - while ((1UL << blockShift) < blockSize) { - blockShift++; - } - - uint32 blocks_per_ag = 1; - uint32 ag_shift = 13; - uint32 num_ags = 0; - - uint32 bitsPerBlock = blockSize << 3; - off_t bitmapBlocks = (numBlocks + bitsPerBlock - 1) / bitsPerBlock; - for (uint32 i = 8192; i < bitsPerBlock; i *= 2) { - ag_shift++; - } - - // Many allocation groups help applying allocation policies, but if - // they are too small, we will need to many block_runs to cover large - // files - - while (true) { - num_ags = (bitmapBlocks + blocks_per_ag - 1) / blocks_per_ag; - if (num_ags > kDesiredAllocationGroups) { - if (ag_shift == 16) - break; - - ag_shift++; - blocks_per_ag *= 2; - } else - break; - } - - printf("blocks = %Ld\n", numBlocks); - printf("block shift = %ld\n", blockShift); - printf("bits per block = %lu\n", bitsPerBlock); - printf("bitmap blocks = %Ld\n", bitmapBlocks); - printf("allocation groups = %lu\n", num_ags); - printf("shift = %lu (%lu)\n", ag_shift, 1UL << ag_shift); - printf("blocks per group = %lu\n", blocks_per_ag); - - uint32 bitsPerGroup = 8 * (blocks_per_ag << blockShift); - - printf("\nBits per group: %ld\n", bitsPerGroup); - printf("Bits in last group: %ld\n", numBlocks - (num_ags - 1) * bitsPerGroup); - - return 0; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/boot_block.h b/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/boot_block.h deleted file mode 100644 index 39b7b1de45..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/boot_block.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef BOOT_BLOCK_DATA_H -#define BOOT_BLOCK_DATA_H - -// Note: the boot block here is taken directly from my main BFS disk. -// There is no Haiku replacement for that code yet! - -static uint8 sBootBlockData1[512] = { - 0xb8, 0x00, 0x90, 0x8e, 0xd0, 0x89, 0xc4, 0xfc, 0x68, 0xc0, 0x07, 0x1f, 0x1e, 0x07, 0x80, 0xfa, - 0x02, 0x72, 0x0c, 0x80, 0xfa, 0x80, 0x72, 0x05, 0x80, 0xfa, 0x90, 0x72, 0x02, 0xb2, 0x80, 0x66, - 0x31, 0xc0, 0x40, 0xbb, 0x00, 0x02, 0xe8, 0x7e, 0x00, 0x81, 0x7f, 0x20, 0x31, 0x53, 0x75, 0x75, - 0x80, 0xbf, 0xff, 0x01, 0x20, 0x75, 0x6e, 0xbe, 0x74, 0x02, 0xe8, 0x82, 0x01, 0xe8, 0xa3, 0x02, - 0xbf, 0x07, 0x03, 0xbb, 0x00, 0x06, 0x57, 0xe8, 0xf5, 0x00, 0x72, 0x59, 0x5f, 0x53, 0xbb, 0x00, - 0x02, 0x59, 0x51, 0x81, 0xc3, 0x00, 0x04, 0x39, 0xcb, 0x73, 0xe8, 0x66, 0xff, 0x47, 0x10, 0x75, - 0xf0, 0x66, 0xff, 0x47, 0x14, 0x75, 0xea, 0xb0, 0x2f, 0x89, 0xd9, 0x57, 0xf2, 0xae, 0x75, 0x35, - 0x29, 0xd9, 0xf7, 0xd1, 0x5f, 0x60, 0xe8, 0x2b, 0x02, 0x61, 0x72, 0xd5, 0x8c, 0xd5, 0x01, 0xcf, - 0x47, 0x38, 0x05, 0x75, 0xbe, 0xb8, 0x00, 0x10, 0x8e, 0xc0, 0x31, 0xdb, 0xc1, 0xeb, 0x04, 0x8c, - 0xc0, 0x01, 0xd8, 0x8e, 0xc0, 0x31, 0xdb, 0xe8, 0xa5, 0x00, 0x73, 0xf0, 0x66, 0xa1, 0xab, 0x00, - 0xea, 0x00, 0x02, 0x00, 0x10, 0xeb, 0x7f, 0x66, 0x60, 0x66, 0x05, 0x5b, 0x63, 0x40, 0x01, 0x06, - 0x53, 0x52, 0x66, 0x50, 0x66, 0x31, 0xf6, 0x66, 0xc7, 0x04, 0x10, 0x00, 0x01, 0x00, 0x89, 0x5c, - 0x04, 0x8c, 0x44, 0x06, 0x66, 0x89, 0x44, 0x08, 0x66, 0x89, 0x74, 0x0c, 0xb4, 0x08, 0xcd, 0x13, - 0x88, 0xc8, 0x86, 0xe9, 0xc0, 0xc5, 0x02, 0x80, 0xe5, 0x03, 0x89, 0xcd, 0x25, 0x3f, 0x00, 0x89, - 0xc3, 0xb2, 0x00, 0x86, 0xd6, 0x42, 0xf7, 0xe2, 0x91, 0x58, 0x5a, 0xe3, 0x39, 0x39, 0xca, 0x73, - 0x2a, 0xf7, 0xf1, 0x39, 0xc5, 0x76, 0x24, 0x91, 0x92, 0xf6, 0xf3, 0x86, 0xcd, 0xc0, 0xc9, 0x02, - 0x08, 0xe1, 0x41, 0x5a, 0x88, 0xc6, 0x5b, 0x07, 0xbf, 0x05, 0x00, 0xb8, 0x01, 0x02, 0xcd, 0x13, - 0x73, 0x27, 0x31, 0xc0, 0xcd, 0x13, 0x4f, 0x75, 0xf2, 0xeb, 0x03, 0x5a, 0x5b, 0x07, 0x31, 0xf6, - 0xb4, 0x42, 0xcd, 0x13, 0x73, 0x13, 0xbe, 0xd4, 0x01, 0x31, 0xdb, 0xb4, 0x0e, 0xac, 0xcd, 0x10, - 0x3c, 0x2e, 0x75, 0xf9, 0x93, 0xcd, 0x16, 0xcd, 0x19, 0x66, 0x61, 0xc3, 0x01, 0x46, 0x00, 0x8b, - 0x76, 0x00, 0xe8, 0x7a, 0x00, 0x83, 0x3c, 0x00, 0x75, 0x27, 0xb8, 0x08, 0x00, 0x01, 0x46, 0x00, - 0xff, 0x4e, 0x02, 0x74, 0x08, 0x83, 0x4c, 0x08, 0x00, 0x75, 0xe4, 0xf9, 0xc3, 0xff, 0x46, 0x02, - 0xfe, 0x0e, 0x10, 0x00, 0x7c, 0xf5, 0x81, 0xfd, 0x00, 0x90, 0x74, 0xd0, 0x83, 0xed, 0x04, 0xeb, - 0xce, 0xff, 0x44, 0xfe, 0xff, 0x0c, 0x80, 0x3e, 0x10, 0x00, 0x02, 0x74, 0x24, 0x06, 0x53, 0x1e, - 0x07, 0x8d, 0x5e, 0x08, 0xc1, 0xe3, 0x0b, 0xe8, 0x17, 0x00, 0x93, 0x5b, 0x07, 0x83, 0xc5, 0x04, - 0x89, 0x7e, 0x00, 0x29, 0xf8, 0xc1, 0xe8, 0x03, 0x89, 0x46, 0x02, 0xfe, 0x06, 0x10, 0x00, 0xeb, - 0x9e, 0xbe, 0x00, 0x02, 0x89, 0xdf, 0x8a, 0x4c, 0x2c, 0x80, 0xe9, 0x09, 0x66, 0xd3, 0xe0, 0x8b, - 0x4c, 0x28, 0xc1, 0xe9, 0x09, 0xe8, 0xef, 0xfe, 0x01, 0xf3, 0x66, 0x40, 0xe2, 0xf7, 0xc3, 0x51, - 0x66, 0xad, 0x8a, 0x0e, 0x4c, 0x02, 0x66, 0xd3, 0xe0, 0x66, 0x91, 0x66, 0x31, 0xc0, 0xad, 0x66, - 0x01, 0xc8, 0x59, 0xc3, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x20, 0x4f, 0x53, 0x3b, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, - 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x2e, 0x55, 0xaa, -}; - -static const size_t kBootBlockData2Offset = 676; -static uint8 sBootBlockData2[348] = { - 0x52, 0x8d, 0x77, 0x18, 0xad, 0x92, 0x52, 0xad, 0x89, 0xf3, 0x89, 0xf5, 0x01, 0xc6, 0x83, 0xc6, - 0x07, 0x81, 0xe6, 0xf8, 0xff, 0xad, 0x01, 0xe8, 0x29, 0xd8, 0x39, 0xc8, 0x75, 0x08, 0x60, 0x89, - 0xde, 0xf3, 0xa6, 0x61, 0x74, 0x09, 0x01, 0xc3, 0x4a, 0x75, 0xea, 0x58, 0x5a, 0xf9, 0xc3, 0x58, - 0x29, 0xd0, 0xc1, 0xe0, 0x03, 0x01, 0xc6, 0x4a, 0x01, 0xd6, 0x01, 0xd6, 0x5a, 0x66, 0xad, 0x8c, - 0xd5, 0xbb, 0x00, 0x04, 0xe8, 0xb6, 0xfe, 0x66, 0x81, 0x3d, 0xd9, 0x0a, 0xbe, 0x3b, 0x75, 0x10, - 0xc6, 0x06, 0x10, 0x00, 0x02, 0xc7, 0x46, 0x00, 0x48, 0x04, 0xc7, 0x46, 0x02, 0x0c, 0x00, 0xc3, - 0xe9, 0x1f, 0xfe, 0x62, 0x65, 0x6f, 0x73, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x7a, - 0x62, 0x65, 0x6f, 0x73, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, -}; - -static const size_t kBootBlockData1Size = sizeof(sBootBlockData1); -static const size_t kBootBlockData2Size = sizeof(sBootBlockData2); - -static const size_t kBIOSDriveOffset = 0x1e; -static const size_t kPartitionDataOffset = 0xab; - -#endif /* BOOT_BLOCK_DATA_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/mkbfs.cpp b/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/mkbfs.cpp deleted file mode 100644 index 64a63b55e4..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/mkbfs/mkbfs.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include "Volume.h" - -#include "boot_block.h" -#include "compat.h" - -// the definitions in fsproto.h (included by kprotos.h) and fs_interface.h -// (included by Volume.h) are not compatible -#define get_vnode _get_vnode -#define put_vnode _put_vnode -#define new_vnode _new_vnode -#define remove_vnode _remove_vnode -#define unremove_vnode _unremove_vnode -#define notify_listener _notify_listener -#define send_notification _send_notification - -#include "kprotos.h" - -#include -#include -#include -#include - - -// Note: these structures have been stolen from fs_shell/kernel.c. -// They shouldn't be used anywhere else - -extern vnode_ops fs_entry; -struct vnode; - -struct vnlist { - vnode *head; - vnode *tail; - int num; -}; - -struct nspace { - nspace_id nsid; - my_dev_t dev; - my_ino_t ino; - fsystem *fs; - vnlist vnodes; - void *data; - vnode *root; - vnode *mount; - nspace *prev; - nspace *next; - char shutdown; -}; - - -void -usage(char *programName) -{ - fprintf(stderr, "usage: %s [-vn] [-b block-size] device-path volume-name\n" - "Creates a new BFS file system on the specified device (or image)\n" - "\t-b\tblock size (defaults to 1024)\n" - "\t-n\tdisable indices (long option --noindex)\n" - "\t-v\tfor verbose output.\n", - programName); - - exit(0); -} - - -int -main(int argc, char **argv) -{ - char *programName = argv[0]; - if (strrchr(programName, '/')) - programName = strrchr(programName, '/') + 1; - - if (argc < 2) - usage(programName); - - uint32 blockSize = 1024; - uint32 flags = 0; - bool verbose = false; - - while (*++argv) { - char *arg = *argv; - if (*arg == '-') { - if (arg[1] == '-') { - if (!strcmp(arg, "--noindex")) - flags |= VOLUME_NO_INDICES; - else - usage(programName); - } - - while (*++arg && isalpha(*arg)) { - switch (*arg) { - case 'v': - verbose = true; - break; - case 'b': - if (*++argv == NULL || !isdigit(**argv)) - usage(programName); - - blockSize = atol(*argv); - break; - case 'n': - flags |= VOLUME_NO_INDICES; - break; - } - } - } - else - break; - } - - char *deviceName = argv[0]; - if (deviceName == NULL) { - fprintf(stderr, "%s: You must at least specify a device where " - "the image is going to be created\n", programName); - return -1; - } - - char *volumeName = "Unnamed"; - if (argv[1] != NULL) - volumeName = argv[1]; - - if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096 && blockSize != 8192) { - fprintf(stderr, "%s: valid block sizes are: 1024, 2048, 4096, and 8192\n", programName); - return -1; - } - - // set up tiny VFS and initialize the device/image - - init_block_cache(4096, 0); - init_vnode_layer(); - - if (install_file_system(&fs_entry, "bfs", 1, -1) == NULL) { - printf("can't install my file system\n"); - exit(0); - } - - struct nspace *mount = (nspace *)malloc(sizeof(nspace)); - if (add_nspace(mount, NULL, "bfs", -1, -1) < B_OK) { - fprintf(stderr, "%s: add mount structure failed\n", programName); - return -1; - } - - Volume volume(mount->nsid); - status_t status = volume.Initialize(deviceName, volumeName, blockSize, flags); - if (status < B_OK) { - fprintf(stderr, "%s: Initializing volume failed: %s\n", programName, strerror(status)); - return -1; - } - - remove_nspace(mount); - - if (verbose) { - disk_super_block super = volume.SuperBlock(); - - printf("%s: Disk was initialized successfully.\n", programName); - printf("\tname: \"%s\"\n", super.name); - printf("\tnum blocks: %Ld\n", super.NumBlocks()); - printf("\tused blocks: %Ld\n", super.UsedBlocks()); - printf("\tblock size: %lu bytes\n", super.BlockSize()); - printf("\tnum allocation groups: %ld\n", super.AllocationGroups()); - printf("\tallocation group size: %ld blocks\n", 1L << super.AllocationGroupShift()); - printf("\tlog size: %u blocks\n", super.log_blocks.Length()); - } - - shutdown_block_cache(); - - // make the disk image bootable - - int device = open(deviceName, O_RDWR); - if (device < 0) { - fprintf(stderr, "%s: Could not make image bootable: %s\n", programName, strerror(errno)); - return -1; - } - - // change BIOS drive and partition offset - // ToDo: for now, this will only work for images only - - sBootBlockData1[kBIOSDriveOffset] = 0x80; - // for now, this should be replaced by the real thing - uint32 *offset = (uint32 *)&sBootBlockData1[kPartitionDataOffset]; - *offset = 0; - - write_pos(device, 0, sBootBlockData1, kBootBlockData1Size); - write_pos(device, kBootBlockData2Offset, sBootBlockData2, kBootBlockData2Size); - - close(device); - sync(); - - return 0; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/BPlusTree.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/BPlusTree.cpp deleted file mode 100644 index 7798083be0..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/BPlusTree.cpp +++ /dev/null @@ -1,2141 +0,0 @@ -/* BPlusTree - BFS B+Tree implementation - * - * Roughly based on 'btlib' written by Marcus J. Ranum - it shares - * no code but achieves binary compatibility with the on disk format. - * - * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de. - * This file may be used under the terms of the MIT License. - */ - - -#include "Debug.h" -#include "BPlusTree.h" -#include "Inode.h" -#include "Utility.h" -#include "Stack.h" - -#include -#include - -#include -#include -#include - - -#ifdef DEBUG -class NodeChecker { - public: - NodeChecker(bplustree_node *node, int32 nodeSize) - : - fNode(node), - fSize(nodeSize) - { - } - - ~NodeChecker() - { - fNode->CheckIntegrity(fSize); - } - - private: - bplustree_node *fNode; - int32 fSize; -}; -#endif - -// Node Caching for the BPlusTree class -// -// With write support, there is the need for a function that allocates new -// nodes by either returning empty nodes, or by growing the file's data stream -// -// !! The CachedNode class assumes that you have properly locked the stream -// !! before asking for nodes. -// -// Note: This code will fail if the block size is smaller than the node size! -// Since BFS supports block sizes of 1024 bytes or greater, and the node size -// is hard-coded to 1024 bytes, that's not an issue now. - -void -CachedNode::Unset() -{ - if (fTree == NULL || fTree->fStream == NULL) - return; - - if (fBlock != NULL) { - release_block(fTree->fStream->GetVolume()->Device(), fBlockNumber); - - fBlock = NULL; - fNode = NULL; - } -} - - -bplustree_node * -CachedNode::SetTo(off_t offset, bool check) -{ - if (fTree == NULL || fTree->fStream == NULL) { - REPORT_ERROR(B_BAD_VALUE); - return NULL; - } - - Unset(); - - // You can only ask for nodes at valid positions - you can't - // even access the b+tree header with this method (use SetToHeader() - // instead) - if (offset > fTree->fHeader->MaximumSize() - fTree->fNodeSize - || offset <= 0 - || (offset % fTree->fNodeSize) != 0) - return NULL; - - if (InternalSetTo(offset) != NULL && check) { - // sanity checks (links, all_key_count) - bplustree_header *header = fTree->fHeader; - if (!header->IsValidLink(fNode->LeftLink()) - || !header->IsValidLink(fNode->RightLink()) - || !header->IsValidLink(fNode->OverflowLink()) - || (int8 *)fNode->Values() + fNode->NumKeys() * sizeof(off_t) > - (int8 *)fNode + fTree->fNodeSize) { - FATAL(("invalid node read from offset %Ld, inode at %Ld\n", - offset, fTree->fStream->ID())); - return NULL; - } - } - return fNode; -} - - -bplustree_header * -CachedNode::SetToHeader() -{ - if (fTree == NULL || fTree->fStream == NULL) { - REPORT_ERROR(B_BAD_VALUE); - return NULL; - } - - Unset(); - - InternalSetTo(0LL); - return (bplustree_header *)fNode; -} - - -bplustree_node * -CachedNode::InternalSetTo(off_t offset) -{ - fNode = NULL; - - off_t fileOffset; - block_run run; - if (offset < fTree->fStream->Size() - && fTree->fStream->FindBlockRun(offset, run, fileOffset) == B_OK) { - Volume *volume = fTree->fStream->GetVolume(); - - int32 blockOffset = (offset - fileOffset) / volume->BlockSize(); - fBlockNumber = volume->ToBlock(run) + blockOffset; - - fBlock = (uint8 *)get_block(volume->Device(), fBlockNumber, volume->BlockSize()); - if (fBlock) { - // the node is somewhere in that block... (confusing offset calculation) - fNode = (bplustree_node *)(fBlock + offset - - (fileOffset + blockOffset * volume->BlockSize())); - } else - REPORT_ERROR(B_IO_ERROR); - } - return fNode; -} - - -status_t -CachedNode::Free(Transaction *transaction, off_t offset) -{ - if (transaction == NULL || fTree == NULL || fTree->fStream == NULL - || offset == BPLUSTREE_NULL) - RETURN_ERROR(B_BAD_VALUE); - - // ToDo: scan the free nodes list and remove all nodes at the end - // of the tree - perhaps that shouldn't be done everytime that - // function is called, perhaps it should be done when the directory - // inode is closed or based on some calculation or whatever... - - // if the node is the last one in the tree, we shrink - // the tree and file size by one node - off_t lastOffset = fTree->fHeader->MaximumSize() - fTree->fNodeSize; - if (offset == lastOffset) { - fTree->fHeader->maximum_size = HOST_ENDIAN_TO_BFS_INT64(lastOffset); - - status_t status = fTree->fStream->SetFileSize(transaction, lastOffset); - if (status < B_OK) - return status; - - return fTree->fCachedHeader.WriteBack(transaction); - } - - // add the node to the free nodes list - fNode->left_link = fTree->fHeader->free_node_pointer; - fNode->overflow_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_FREE); - - if (WriteBack(transaction) == B_OK) { - fTree->fHeader->free_node_pointer = HOST_ENDIAN_TO_BFS_INT64(offset); - return fTree->fCachedHeader.WriteBack(transaction); - } - return B_ERROR; -} - - -status_t -CachedNode::Allocate(Transaction *transaction, bplustree_node **_node, off_t *_offset) -{ - if (transaction == NULL || fTree == NULL || fTree->fHeader == NULL - || fTree->fStream == NULL) { - RETURN_ERROR(B_BAD_VALUE); - } - - status_t status; - - // if there are any free nodes, recycle them - if (SetTo(fTree->fHeader->FreeNode(), false) != NULL) { - *_offset = fTree->fHeader->FreeNode(); - - // set new free node pointer - fTree->fHeader->free_node_pointer = fNode->left_link; - if ((status = fTree->fCachedHeader.WriteBack(transaction)) == B_OK) { - fNode->Initialize(); - *_node = fNode; - return B_OK; - } - return status; - } - // allocate space for a new node - Inode *stream = fTree->fStream; - if ((status = stream->Append(transaction, fTree->fNodeSize)) < B_OK) - return status; - - // the maximum_size has to be changed before the call to SetTo() - or - // else it will fail because the requested node is out of bounds - off_t offset = fTree->fHeader->MaximumSize(); - fTree->fHeader->maximum_size = HOST_ENDIAN_TO_BFS_INT64(fTree->fHeader->MaximumSize() + fTree->fNodeSize); - - if (SetTo(offset, false) != NULL) { - *_offset = offset; - - if (fTree->fCachedHeader.WriteBack(transaction) >= B_OK) { - fNode->Initialize(); - *_node = fNode; - return B_OK; - } - } - RETURN_ERROR(B_ERROR); -} - - -status_t -CachedNode::WriteBack(Transaction *transaction) -{ - if (transaction == NULL || fTree == NULL || fTree->fStream == NULL || fNode == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return transaction->WriteBlocks(fBlockNumber, fBlock); -} - - -// #pragma mark - - - -BPlusTree::BPlusTree(Transaction *transaction, Inode *stream, int32 nodeSize) - : - fStream(NULL), - fHeader(NULL), - fCachedHeader(this) -{ - SetTo(transaction, stream); -} - - -BPlusTree::BPlusTree(Inode *stream) - : - fStream(NULL), - fHeader(NULL), - fCachedHeader(this) -{ - SetTo(stream); -} - - -BPlusTree::BPlusTree() - : - fStream(NULL), - fHeader(NULL), - fCachedHeader(this), - fNodeSize(BPLUSTREE_NODE_SIZE), - fAllowDuplicates(true), - fStatus(B_NO_INIT) -{ -} - - -BPlusTree::~BPlusTree() -{ - // if there are any TreeIterators left, we need to stop them - // (can happen when the tree's inode gets deleted while - // traversing the tree - a TreeIterator doesn't lock the inode) - if (fIteratorLock.Lock() < B_OK) - return; - - TreeIterator *iterator = NULL; - while ((iterator = fIterators.Next(iterator)) != NULL) - iterator->Stop(); - - fIteratorLock.Unlock(); -} - - -/** Create a new B+Tree on the specified stream */ - -status_t -BPlusTree::SetTo(Transaction *transaction, Inode *stream, int32 nodeSize) -{ - // initializes in-memory B+Tree - - fCachedHeader.Unset(); - fStream = stream; - - fHeader = fCachedHeader.SetToHeader(); - if (fHeader == NULL) { - // allocate space for new header + node! - fStatus = stream->SetFileSize(transaction, nodeSize * 2); - if (fStatus < B_OK) - RETURN_ERROR(fStatus); - - fHeader = fCachedHeader.SetToHeader(); - if (fHeader == NULL) - RETURN_ERROR(fStatus = B_ERROR); - } - - fAllowDuplicates = ((stream->Mode() & S_INDEX_DIR) == S_INDEX_DIR - && stream->BlockRun() != stream->Parent()) - || (stream->Mode() & S_ALLOW_DUPS) != 0; - - fNodeSize = nodeSize; - - // initialize b+tree header - fHeader->magic = HOST_ENDIAN_TO_BFS_INT32(BPLUSTREE_MAGIC); - fHeader->node_size = HOST_ENDIAN_TO_BFS_INT32(fNodeSize); - fHeader->max_number_of_levels = HOST_ENDIAN_TO_BFS_INT32(1); - fHeader->data_type = HOST_ENDIAN_TO_BFS_INT32(ModeToKeyType(stream->Mode())); - fHeader->root_node_pointer = HOST_ENDIAN_TO_BFS_INT64(nodeSize); - fHeader->free_node_pointer = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL); - fHeader->maximum_size = HOST_ENDIAN_TO_BFS_INT64(nodeSize * 2); - - if (fCachedHeader.WriteBack(transaction) < B_OK) - RETURN_ERROR(fStatus = B_ERROR); - - // initialize b+tree root node - CachedNode cached(this, fHeader->RootNode(), false); - if (cached.Node() == NULL) - RETURN_ERROR(B_ERROR); - - cached.Node()->Initialize(); - return fStatus = cached.WriteBack(transaction); -} - - -status_t -BPlusTree::SetTo(Inode *stream) -{ - if (stream == NULL || stream->Node() == NULL) - RETURN_ERROR(fStatus = B_BAD_VALUE); - - // get on-disk B+Tree header - - fCachedHeader.Unset(); - fStream = stream; - - fHeader = fCachedHeader.SetToHeader(); - if (fHeader == NULL) - RETURN_ERROR(fStatus = B_NO_INIT); - - // is header valid? - - if (fHeader->Magic() != BPLUSTREE_MAGIC - || fHeader->MaximumSize() != stream->Size() - || (fHeader->RootNode() % fHeader->NodeSize()) != 0 - || !fHeader->IsValidLink(fHeader->RootNode()) - || !fHeader->IsValidLink(fHeader->FreeNode())) - RETURN_ERROR(fStatus = B_BAD_DATA); - - fNodeSize = fHeader->NodeSize(); - - { - uint32 toMode[] = {S_STR_INDEX, S_INT_INDEX, S_UINT_INDEX, S_LONG_LONG_INDEX, - S_ULONG_LONG_INDEX, S_FLOAT_INDEX, S_DOUBLE_INDEX}; - uint32 mode = stream->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX - | S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX); - - if (fHeader->DataType() > BPLUSTREE_DOUBLE_TYPE - || (stream->Mode() & S_INDEX_DIR) && toMode[fHeader->DataType()] != mode - || !stream->IsContainer()) { - D( dump_bplustree_header(fHeader); - dump_inode(stream->Node()); - ); - RETURN_ERROR(fStatus = B_BAD_TYPE); - } - - // although it's in stat.h, the S_ALLOW_DUPS flag is obviously unused - // in the original BFS code - we will honour it nevertheless - fAllowDuplicates = ((stream->Mode() & S_INDEX_DIR) == S_INDEX_DIR - && stream->BlockRun() != stream->Parent()) - || (stream->Mode() & S_ALLOW_DUPS) != 0; - } - - CachedNode cached(this, fHeader->RootNode()); - RETURN_ERROR(fStatus = cached.Node() ? B_OK : B_BAD_DATA); -} - - -status_t -BPlusTree::InitCheck() -{ - return fStatus; -} - - -int32 -BPlusTree::TypeCodeToKeyType(type_code code) -{ - switch (code) { - case B_STRING_TYPE: - return BPLUSTREE_STRING_TYPE; - case B_SSIZE_T_TYPE: - case B_INT32_TYPE: - return BPLUSTREE_INT32_TYPE; - case B_SIZE_T_TYPE: - case B_UINT32_TYPE: - return BPLUSTREE_UINT32_TYPE; - case B_OFF_T_TYPE: - case B_INT64_TYPE: - return BPLUSTREE_INT64_TYPE; - case B_UINT64_TYPE: - return BPLUSTREE_UINT64_TYPE; - case B_FLOAT_TYPE: - return BPLUSTREE_FLOAT_TYPE; - case B_DOUBLE_TYPE: - return BPLUSTREE_DOUBLE_TYPE; - } - return -1; -} - - -int32 -BPlusTree::ModeToKeyType(mode_t mode) -{ - switch (mode & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX - | S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX)) { - case S_INT_INDEX: - return BPLUSTREE_INT32_TYPE; - case S_UINT_INDEX: - return BPLUSTREE_UINT32_TYPE; - case S_LONG_LONG_INDEX: - return BPLUSTREE_INT64_TYPE; - case S_ULONG_LONG_INDEX: - return BPLUSTREE_UINT64_TYPE; - case S_FLOAT_INDEX: - return BPLUSTREE_FLOAT_TYPE; - case S_DOUBLE_INDEX: - return BPLUSTREE_DOUBLE_TYPE; - case S_STR_INDEX: - default: - // default is for standard directories - return BPLUSTREE_STRING_TYPE; - } -} - - -// #pragma mark - - - -void -BPlusTree::UpdateIterators(off_t offset, off_t nextOffset, uint16 keyIndex, uint16 splitAt, - int8 change) -{ - // Although every iterator which is affected by this update currently - // waits on a semaphore, other iterators could be added/removed at - // any time, so we need to protect this loop - if (fIteratorLock.Lock() < B_OK) - return; - - TreeIterator *iterator = NULL; - while ((iterator = fIterators.Next(iterator)) != NULL) - iterator->Update(offset, nextOffset, keyIndex, splitAt, change); - - fIteratorLock.Unlock(); -} - - -void -BPlusTree::AddIterator(TreeIterator *iterator) -{ - if (fIteratorLock.Lock() < B_OK) - return; - - fIterators.Add(iterator); - - fIteratorLock.Unlock(); -} - - -void -BPlusTree::RemoveIterator(TreeIterator *iterator) -{ - if (fIteratorLock.Lock() < B_OK) - return; - - fIterators.Remove(iterator); - - fIteratorLock.Unlock(); -} - - -int32 -BPlusTree::CompareKeys(const void *key1, int keyLength1, const void *key2, int keyLength2) -{ - type_code type = 0; - switch (fHeader->data_type) { - case BPLUSTREE_STRING_TYPE: - type = B_STRING_TYPE; - break; - case BPLUSTREE_INT32_TYPE: - type = B_INT32_TYPE; - break; - case BPLUSTREE_UINT32_TYPE: - type = B_UINT32_TYPE; - break; - case BPLUSTREE_INT64_TYPE: - type = B_INT64_TYPE; - break; - case BPLUSTREE_UINT64_TYPE: - type = B_UINT64_TYPE; - break; - case BPLUSTREE_FLOAT_TYPE: - type = B_FLOAT_TYPE; - break; - case BPLUSTREE_DOUBLE_TYPE: - type = B_DOUBLE_TYPE; - break; - } - return compareKeys(type, key1, keyLength1, key2, keyLength2); -} - - -status_t -BPlusTree::FindKey(bplustree_node *node, const uint8 *key, uint16 keyLength, uint16 *index, - off_t *next) -{ - if (node->all_key_count == 0) { - if (index) - *index = 0; - if (next) - *next = node->OverflowLink(); - return B_ENTRY_NOT_FOUND; - } - - off_t *values = node->Values(); - int16 saveIndex = -1; - - // binary search in the key array - for (int16 first = 0, last = node->NumKeys() - 1; first <= last;) { - uint16 i = (first + last) >> 1; - - uint16 searchLength; - uint8 *searchKey = node->KeyAt(i, &searchLength); - if (searchKey + searchLength + sizeof(off_t) + sizeof(uint16) > (uint8 *)node + fNodeSize - || searchLength > BPLUSTREE_MAX_KEY_LENGTH) { - fStream->GetVolume()->Panic(); - RETURN_ERROR(B_BAD_DATA); - } - - int32 cmp = CompareKeys(key, keyLength, searchKey, searchLength); - if (cmp < 0) { - last = i - 1; - saveIndex = i; - } else if (cmp > 0) { - saveIndex = first = i + 1; - } else { - if (index) - *index = i; - if (next) - *next = BFS_ENDIAN_TO_HOST_INT64(values[i]); - return B_OK; - } - } - - if (index) - *index = saveIndex; - if (next) { - if (saveIndex == node->NumKeys()) - *next = node->OverflowLink(); - else - *next = BFS_ENDIAN_TO_HOST_INT64(values[saveIndex]); - } - return B_ENTRY_NOT_FOUND; -} - - -/** Prepares the stack to contain all nodes that were passed while - * following the key, from the root node to the leaf node that could - * or should contain that key. - */ - -status_t -BPlusTree::SeekDown(Stack &stack, const uint8 *key, uint16 keyLength) -{ - // set the root node to begin with - node_and_key nodeAndKey; - nodeAndKey.nodeOffset = fHeader->RootNode(); - - CachedNode cached(this); - bplustree_node *node; - while ((node = cached.SetTo(nodeAndKey.nodeOffset)) != NULL) { - // if we are already on leaf level, we're done - if (node->OverflowLink() == BPLUSTREE_NULL) { - // node that the keyIndex is not properly set here (but it's not - // needed in the calling functions anyway)! - nodeAndKey.keyIndex = 0; - stack.Push(nodeAndKey); - return B_OK; - } - - off_t nextOffset; - status_t status = FindKey(node, key, keyLength, &nodeAndKey.keyIndex, &nextOffset); - - if (status == B_ENTRY_NOT_FOUND && nextOffset == nodeAndKey.nodeOffset) - RETURN_ERROR(B_ERROR); - - // put the node offset & the correct keyIndex on the stack - stack.Push(nodeAndKey); - - nodeAndKey.nodeOffset = nextOffset; - } - - RETURN_ERROR(B_ERROR); -} - - -status_t -BPlusTree::FindFreeDuplicateFragment(bplustree_node *node, CachedNode *cached, off_t *_offset, - bplustree_node **_fragment, uint32 *_index) -{ - off_t *values = node->Values(); - for (int32 i = 0; i < node->NumKeys(); i++) { - off_t value = BFS_ENDIAN_TO_HOST_INT64(values[i]); - - // does the value link to a duplicate fragment? - if (bplustree_node::LinkType(value) != BPLUSTREE_DUPLICATE_FRAGMENT) - continue; - - bplustree_node *fragment = cached->SetTo(bplustree_node::FragmentOffset(value), false); - if (fragment == NULL) { - FATAL(("Could not get duplicate fragment at %Ld\n", value)); - continue; - } - - // see if there is some space left for us - int32 num = (fNodeSize >> 3) / (NUM_FRAGMENT_VALUES + 1); - for (int32 j = 0;j < num;j++) { - duplicate_array *array = fragment->FragmentAt(j); - - if (array->count == 0) { - *_offset = bplustree_node::FragmentOffset(value); - *_fragment = fragment; - *_index = j; - return B_OK; - } - } - } - return B_ENTRY_NOT_FOUND; -} - - -status_t -BPlusTree::InsertDuplicate(Transaction *transaction, CachedNode *cached, bplustree_node *node, - uint16 index, off_t value) -{ - CachedNode cachedDuplicate(this); - off_t *values = node->Values(); - off_t oldValue = BFS_ENDIAN_TO_HOST_INT64(values[index]); - status_t status; - off_t offset; - - if (bplustree_node::IsDuplicate(oldValue)) { - // If it's a duplicate fragment, try to insert it into that, or if it - // doesn't fit anymore, create a new duplicate node - - if (bplustree_node::LinkType(oldValue) == BPLUSTREE_DUPLICATE_FRAGMENT) { - bplustree_node *duplicate = cachedDuplicate.SetTo( - bplustree_node::FragmentOffset(oldValue), false); - if (duplicate == NULL) - return B_IO_ERROR; - - duplicate_array *array = duplicate->FragmentAt(bplustree_node::FragmentIndex(oldValue)); - if (array->CountItems() > NUM_FRAGMENT_VALUES - || array->CountItems() < 1) { - FATAL(("insertDuplicate: Invalid array[%ld] size in fragment %Ld == %Ld!\n", - bplustree_node::FragmentIndex(oldValue), - bplustree_node::FragmentOffset(oldValue), - array->CountItems())); - return B_BAD_DATA; - } - - if (array->CountItems() < NUM_FRAGMENT_VALUES) { - array->Insert(value); - } else { - // test if the fragment will be empty if we remove this key's values - if (duplicate->FragmentsUsed(fNodeSize) < 2) { - // the node will be empty without our values, so let us - // reuse it as a duplicate node - offset = bplustree_node::FragmentOffset(oldValue); - - memmove(duplicate->DuplicateArray(), array, (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); - duplicate->left_link = duplicate->right_link = HOST_ENDIAN_TO_BFS_INT64( - (uint64)BPLUSTREE_NULL); - - array = duplicate->DuplicateArray(); - array->Insert(value); - } else { - // create a new duplicate node - CachedNode cachedNewDuplicate(this); - bplustree_node *newDuplicate; - status = cachedNewDuplicate.Allocate(transaction, &newDuplicate, &offset); - if (status < B_OK) - return status; - - // copy the array from the fragment node to the duplicate node - // and free the old entry (by zero'ing all values) - newDuplicate->overflow_link = array->count; - memcpy(&newDuplicate->all_key_count, &array->values[0], - array->CountItems() * sizeof(off_t)); - memset(array, 0, (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); - - array = newDuplicate->DuplicateArray(); - array->Insert(value); - - // if this fails, the old fragments node will contain wrong - // data... (but since it couldn't be written, it shouldn't - // be fatal) - if ((status = cachedNewDuplicate.WriteBack(transaction)) < B_OK) - return status; - } - - // update the main pointer to link to a duplicate node - values[index] = HOST_ENDIAN_TO_BFS_INT64(bplustree_node::MakeLink( - BPLUSTREE_DUPLICATE_NODE, offset)); - if ((status = cached->WriteBack(transaction)) < B_OK) - return status; - } - - return cachedDuplicate.WriteBack(transaction); - } - - // Put the value into a dedicated duplicate node - - // search for free space in the duplicate nodes of that key - duplicate_array *array; - bplustree_node *duplicate; - off_t duplicateOffset; - do { - duplicateOffset = bplustree_node::FragmentOffset(oldValue); - duplicate = cachedDuplicate.SetTo(duplicateOffset, false); - if (duplicate == NULL) - return B_IO_ERROR; - - array = duplicate->DuplicateArray(); - if (array->CountItems() > NUM_DUPLICATE_VALUES || array->CountItems() < 0) { - FATAL(("removeDuplicate: Invalid array size in duplicate %Ld == %Ld!\n", - duplicateOffset, array->CountItems())); - return B_BAD_DATA; - } - } while (array->CountItems() >= NUM_DUPLICATE_VALUES - && (oldValue = duplicate->RightLink()) != BPLUSTREE_NULL); - - if (array->CountItems() < NUM_DUPLICATE_VALUES) { - array->Insert(value); - } else { - // no space left - add a new duplicate node - - CachedNode cachedNewDuplicate(this); - bplustree_node *newDuplicate; - status = cachedNewDuplicate.Allocate(transaction, &newDuplicate, &offset); - if (status < B_OK) - return status; - - // link the two nodes together - duplicate->right_link = HOST_ENDIAN_TO_BFS_INT64(offset); - newDuplicate->left_link = HOST_ENDIAN_TO_BFS_INT64(duplicateOffset); - - array = newDuplicate->DuplicateArray(); - array->count = 0; - array->Insert(value); - - status = cachedNewDuplicate.WriteBack(transaction); - if (status < B_OK) - return status; - } - return cachedDuplicate.WriteBack(transaction); - } - - // Search for a free duplicate fragment or create a new one - // to insert the duplicate value into - - uint32 fragmentIndex = 0; - bplustree_node *fragment; - if (FindFreeDuplicateFragment(node, &cachedDuplicate, &offset, &fragment, &fragmentIndex) < B_OK) { - // allocate a new duplicate fragment node - if ((status = cachedDuplicate.Allocate(transaction, &fragment, &offset)) < B_OK) - return status; - - memset(fragment, 0, fNodeSize); - } - duplicate_array *array = fragment->FragmentAt(fragmentIndex); - array->Insert(oldValue); - array->Insert(value); - - if ((status = cachedDuplicate.WriteBack(transaction)) < B_OK) - return status; - - values[index] = HOST_ENDIAN_TO_BFS_INT64(bplustree_node::MakeLink( - BPLUSTREE_DUPLICATE_FRAGMENT, offset, fragmentIndex)); - - return cached->WriteBack(transaction); -} - - -void -BPlusTree::InsertKey(bplustree_node *node, uint16 index, uint8 *key, uint16 keyLength, - off_t value) -{ - // should never happen, but who knows? - if (index > node->NumKeys()) - return; - - off_t *values = node->Values(); - uint16 *keyLengths = node->KeyLengths(); - uint8 *keys = node->Keys(); - - node->all_key_count = HOST_ENDIAN_TO_BFS_INT16(node->NumKeys() + 1); - node->all_key_length = HOST_ENDIAN_TO_BFS_INT16(node->AllKeyLength() + keyLength); - - off_t *newValues = node->Values(); - uint16 *newKeyLengths = node->KeyLengths(); - - // move values and copy new value into them - memmove(newValues + index + 1, values + index, sizeof(off_t) * (node->NumKeys() - 1 - index)); - memmove(newValues, values, sizeof(off_t) * index); - - newValues[index] = HOST_ENDIAN_TO_BFS_INT64(value); - - // move and update key length index - for (uint16 i = node->NumKeys(); i-- > index + 1;) - newKeyLengths[i] = HOST_ENDIAN_TO_BFS_INT16(BFS_ENDIAN_TO_HOST_INT16(keyLengths[i - 1]) + keyLength); - memmove(newKeyLengths, keyLengths, sizeof(uint16) * index); - - int32 keyStart; - newKeyLengths[index] = HOST_ENDIAN_TO_BFS_INT16(keyLength + (keyStart = index > 0 ? BFS_ENDIAN_TO_HOST_INT16(newKeyLengths[index - 1]) : 0)); - - // move keys and copy new key into them - uint16 length = BFS_ENDIAN_TO_HOST_INT16(newKeyLengths[index]); - int32 size = node->AllKeyLength() - length; - if (size > 0) - memmove(keys + length, keys + length - keyLength, size); - - memcpy(keys + keyStart, key, keyLength); -} - - -status_t -BPlusTree::SplitNode(bplustree_node *node, off_t nodeOffset, bplustree_node *other, - off_t otherOffset, uint16 *_keyIndex, uint8 *key, uint16 *_keyLength, off_t *_value) -{ - if (*_keyIndex > node->NumKeys() + 1) - return B_BAD_VALUE; - - uint16 *inKeyLengths = node->KeyLengths(); - off_t *inKeyValues = node->Values(); - uint8 *inKeys = node->Keys(); - uint8 *outKeys = other->Keys(); - int32 keyIndex = *_keyIndex; // can become less than zero! - - // how many keys will fit in one (half) page? - // that loop will find the answer to this question and - // change the key lengths indices for their new home - - // "bytes" is the number of bytes written for the new key, - // "bytesBefore" are the bytes before that key - // "bytesAfter" are the bytes after the new key, if any - int32 bytes = 0, bytesBefore = 0, bytesAfter = 0; - - size_t size = fNodeSize >> 1; - int32 out, in; - for (in = out = 0; in < node->NumKeys() + 1;) { - if (!bytes) - bytesBefore = in > 0 ? BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in - 1]) : 0; - - if (in == keyIndex && !bytes) { - bytes = *_keyLength; - } else { - if (keyIndex < out) - bytesAfter = BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in]) - bytesBefore; - - in++; - } - out++; - - if (round_up(sizeof(bplustree_node) + bytesBefore + bytesAfter + bytes) - + out * (sizeof(uint16) + sizeof(off_t)) >= size) { - // we have found the number of keys in the new node! - break; - } - } - - // if the new key was not inserted, set the length of the keys - // that can be copied directly - if (keyIndex >= out && in > 0) - bytesBefore = BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in - 1]); - - if (bytesBefore < 0 || bytesAfter < 0) - return B_BAD_DATA; - - other->left_link = node->left_link; - other->right_link = HOST_ENDIAN_TO_BFS_INT64(nodeOffset); - other->all_key_length = HOST_ENDIAN_TO_BFS_INT16(bytes + bytesBefore + bytesAfter); - other->all_key_count = HOST_ENDIAN_TO_BFS_INT16(out); - - uint16 *outKeyLengths = other->KeyLengths(); - off_t *outKeyValues = other->Values(); - int32 keys = out > keyIndex ? keyIndex : out; - - if (bytesBefore) { - // copy the keys - memcpy(outKeys, inKeys, bytesBefore); - memcpy(outKeyLengths, inKeyLengths, keys * sizeof(uint16)); - memcpy(outKeyValues, inKeyValues, keys * sizeof(off_t)); - } - if (bytes) { - // copy the newly inserted key - memcpy(outKeys + bytesBefore, key, bytes); - outKeyLengths[keyIndex] = HOST_ENDIAN_TO_BFS_INT16(bytes + bytesBefore); - outKeyValues[keyIndex] = HOST_ENDIAN_TO_BFS_INT64(*_value); - - if (bytesAfter) { - // copy the keys after the new key - memcpy(outKeys + bytesBefore + bytes, inKeys + bytesBefore, bytesAfter); - keys = out - keyIndex - 1; - for (int32 i = 0;i < keys;i++) - outKeyLengths[keyIndex + i + 1] = HOST_ENDIAN_TO_BFS_INT16(BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[keyIndex + i]) + bytes); - memcpy(outKeyValues + keyIndex + 1, inKeyValues + keyIndex, keys * sizeof(off_t)); - } - } - - // if the new key was already inserted, we shouldn't use it again - if (in != out) - keyIndex--; - - int32 total = bytesBefore + bytesAfter; - - // these variables are for the key that will be returned - // to the parent node - uint8 *newKey = NULL; - uint16 newLength; - bool newAllocated = false; - - // If we have split an index node, we have to drop the first key - // of the next node (which can also be the new key to insert). - // The dropped key is also the one which has to be inserted in - // the parent node, so we will set the "newKey" already here. - if (node->OverflowLink() != BPLUSTREE_NULL) { - if (in == keyIndex) { - newKey = key; - newLength = *_keyLength; - - other->overflow_link = HOST_ENDIAN_TO_BFS_INT64(*_value); - keyIndex--; - } else { - // If a key is dropped (is not the new key), we have to copy - // it, because it would be lost if not. - uint8 *droppedKey = node->KeyAt(in, &newLength); - if (droppedKey + newLength + sizeof(off_t) + sizeof(uint16) > (uint8 *)node + fNodeSize - || newLength > BPLUSTREE_MAX_KEY_LENGTH) { - fStream->GetVolume()->Panic(); - RETURN_ERROR(B_BAD_DATA); - } - newKey = (uint8 *)malloc(newLength); - if (newKey == NULL) - return B_NO_MEMORY; - memcpy(newKey, droppedKey, newLength); - - other->overflow_link = inKeyValues[in]; - total = BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in++]); - } - } - - // and now the same game for the other page and the rest of the keys - // (but with memmove() instead of memcpy(), because they may overlap) - - bytesBefore = bytesAfter = bytes = 0; - out = 0; - int32 skip = in; - while (in < node->NumKeys() + 1) { - if (in == keyIndex && !bytes) { - // it's enough to set bytesBefore once here, because we do - // not need to know the exact length of all keys in this - // loop - bytesBefore = in > skip ? BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in - 1]) : 0; - bytes = *_keyLength; - } else { - if (in < node->NumKeys()) { - inKeyLengths[in] = HOST_ENDIAN_TO_BFS_INT16(BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in]) - total); - if (bytes) { - inKeyLengths[in] = HOST_ENDIAN_TO_BFS_INT16(BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in]) + bytes); - bytesAfter = BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in]) - bytesBefore - bytes; - } - } - in++; - } - - out++; - - // break out when all keys are done - if (in > node->NumKeys() && keyIndex < in) - break; - } - - // adjust the byte counts (since we were a bit lazy in the loop) - if (keyIndex >= in && keyIndex - skip < out) - bytesAfter = BFS_ENDIAN_TO_HOST_INT16(inKeyLengths[in]) - bytesBefore - total; - else if (keyIndex < skip) - bytesBefore = node->AllKeyLength() - total; - - if (bytesBefore < 0 || bytesAfter < 0) - return B_BAD_DATA; - - node->left_link = HOST_ENDIAN_TO_BFS_INT64(otherOffset); - // right link, and overflow link can stay the same - node->all_key_length = HOST_ENDIAN_TO_BFS_INT16(bytes + bytesBefore + bytesAfter); - node->all_key_count = HOST_ENDIAN_TO_BFS_INT16(out - 1); - - // array positions have changed - outKeyLengths = node->KeyLengths(); - outKeyValues = node->Values(); - - // move the keys in the old node: the order is important here, - // because we don't want to overwrite any contents - - keys = keyIndex <= skip ? out : keyIndex - skip; - keyIndex -= skip; - - if (bytesBefore) - memmove(inKeys, inKeys + total, bytesBefore); - if (bytesAfter) - memmove(inKeys + bytesBefore + bytes, inKeys + total + bytesBefore, bytesAfter); - - if (bytesBefore) - memmove(outKeyLengths, inKeyLengths + skip, keys * sizeof(uint16)); - in = out - keyIndex - 1; - if (bytesAfter) - memmove(outKeyLengths + keyIndex + 1, inKeyLengths + skip + keyIndex, in * sizeof(uint16)); - - if (bytesBefore) - memmove(outKeyValues, inKeyValues + skip, keys * sizeof(off_t)); - if (bytesAfter) - memmove(outKeyValues + keyIndex + 1, inKeyValues + skip + keyIndex, in * sizeof(off_t)); - - if (bytes) { - // finally, copy the newly inserted key (don't overwrite anything) - memcpy(inKeys + bytesBefore, key, bytes); - outKeyLengths[keyIndex] = HOST_ENDIAN_TO_BFS_INT16(bytes + bytesBefore); - outKeyValues[keyIndex] = HOST_ENDIAN_TO_BFS_INT64(*_value); - } - - // Prepare the key that will be inserted in the parent node which - // is either the dropped key or the last of the other node. - // If it's the dropped key, "newKey" was already set earlier. - - if (newKey == NULL) - newKey = other->KeyAt(other->NumKeys() - 1, &newLength); - - memcpy(key, newKey, newLength); - *_keyLength = newLength; - *_value = otherOffset; - - if (newAllocated) - free(newKey); - - return B_OK; -} - - -status_t -BPlusTree::Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value) -{ - if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH) - RETURN_ERROR(B_BAD_VALUE); - - // lock access to stream - WriteLocked locked(fStream->Lock()); - - Stack stack; - if (SeekDown(stack, key, keyLength) != B_OK) - RETURN_ERROR(B_ERROR); - - uint8 keyBuffer[BPLUSTREE_MAX_KEY_LENGTH + 1]; - - memcpy(keyBuffer, key, keyLength); - keyBuffer[keyLength] = 0; - - node_and_key nodeAndKey; - bplustree_node *node; - - CachedNode cached(this); - while (stack.Pop(&nodeAndKey) && (node = cached.SetTo(nodeAndKey.nodeOffset)) != NULL) { -#ifdef DEBUG - NodeChecker checker(node, fNodeSize); -#endif - if (node->IsLeaf()) { - // first round, check for duplicate entries - status_t status = FindKey(node, key, keyLength, &nodeAndKey.keyIndex); - - // is this a duplicate entry? - if (status == B_OK) { - if (fAllowDuplicates) - return InsertDuplicate(transaction, &cached, node, nodeAndKey.keyIndex, value); - - RETURN_ERROR(B_NAME_IN_USE); - } - } - - // is the node big enough to hold the pair? - if (int32(round_up(sizeof(bplustree_node) + node->AllKeyLength() + keyLength) - + (node->NumKeys() + 1) * (sizeof(uint16) + sizeof(off_t))) < fNodeSize) - { - InsertKey(node, nodeAndKey.keyIndex, keyBuffer, keyLength, value); - UpdateIterators(nodeAndKey.nodeOffset, BPLUSTREE_NULL, nodeAndKey.keyIndex, 0, 1); - - return cached.WriteBack(transaction); - } else { - CachedNode cachedNewRoot(this); - CachedNode cachedOther(this); - - // do we need to allocate a new root node? if so, then do - // it now - off_t newRoot = BPLUSTREE_NULL; - if (nodeAndKey.nodeOffset == fHeader->RootNode()) { - bplustree_node *root; - status_t status = cachedNewRoot.Allocate(transaction, &root, &newRoot); - if (status < B_OK) { - // The tree is most likely corrupted! - // But it's still sane at leaf level - we could set - // a flag in the header that forces the tree to be - // rebuild next time... - // But since we will have journaling, that's not a big - // problem anyway. - RETURN_ERROR(status); - } - } - - // reserve space for the other node - bplustree_node *other; - off_t otherOffset; - status_t status = cachedOther.Allocate(transaction, &other, &otherOffset); - if (status < B_OK) { - cachedNewRoot.Free(transaction, newRoot); - RETURN_ERROR(status); - } - - if (SplitNode(node, nodeAndKey.nodeOffset, other, otherOffset, - &nodeAndKey.keyIndex, keyBuffer, &keyLength, &value) < B_OK) { - // free root node & other node here - cachedNewRoot.Free(transaction, newRoot); - cachedOther.Free(transaction, otherOffset); - - RETURN_ERROR(B_ERROR); - } - - // write the updated nodes back - - if (cached.WriteBack(transaction) < B_OK - || cachedOther.WriteBack(transaction) < B_OK) - RETURN_ERROR(B_ERROR); - - UpdateIterators(nodeAndKey.nodeOffset, otherOffset, nodeAndKey.keyIndex, - node->NumKeys(), 1); - - // update the right link of the node in the left of the new node - if ((other = cachedOther.SetTo(other->LeftLink())) != NULL) { - other->right_link = HOST_ENDIAN_TO_BFS_INT64(otherOffset); - if (cachedOther.WriteBack(transaction) < B_OK) - RETURN_ERROR(B_ERROR); - } - - // create a new root if necessary - if (newRoot != BPLUSTREE_NULL) { - bplustree_node *root = cachedNewRoot.Node(); - - InsertKey(root, 0, keyBuffer, keyLength, node->LeftLink()); - root->overflow_link = HOST_ENDIAN_TO_BFS_INT64(nodeAndKey.nodeOffset); - - if (cachedNewRoot.WriteBack(transaction) < B_OK) - RETURN_ERROR(B_ERROR); - - // finally, update header to point to the new root - fHeader->root_node_pointer = HOST_ENDIAN_TO_BFS_INT64(newRoot); - fHeader->max_number_of_levels = HOST_ENDIAN_TO_BFS_INT32(fHeader->MaxNumberOfLevels() + 1); - - return fCachedHeader.WriteBack(transaction); - } - } - } - - RETURN_ERROR(B_ERROR); -} - - -status_t -BPlusTree::RemoveDuplicate(Transaction *transaction, bplustree_node *node, CachedNode *cached, - uint16 index, off_t value) -{ - CachedNode cachedDuplicate(this); - off_t *values = node->Values(); - off_t oldValue = BFS_ENDIAN_TO_HOST_INT64(values[index]); - status_t status; - - off_t duplicateOffset = bplustree_node::FragmentOffset(oldValue); - bplustree_node *duplicate = cachedDuplicate.SetTo(duplicateOffset, false); - if (duplicate == NULL) - return B_IO_ERROR; - - // if it's a duplicate fragment, remove the entry from there - if (bplustree_node::LinkType(oldValue) == BPLUSTREE_DUPLICATE_FRAGMENT) { - duplicate_array *array = duplicate->FragmentAt(bplustree_node::FragmentIndex(oldValue)); - - if (array->CountItems() > NUM_FRAGMENT_VALUES - || array->CountItems() < 1) { - FATAL(("removeDuplicate: Invalid array[%ld] size in fragment %Ld == %Ld!\n", - bplustree_node::FragmentIndex(oldValue), duplicateOffset, array->CountItems())); - return B_BAD_DATA; - } - if (!array->Remove(value)) { - FATAL(("Oh no, value %Ld not found in fragments of node %Ld...\n", - value, duplicateOffset)); - return B_ENTRY_NOT_FOUND; - } - - // remove the array from the fragment node if it is empty - if (array->CountItems() == 1) { - // set the link to the remaining value - values[index] = array->values[0]; - - // Remove the whole fragment node, if this was the only array, - // otherwise free the array and write the changes back - if (duplicate->FragmentsUsed(fNodeSize) == 1) - status = cachedDuplicate.Free(transaction, duplicateOffset); - else { - array->count = 0; - status = cachedDuplicate.WriteBack(transaction); - } - if (status < B_OK) - return status; - - return cached->WriteBack(transaction); - } - return cachedDuplicate.WriteBack(transaction); - } - - // Remove value from a duplicate node! - - duplicate_array *array = NULL; - - if (duplicate->LeftLink() != BPLUSTREE_NULL) { - FATAL(("invalid duplicate node: first left link points to %Ld!\n", duplicate->LeftLink())); - return B_BAD_DATA; - } - - // Search the duplicate nodes until the entry could be found (and removed) - while (duplicate != NULL) { - array = duplicate->DuplicateArray(); - if (array->CountItems() > NUM_DUPLICATE_VALUES - || array->CountItems() < 0) { - FATAL(("removeDuplicate: Invalid array size in duplicate %Ld == %Ld!\n", - duplicateOffset, array->CountItems())); - return B_BAD_DATA; - } - - if (array->Remove(value)) - break; - - if ((duplicateOffset = duplicate->RightLink()) == BPLUSTREE_NULL) - RETURN_ERROR(B_ENTRY_NOT_FOUND); - - duplicate = cachedDuplicate.SetTo(duplicateOffset, false); - } - if (duplicate == NULL) - RETURN_ERROR(B_IO_ERROR); - - while (true) { - off_t left = duplicate->LeftLink(); - off_t right = duplicate->RightLink(); - bool isLast = left == BPLUSTREE_NULL && right == BPLUSTREE_NULL; - - if (isLast && array->CountItems() == 1 || array->CountItems() == 0) { - // Free empty duplicate page, link their siblings together, and - // update the duplicate link if needed (which should not be, if - // we are the only one working on that tree...) - - if (duplicateOffset == bplustree_node::FragmentOffset(oldValue) - || array->CountItems() == 1) { - if (array->CountItems() == 1 && isLast) - values[index] = array->values[0]; - else if (isLast) { - FATAL(("removed last value from duplicate!\n")); - } else { - values[index] = HOST_ENDIAN_TO_BFS_INT64(bplustree_node::MakeLink( - BPLUSTREE_DUPLICATE_NODE, right)); - } - - if ((status = cached->WriteBack(transaction)) < B_OK) - return status; - } - - if ((status = cachedDuplicate.Free(transaction, duplicateOffset)) < B_OK) - return status; - - if (left != BPLUSTREE_NULL - && (duplicate = cachedDuplicate.SetTo(left, false)) != NULL) { - duplicate->right_link = HOST_ENDIAN_TO_BFS_INT64(right); - - // If the next node is the last node, we need to free that node - // and convert the duplicate entry back into a normal entry - array = duplicate->DuplicateArray(); - if (right == BPLUSTREE_NULL && duplicate->LeftLink() == BPLUSTREE_NULL - && duplicate->DuplicateArray()->count <= NUM_FRAGMENT_VALUES) { - duplicateOffset = left; - continue; - } - - status = cachedDuplicate.WriteBack(transaction); - if (status < B_OK) - return status; - } - if (right != BPLUSTREE_NULL - && (duplicate = cachedDuplicate.SetTo(right, false)) != NULL) { - duplicate->left_link = HOST_ENDIAN_TO_BFS_INT64(left); - - // Again, we may need to turn the duplicate entry back into a normal entry - array = duplicate->DuplicateArray(); - if (left == BPLUSTREE_NULL && duplicate->RightLink() == BPLUSTREE_NULL - && duplicate->DuplicateArray()->CountItems() <= NUM_FRAGMENT_VALUES) { - duplicateOffset = right; - continue; - } - - return cachedDuplicate.WriteBack(transaction); - } - return status; - } else if (isLast && array->CountItems() <= NUM_FRAGMENT_VALUES) { - // If the number of entries fits in a duplicate fragment, then - // either find a free fragment node, or convert this node to a - // fragment node. - CachedNode cachedOther(this); - - bplustree_node *fragment = NULL; - uint32 fragmentIndex = 0; - off_t offset; - if (FindFreeDuplicateFragment(node, &cachedOther, &offset, - &fragment, &fragmentIndex) < B_OK) { - // convert node - memmove(duplicate, array, (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); - memset((off_t *)duplicate + NUM_FRAGMENT_VALUES + 1, 0, - fNodeSize - (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); - } else { - // move to other node - duplicate_array *target = fragment->FragmentAt(fragmentIndex); - memcpy(target, array, (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); - - cachedDuplicate.Free(transaction, duplicateOffset); - duplicateOffset = offset; - } - values[index] = HOST_ENDIAN_TO_BFS_INT64(bplustree_node::MakeLink( - BPLUSTREE_DUPLICATE_FRAGMENT, duplicateOffset, fragmentIndex)); - - if ((status = cached->WriteBack(transaction)) < B_OK) - return status; - - if (fragment != NULL) - return cachedOther.WriteBack(transaction); - } - return cachedDuplicate.WriteBack(transaction); - } -} - - -/** Removes the key with the given index from the specified node. - * Since it has to get the key from the node anyway (to obtain it's - * pointer), it's not needed to pass the key & its length, although - * the calling method (BPlusTree::Remove()) have this data. - */ - -void -BPlusTree::RemoveKey(bplustree_node *node, uint16 index) -{ - // should never happen, but who knows? - if (index > node->NumKeys() && node->NumKeys() > 0) { - FATAL(("Asked me to remove key outer limits: %u\n", index)); - return; - } - - off_t *values = node->Values(); - - // if we would have to drop the overflow link, drop - // the last key instead and update the overflow link - // to the value of that one - if (!node->IsLeaf() && index == node->NumKeys()) - node->overflow_link = values[--index]; - - uint16 length; - uint8 *key = node->KeyAt(index, &length); - if (key + length + sizeof(off_t) + sizeof(uint16) > (uint8 *)node + fNodeSize - || length > BPLUSTREE_MAX_KEY_LENGTH) { - FATAL(("Key length to long: %s, %u (inode at %ld,%u)\n", key, length, - fStream->BlockRun().allocation_group, fStream->BlockRun().start)); - fStream->GetVolume()->Panic(); - return; - } - - uint16 *keyLengths = node->KeyLengths(); - uint8 *keys = node->Keys(); - - node->all_key_count = HOST_ENDIAN_TO_BFS_INT16(node->NumKeys() - 1); - node->all_key_length = HOST_ENDIAN_TO_BFS_INT64(node->AllKeyLength() - length); - - off_t *newValues = node->Values(); - uint16 *newKeyLengths = node->KeyLengths(); - - // move key data - memmove(key, key + length, node->AllKeyLength() - (key - keys)); - - // move and update key lengths - if (index > 0 && newKeyLengths != keyLengths) - memmove(newKeyLengths, keyLengths, index * sizeof(uint16)); - for (uint16 i = index; i < node->NumKeys(); i++) { - newKeyLengths[i] = HOST_ENDIAN_TO_BFS_INT16( - BFS_ENDIAN_TO_HOST_INT16(keyLengths[i + 1]) - length); - } - - // move values - if (index > 0) - memmove(newValues, values, index * sizeof(off_t)); - if (node->NumKeys() > index) - memmove(newValues + index, values + index + 1, (node->NumKeys() - index) * sizeof(off_t)); -} - - -/** Removes the specified key from the tree. The "value" parameter is only used - * for trees which allow duplicates, so you may safely ignore it. - * It's not an optional parameter, so at least you have to think about it. - */ - -status_t -BPlusTree::Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value) -{ - if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH) - RETURN_ERROR(B_BAD_VALUE); - - // lock access to stream - WriteLocked locked(fStream->Lock()); - - Stack stack; - if (SeekDown(stack, key, keyLength) != B_OK) - RETURN_ERROR(B_ERROR); - - node_and_key nodeAndKey; - bplustree_node *node; - - CachedNode cached(this); - while (stack.Pop(&nodeAndKey) && (node = cached.SetTo(nodeAndKey.nodeOffset)) != NULL) { -#ifdef DEBUG - NodeChecker checker(node, fNodeSize); -#endif - if (node->IsLeaf()) { - // first round, check for duplicate entries - status_t status = FindKey(node, key, keyLength, &nodeAndKey.keyIndex); - if (status < B_OK) - RETURN_ERROR(status); - - // If we will remove the last key, the iterator will be set - // to the next node after the current - if there aren't any - // more nodes, we need a way to prevent the TreeIterators to - // touch the old node again, we use BPLUSTREE_FREE for this - off_t next = node->RightLink() == BPLUSTREE_NULL ? BPLUSTREE_FREE : node->RightLink(); - UpdateIterators(nodeAndKey.nodeOffset, node->NumKeys() == 1 ? - next : BPLUSTREE_NULL, nodeAndKey.keyIndex, 0 , -1); - - // is this a duplicate entry? - if (bplustree_node::IsDuplicate(BFS_ENDIAN_TO_HOST_INT64( - node->Values()[nodeAndKey.keyIndex]))) { - if (fAllowDuplicates) - return RemoveDuplicate(transaction, node, &cached, nodeAndKey.keyIndex, value); - else - RETURN_ERROR(B_NAME_IN_USE); - } - } - - // if it's an empty root node, we have to convert it - // to a leaf node by dropping the overflow link, or, - // if it's already a leaf node, just empty it - if (nodeAndKey.nodeOffset == fHeader->RootNode() - && (node->NumKeys() == 0 || node->NumKeys() == 1 && node->IsLeaf())) { - node->overflow_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL); - node->all_key_count = 0; - node->all_key_length = 0; - - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - - // if we've made a leaf node out of the root node, we need - // to reset the maximum number of levels in the header - if (fHeader->MaxNumberOfLevels() != 1) { - fHeader->max_number_of_levels = HOST_ENDIAN_TO_BFS_INT32(1); - return fCachedHeader.WriteBack(transaction); - } - return B_OK; - } - - // if there is only one key left, we don't have to remove - // it, we can just dump the node (index nodes still have - // the overflow link, so we have to drop the last key) - if (node->NumKeys() > 1 - || !node->IsLeaf() && node->NumKeys() == 1) { - RemoveKey(node, nodeAndKey.keyIndex); - return cached.WriteBack(transaction); - } - - // when we are here, we can just free the node, but - // we have to update the right/left link of the - // siblings first - CachedNode otherCached(this); - bplustree_node *other = otherCached.SetTo(node->LeftLink()); - if (other != NULL) { - other->right_link = node->right_link; - if (otherCached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - } - - if ((other = otherCached.SetTo(node->RightLink())) != NULL) { - other->left_link = node->left_link; - if (otherCached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - } - - cached.Free(transaction, nodeAndKey.nodeOffset); - } - RETURN_ERROR(B_ERROR); -} - - -/** Replaces the value for the key in the tree. - * Returns B_OK if the key could be found and its value replaced, - * B_ENTRY_NOT_FOUND if the key couldn't be found, and other errors - * to indicate that something went terribly wrong. - * Note that this doesn't work with duplicates - it will just - * return B_BAD_TYPE if you call this function on a tree where - * duplicates are allowed. - */ - -status_t -BPlusTree::Replace(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value) -{ - if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH - || key == NULL) - RETURN_ERROR(B_BAD_VALUE); - - if (fAllowDuplicates) - RETURN_ERROR(B_BAD_TYPE); - - // lock access to stream (a read lock is okay for this purpose) - ReadLocked locked(fStream->Lock()); - - off_t nodeOffset = fHeader->RootNode(); - CachedNode cached(this); - bplustree_node *node; - - while ((node = cached.SetTo(nodeOffset)) != NULL) { - uint16 keyIndex = 0; - off_t nextOffset; - status_t status = FindKey(node, key, keyLength, &keyIndex, &nextOffset); - - if (node->OverflowLink() == BPLUSTREE_NULL) { - if (status == B_OK) { - node->Values()[keyIndex] = HOST_ENDIAN_TO_BFS_INT64(value); - return cached.WriteBack(transaction); - } - - return status; - } else if (nextOffset == nodeOffset) - RETURN_ERROR(B_ERROR); - - nodeOffset = nextOffset; - } - RETURN_ERROR(B_ERROR); -} - - -/** Searches the key in the tree, and stores the offset found in - * _value, if successful. - * It's very similar to BPlusTree::SeekDown(), but doesn't fill - * a stack while it descends the tree. - * Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND - * if not. It can also return other errors to indicate that - * something went wrong. - * Note that this doesn't work with duplicates - it will just - * return B_BAD_TYPE if you call this function on a tree where - * duplicates are allowed. - */ - -status_t -BPlusTree::Find(const uint8 *key, uint16 keyLength, off_t *_value) -{ - if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH - || key == NULL) - RETURN_ERROR(B_BAD_VALUE); - - if (fAllowDuplicates) - RETURN_ERROR(B_BAD_TYPE); - - // lock access to stream - ReadLocked locked(fStream->Lock()); - - off_t nodeOffset = fHeader->RootNode(); - CachedNode cached(this); - bplustree_node *node; - -#ifdef DEBUG - int32 levels = 0; -#endif - - while ((node = cached.SetTo(nodeOffset)) != NULL) { - uint16 keyIndex = 0; - off_t nextOffset; - status_t status = FindKey(node, key, keyLength, &keyIndex, &nextOffset); - -#ifdef DEBUG - levels++; -#endif - if (node->OverflowLink() == BPLUSTREE_NULL) { - if (status == B_OK && _value != NULL) - *_value = BFS_ENDIAN_TO_HOST_INT64(node->Values()[keyIndex]); - -#ifdef DEBUG - if (levels != (int32)fHeader->MaxNumberOfLevels()) - DEBUGGER(("levels don't match")); -#endif - return status; - } else if (nextOffset == nodeOffset) - RETURN_ERROR(B_ERROR); - - nodeOffset = nextOffset; - } - FATAL(("b+tree node at %Ld could not be loaded\n", nodeOffset)); - RETURN_ERROR(B_ERROR); -} - - -// #pragma mark - - - -TreeIterator::TreeIterator(BPlusTree *tree) - : - fTree(tree), - fCurrentNodeOffset(BPLUSTREE_NULL), - fNext(NULL) -{ - tree->AddIterator(this); -} - - -TreeIterator::~TreeIterator() -{ - if (fTree) - fTree->RemoveIterator(this); -} - - -status_t -TreeIterator::Goto(int8 to) -{ - if (fTree == NULL || fTree->fHeader == NULL) - RETURN_ERROR(B_BAD_VALUE); - - // lock access to stream - ReadLocked locked(fTree->fStream->Lock()); - - off_t nodeOffset = fTree->fHeader->RootNode(); - CachedNode cached(fTree); - bplustree_node *node; - - while ((node = cached.SetTo(nodeOffset)) != NULL) { - // is the node a leaf node? - if (node->OverflowLink() == BPLUSTREE_NULL) { - fCurrentNodeOffset = nodeOffset; - fCurrentKey = to == BPLUSTREE_BEGIN ? -1 : node->NumKeys(); - fDuplicateNode = BPLUSTREE_NULL; - - return B_OK; - } - - // get the next node offset depending on the direction (and if there - // are any keys in that node at all) - off_t nextOffset; - if (to == BPLUSTREE_END || node->all_key_count == 0) - nextOffset = node->OverflowLink(); - else { - if (node->AllKeyLength() > fTree->fNodeSize - || (uint32)node->Values() > (uint32)node + fTree->fNodeSize - 8 * node->NumKeys()) - RETURN_ERROR(B_ERROR); - - nextOffset = BFS_ENDIAN_TO_HOST_INT64(node->Values()[0]); - } - if (nextOffset == nodeOffset) - break; - - nodeOffset = nextOffset; - } - FATAL(("%s fails\n", __FUNCTION__)); - - RETURN_ERROR(B_ERROR); -} - - -/** Iterates through the tree in the specified direction. - * When it iterates through duplicates, the "key" is only updated for the - * first entry - if you need to know when this happens, use the "duplicate" - * parameter which is 0 for no duplicate, 1 for the first, and 2 for all - * the other duplicates. - * That's not too nice, but saves the 256 bytes that would be needed to - * store the last key - if this will ever become an issue, it will be - * easy to change. - * The other advantage of this is, that the queries can skip all duplicates - * at once when they are not relevant to them. - */ - -status_t -TreeIterator::Traverse(int8 direction, void *key, uint16 *keyLength, uint16 maxLength, - off_t *value, uint16 *duplicate) -{ - if (fTree == NULL) - return B_INTERRUPTED; - if (fCurrentNodeOffset == BPLUSTREE_NULL - && Goto(direction == BPLUSTREE_FORWARD ? BPLUSTREE_BEGIN : BPLUSTREE_END) < B_OK) - RETURN_ERROR(B_ERROR); - - // if the tree was emptied since the last call - if (fCurrentNodeOffset == BPLUSTREE_FREE) - return B_ENTRY_NOT_FOUND; - - // lock access to stream - ReadLocked locked(fTree->fStream->Lock()); - - CachedNode cached(fTree); - bplustree_node *node; - - if (fDuplicateNode != BPLUSTREE_NULL) { - // regardless of traverse direction the duplicates are always presented in - // the same order; since they are all considered as equal, this shouldn't - // cause any problems - - if (!fIsFragment || fDuplicate < fNumDuplicates) - node = cached.SetTo(bplustree_node::FragmentOffset(fDuplicateNode), false); - else - node = NULL; - - if (node != NULL) { - if (!fIsFragment && fDuplicate >= fNumDuplicates) { - // if the node is out of duplicates, we go directly to the next one - fDuplicateNode = node->RightLink(); - if (fDuplicateNode != BPLUSTREE_NULL - && (node = cached.SetTo(fDuplicateNode, false)) != NULL) { - fNumDuplicates = node->CountDuplicates(fDuplicateNode, false); - fDuplicate = 0; - } - } - if (fDuplicate < fNumDuplicates) { - *value = node->DuplicateAt(fDuplicateNode, fIsFragment, fDuplicate++); - if (duplicate) - *duplicate = 2; - return B_OK; - } - } - fDuplicateNode = BPLUSTREE_NULL; - } - - off_t savedNodeOffset = fCurrentNodeOffset; - int32 savedKey = fCurrentKey; - - if ((node = cached.SetTo(fCurrentNodeOffset)) == NULL) - RETURN_ERROR(B_ERROR); - - if (duplicate) - *duplicate = 0; - - fCurrentKey += direction; - - // is the current key in the current node? - while ((direction == BPLUSTREE_FORWARD && fCurrentKey >= node->NumKeys()) - || (direction == BPLUSTREE_BACKWARD && fCurrentKey < 0)) { - fCurrentNodeOffset = direction == BPLUSTREE_FORWARD ? node->RightLink() : node->LeftLink(); - - // are there any more nodes? - if (fCurrentNodeOffset != BPLUSTREE_NULL) { - node = cached.SetTo(fCurrentNodeOffset); - if (!node) - RETURN_ERROR(B_ERROR); - - // reset current key - fCurrentKey = direction == BPLUSTREE_FORWARD ? 0 : node->NumKeys(); - } else { - // there are no nodes left, so turn back to the last key - fCurrentNodeOffset = savedNodeOffset; - fCurrentKey = savedKey; - - return B_ENTRY_NOT_FOUND; - } - } - - if (node->all_key_count == 0) - RETURN_ERROR(B_ERROR); // B_ENTRY_NOT_FOUND ? - - uint16 length; - uint8 *keyStart = node->KeyAt(fCurrentKey, &length); - if (keyStart + length + sizeof(off_t) + sizeof(uint16) > (uint8 *)node + fTree->fNodeSize - || length > BPLUSTREE_MAX_KEY_LENGTH) { - fTree->fStream->GetVolume()->Panic(); - RETURN_ERROR(B_BAD_DATA); - } - - length = min_c(length, maxLength); - memcpy(key, keyStart, length); - - if (fTree->fHeader->data_type == BPLUSTREE_STRING_TYPE) { - // terminate string type - if (length == maxLength) - length--; - ((char *)key)[length] = '\0'; - } - *keyLength = length; - - off_t offset = BFS_ENDIAN_TO_HOST_INT64(node->Values()[fCurrentKey]); - - // duplicate fragments? - uint8 type = bplustree_node::LinkType(offset); - if (type == BPLUSTREE_DUPLICATE_FRAGMENT || type == BPLUSTREE_DUPLICATE_NODE) { - fDuplicateNode = offset; - - node = cached.SetTo(bplustree_node::FragmentOffset(fDuplicateNode), false); - if (node == NULL) - RETURN_ERROR(B_ERROR); - - fIsFragment = type == BPLUSTREE_DUPLICATE_FRAGMENT; - - fNumDuplicates = node->CountDuplicates(offset, fIsFragment); - if (fNumDuplicates) { - offset = node->DuplicateAt(offset, fIsFragment, 0); - fDuplicate = 1; - if (duplicate) - *duplicate = 1; - } else { - // shouldn't happen, but we're dealing here with potentially corrupt disks... - fDuplicateNode = BPLUSTREE_NULL; - offset = 0; - } - } - *value = offset; - - return B_OK; -} - - -/** This is more or less a copy of BPlusTree::Find() - but it just - * sets the current position in the iterator, regardless of if the - * key could be found or not. - */ - -status_t -TreeIterator::Find(const uint8 *key, uint16 keyLength) -{ - if (fTree == NULL) - return B_INTERRUPTED; - if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH - || key == NULL) - RETURN_ERROR(B_BAD_VALUE); - - // lock access to stream - ReadLocked locked(fTree->fStream->Lock()); - - off_t nodeOffset = fTree->fHeader->RootNode(); - - CachedNode cached(fTree); - bplustree_node *node; - while ((node = cached.SetTo(nodeOffset)) != NULL) { - uint16 keyIndex = 0; - off_t nextOffset; - status_t status = fTree->FindKey(node, key, keyLength, &keyIndex, &nextOffset); - - if (node->OverflowLink() == BPLUSTREE_NULL) { - fCurrentNodeOffset = nodeOffset; - fCurrentKey = keyIndex - 1; - fDuplicateNode = BPLUSTREE_NULL; - - return status; - } else if (nextOffset == nodeOffset) - RETURN_ERROR(B_ERROR); - - nodeOffset = nextOffset; - } - RETURN_ERROR(B_ERROR); -} - - -void -TreeIterator::SkipDuplicates() -{ - fDuplicateNode = BPLUSTREE_NULL; -} - - -void -TreeIterator::Update(off_t offset, off_t nextOffset, uint16 keyIndex, uint16 splitAt, int8 change) -{ - if (offset != fCurrentNodeOffset) - return; - - if (nextOffset != BPLUSTREE_NULL) { - fCurrentNodeOffset = nextOffset; - if (splitAt <= fCurrentKey) { - fCurrentKey -= splitAt; - keyIndex -= splitAt; - } - } - - // Adjust fCurrentKey to point to the same key as before. - // Note, that if a key is inserted at the current position - // it won't be included in this tree transition. - if (keyIndex <= fCurrentKey) - fCurrentKey += change; - - // ToDo: duplicate handling! -} - - -void -TreeIterator::Stop() -{ - fTree = NULL; -} - - -#ifdef DEBUG -void -TreeIterator::Dump() -{ - __out("TreeIterator at %p:\n", this); - __out("\tfTree = %p\n", fTree); - __out("\tfCurrentNodeOffset = %Ld\n", fCurrentNodeOffset); - __out("\tfCurrentKey = %ld\n", fCurrentKey); - __out("\tfDuplicateNode = %Ld (%Ld, 0x%Lx)\n", bplustree_node::FragmentOffset(fDuplicateNode), fDuplicateNode, fDuplicateNode); - __out("\tfDuplicate = %u\n", fDuplicate); - __out("\tfNumDuplicates = %u\n", fNumDuplicates); - __out("\tfIsFragment = %s\n", fIsFragment ? "true" : "false"); -} -#endif - - -// #pragma mark - - - -void -bplustree_node::Initialize() -{ - left_link = right_link = overflow_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL); - all_key_count = 0; - all_key_length = 0; -} - - -uint8 * -bplustree_node::KeyAt(int32 index, uint16 *keyLength) const -{ - if (index < 0 || index > NumKeys()) - return NULL; - - uint8 *keyStart = Keys(); - uint16 *keyLengths = KeyLengths(); - - *keyLength = BFS_ENDIAN_TO_HOST_INT16(keyLengths[index]) - - (index != 0 ? BFS_ENDIAN_TO_HOST_INT16(keyLengths[index - 1]) : 0); - if (index > 0) - keyStart += BFS_ENDIAN_TO_HOST_INT16(keyLengths[index - 1]); - - return keyStart; -} - - -uint8 -bplustree_node::CountDuplicates(off_t offset, bool isFragment) const -{ - // the duplicate fragment handling is currently hard-coded to a node size - // of 1024 bytes - with future versions of BFS, this may be a problem - - if (isFragment) { - uint32 fragment = (NUM_FRAGMENT_VALUES + 1) * ((uint64)offset & 0x3ff); - - return ((off_t *)this)[fragment]; - } - return OverflowLink(); -} - - -off_t -bplustree_node::DuplicateAt(off_t offset, bool isFragment, int8 index) const -{ - uint32 start; - if (isFragment) - start = 8 * ((uint64)offset & 0x3ff); - else - start = 2; - - return ((off_t *)this)[start + 1 + index]; -} - - -/** Although the name suggests it, this function doesn't return the real - * used fragment count; at least, it can only count to two: it returns - * 0, if there is no fragment used, 1 if there is only one fragment - * used, and 2 if there are at least 2 fragments used. - */ - -int32 -bplustree_node::FragmentsUsed(uint32 nodeSize) -{ - uint32 used = 0; - for (uint32 i = 0; i < nodeSize / ((NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); i++) { - duplicate_array *array = FragmentAt(i); - if (array->CountItems() > 0 && ++used > 1) - return used; - } - return used; -} - - -#ifdef DEBUG -void -bplustree_node::CheckIntegrity(uint32 nodeSize) -{ - if (NumKeys() > nodeSize || AllKeyLength() > nodeSize) - DEBUGGER(("invalid node: key/length count")); - - for (int32 i = 0; i < NumKeys(); i++) { - uint16 length; - uint8 *key = KeyAt(i, &length); - if (key + length + sizeof(off_t) + sizeof(uint16) > (uint8 *)this + nodeSize - || length > BPLUSTREE_MAX_KEY_LENGTH) - DEBUGGER(("invalid node: keys corrupted")); - } -} -#endif - - -// #pragma mark - - - -int32 -compareKeys(type_code type, const void *key1, int keyLength1, const void *key2, int keyLength2) -{ - // if one of the keys is NULL, bail out gracefully - if (key1 == NULL || key2 == NULL) { -//#if USER -// // that's here to see if it's reasonable to handle this case nicely at all -// DEBUGGER(("compareKeys() got NULL key!")); -//#endif - // even if it's most probably a bug in the calling code, we try to - // give a meaningful result - if (key1 == NULL && key2 != NULL) - return 1; - else if (key2 == NULL && key1 != NULL) - return -1; - - return 0; - } - - switch (type) { - case B_STRING_TYPE: - { - int len = min_c(keyLength1, keyLength2); - int result = strncmp((const char *)key1, (const char *)key2, len); - - if (result == 0 - && !(((const char *)key1)[len] == '\0' && ((const char *)key2)[len] == '\0')) - result = keyLength1 - keyLength2; - - return result; - } - - case B_SSIZE_T_TYPE: - case B_INT32_TYPE: - return *(int32 *)key1 - *(int32 *)key2; - - case B_SIZE_T_TYPE: - case B_UINT32_TYPE: - if (*(uint32 *)key1 == *(uint32 *)key2) - return 0; - else if (*(uint32 *)key1 > *(uint32 *)key2) - return 1; - - return -1; - - case B_OFF_T_TYPE: - case B_INT64_TYPE: - if (*(int64 *)key1 == *(int64 *)key2) - return 0; - else if (*(int64 *)key1 > *(int64 *)key2) - return 1; - - return -1; - - case B_UINT64_TYPE: - if (*(uint64 *)key1 == *(uint64 *)key2) - return 0; - else if (*(uint64 *)key1 > *(uint64 *)key2) - return 1; - - return -1; - - case B_FLOAT_TYPE: - { - float result = *(float *)key1 - *(float *)key2; - if (result == 0.0f) - return 0; - - return (result < 0.0f) ? -1 : 1; - } - - case B_DOUBLE_TYPE: - { - double result = *(double *)key1 - *(double *)key2; - if (result == 0.0) - return 0; - - return (result < 0.0) ? -1 : 1; - } - } - - // if the type is unknown, the entries don't match... - return -1; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/BPlusTree.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/BPlusTree.h deleted file mode 100644 index 8663801e68..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/BPlusTree.h +++ /dev/null @@ -1,491 +0,0 @@ -#ifndef B_PLUS_TREE_H -#define B_PLUS_TREE_H -/* BPlusTree - BFS B+Tree implementation -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** Roughly based on 'btlib' written by Marcus J. Ranum -** -** Copyright (c) 2001-2004 pinc Software. All Rights Reserved. -** This file may be used under the terms of the MIT License. -*/ - - -#include "bfs.h" -#include "Journal.h" -#include "Chain.h" - -#include - - -//****************** on-disk structures ******************** - -#define BPLUSTREE_NULL -1LL -#define BPLUSTREE_FREE -2LL - -struct bplustree_header { - uint32 magic; - uint32 node_size; - uint32 max_number_of_levels; - uint32 data_type; - off_t root_node_pointer; - off_t free_node_pointer; - off_t maximum_size; - - uint32 Magic() const { return BFS_ENDIAN_TO_HOST_INT32(magic); } - uint32 NodeSize() const { return BFS_ENDIAN_TO_HOST_INT32(node_size); } - uint32 DataType() const { return BFS_ENDIAN_TO_HOST_INT32(data_type); } - off_t RootNode() const { return BFS_ENDIAN_TO_HOST_INT64(root_node_pointer); } - off_t FreeNode() const { return BFS_ENDIAN_TO_HOST_INT64(free_node_pointer); } - off_t MaximumSize() const { return BFS_ENDIAN_TO_HOST_INT64(maximum_size); } - uint32 MaxNumberOfLevels() const { return BFS_ENDIAN_TO_HOST_INT32(max_number_of_levels); } - - inline bool IsValidLink(off_t link); -} _PACKED; - -#define BPLUSTREE_MAGIC 0x69f6c2e8 -#define BPLUSTREE_NODE_SIZE 1024 -#define BPLUSTREE_MAX_KEY_LENGTH 256 -#define BPLUSTREE_MIN_KEY_LENGTH 1 - -enum bplustree_types { - BPLUSTREE_STRING_TYPE = 0, - BPLUSTREE_INT32_TYPE = 1, - BPLUSTREE_UINT32_TYPE = 2, - BPLUSTREE_INT64_TYPE = 3, - BPLUSTREE_UINT64_TYPE = 4, - BPLUSTREE_FLOAT_TYPE = 5, - BPLUSTREE_DOUBLE_TYPE = 6 -}; - -struct sorted_array; -typedef sorted_array duplicate_array; - -struct bplustree_node { - off_t left_link; - off_t right_link; - off_t overflow_link; - uint16 all_key_count; - uint16 all_key_length; - - off_t LeftLink() const { return BFS_ENDIAN_TO_HOST_INT64(left_link); } - off_t RightLink() const { return BFS_ENDIAN_TO_HOST_INT64(right_link); } - off_t OverflowLink() const { return BFS_ENDIAN_TO_HOST_INT64(overflow_link); } - uint16 NumKeys() const { return BFS_ENDIAN_TO_HOST_INT16(all_key_count); } - uint16 AllKeyLength() const { return BFS_ENDIAN_TO_HOST_INT16(all_key_length); } - - inline uint16 *KeyLengths() const; - inline off_t *Values() const; - inline uint8 *Keys() const; - inline int32 Used() const; - uint8 *KeyAt(int32 index, uint16 *keyLength) const; - - inline bool IsLeaf() const; - - void Initialize(); - uint8 CountDuplicates(off_t offset, bool isFragment) const; - off_t DuplicateAt(off_t offset, bool isFragment, int8 index) const; - int32 FragmentsUsed(uint32 nodeSize); - inline duplicate_array *FragmentAt(int8 index); - inline duplicate_array *DuplicateArray(); - - static inline uint8 LinkType(off_t link); - static inline off_t MakeLink(uint8 type, off_t link, uint32 fragmentIndex = 0); - static inline bool IsDuplicate(off_t link); - static inline off_t FragmentOffset(off_t link); - static inline uint32 FragmentIndex(off_t link); - -#ifdef DEBUG - void CheckIntegrity(uint32 nodeSize); -#endif -} _PACKED; - -//#define BPLUSTREE_NODE 0 -#define BPLUSTREE_DUPLICATE_NODE 2 -#define BPLUSTREE_DUPLICATE_FRAGMENT 3 - -#define NUM_FRAGMENT_VALUES 7 -#define NUM_DUPLICATE_VALUES 125 - -//************************************** - -enum bplustree_traversing { - BPLUSTREE_FORWARD = 1, - BPLUSTREE_BACKWARD = -1, - - BPLUSTREE_BEGIN = 0, - BPLUSTREE_END = 1 -}; - - -//****************** in-memory structures ******************** - -template class Stack; -class BPlusTree; -class TreeIterator; -class CachedNode; -class Inode; - -// needed for searching (utilizing a stack) -struct node_and_key { - off_t nodeOffset; - uint16 keyIndex; -}; - - -//***** Cache handling ***** - -class CachedNode { - public: - CachedNode(BPlusTree *tree) - : - fTree(tree), - fNode(NULL), - fBlock(NULL) - { - } - - CachedNode(BPlusTree *tree, off_t offset, bool check = true) - : - fTree(tree), - fNode(NULL), - fBlock(NULL) - { - SetTo(offset, check); - } - - ~CachedNode() - { - Unset(); - } - - bplustree_node *SetTo(off_t offset, bool check = true); - bplustree_header *SetToHeader(); - void Unset(); - - status_t Free(Transaction *transaction, off_t offset); - status_t Allocate(Transaction *transaction, bplustree_node **node, off_t *offset); - status_t WriteBack(Transaction *transaction); - - bplustree_node *Node() const { return fNode; } - - protected: - bplustree_node *InternalSetTo(off_t offset); - - BPlusTree *fTree; - bplustree_node *fNode; - uint8 *fBlock; - off_t fBlockNumber; -}; - - -//******** B+tree class ********* - -class BPlusTree { - public: - BPlusTree(Transaction *transaction, Inode *stream, int32 nodeSize = BPLUSTREE_NODE_SIZE); - BPlusTree(Inode *stream); - BPlusTree(); - ~BPlusTree(); - - status_t SetTo(Transaction *transaction, Inode *stream, int32 nodeSize = BPLUSTREE_NODE_SIZE); - status_t SetTo(Inode *stream); - status_t SetStream(Inode *stream); - - status_t InitCheck(); - status_t Validate(); - - status_t Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value); - status_t Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value); - - status_t Remove(Transaction *transaction, const char *key, off_t value); - status_t Insert(Transaction *transaction, const char *key, off_t value); - status_t Insert(Transaction *transaction, int32 key, off_t value); - status_t Insert(Transaction *transaction, uint32 key, off_t value); - status_t Insert(Transaction *transaction, int64 key, off_t value); - status_t Insert(Transaction *transaction, uint64 key, off_t value); - status_t Insert(Transaction *transaction, float key, off_t value); - status_t Insert(Transaction *transaction, double key, off_t value); - - status_t Replace(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value); - status_t Find(const uint8 *key, uint16 keyLength, off_t *value); - - static int32 TypeCodeToKeyType(type_code code); - static int32 ModeToKeyType(mode_t mode); - - private: - BPlusTree(const BPlusTree &); - BPlusTree &operator=(const BPlusTree &); - // no implementation - - int32 CompareKeys(const void *key1, int keylength1, const void *key2, int keylength2); - status_t FindKey(bplustree_node *node, const uint8 *key, uint16 keyLength, - uint16 *index = NULL, off_t *next = NULL); - status_t SeekDown(Stack &stack, const uint8 *key, uint16 keyLength); - - status_t FindFreeDuplicateFragment(bplustree_node *node, CachedNode *cached, - off_t *_offset, bplustree_node **_fragment, uint32 *_index); - status_t InsertDuplicate(Transaction *transaction, CachedNode *cached, - bplustree_node *node, uint16 index, off_t value); - void InsertKey(bplustree_node *node, uint16 index, uint8 *key, uint16 keyLength, - off_t value); - status_t SplitNode(bplustree_node *node, off_t nodeOffset, bplustree_node *other, - off_t otherOffset, uint16 *_keyIndex, uint8 *key, uint16 *_keyLength, - off_t *_value); - - status_t RemoveDuplicate(Transaction *transaction, bplustree_node *node, - CachedNode *cached, uint16 keyIndex, off_t value); - void RemoveKey(bplustree_node *node, uint16 index); - - void UpdateIterators(off_t offset, off_t nextOffset, uint16 keyIndex, - uint16 splitAt, int8 change); - void AddIterator(TreeIterator *iterator); - void RemoveIterator(TreeIterator *iterator); - - private: - friend struct TreeIterator; - friend class CachedNode; - - Inode *fStream; - bplustree_header *fHeader; - CachedNode fCachedHeader; - int32 fNodeSize; - bool fAllowDuplicates; - status_t fStatus; - SimpleLock fIteratorLock; - Chain fIterators; -}; - - -//***** helper classes/functions ***** - -extern int32 compareKeys(type_code type, const void *key1, int keyLength1, - const void *key2, int keyLength2); - -class TreeIterator { - public: - TreeIterator(BPlusTree *tree); - ~TreeIterator(); - - status_t Goto(int8 to); - status_t Traverse(int8 direction, void *key, uint16 *keyLength, uint16 maxLength, - off_t *value, uint16 *duplicate = NULL); - status_t Find(const uint8 *key, uint16 keyLength); - - status_t Rewind(); - status_t GetNextEntry(void *key, uint16 *keyLength, uint16 maxLength, - off_t *value, uint16 *duplicate = NULL); - status_t GetPreviousEntry(void *key, uint16 *keyLength, uint16 maxLength, - off_t *value, uint16 *duplicate = NULL); - void SkipDuplicates(); - -#ifdef DEBUG - void Dump(); -#endif - - private: - BPlusTree *fTree; - - off_t fCurrentNodeOffset; // traverse position - int32 fCurrentKey; - off_t fDuplicateNode; - uint16 fDuplicate, fNumDuplicates; - bool fIsFragment; - - private: - friend class Chain; - friend class BPlusTree; - - void Update(off_t offset, off_t nextOffset, uint16 keyIndex, uint16 splitAt, int8 change); - void Stop(); - TreeIterator *fNext; -}; - -// BPlusTree's inline functions (most of them may not be needed) - -inline status_t -BPlusTree::Remove(Transaction *transaction, const char *key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_STRING_TYPE) - return B_BAD_TYPE; - return Remove(transaction, (uint8 *)key, strlen(key), value); -} - -inline status_t -BPlusTree::Insert(Transaction *transaction, const char *key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_STRING_TYPE) - return B_BAD_TYPE; - return Insert(transaction, (uint8 *)key, strlen(key), value); -} - -inline status_t -BPlusTree::Insert(Transaction *transaction, int32 key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_INT32_TYPE) - return B_BAD_TYPE; - return Insert(transaction, (uint8 *)&key, sizeof(key), value); -} - -inline status_t -BPlusTree::Insert(Transaction *transaction, uint32 key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_UINT32_TYPE) - return B_BAD_TYPE; - return Insert(transaction, (uint8 *)&key, sizeof(key), value); -} - -inline status_t -BPlusTree::Insert(Transaction *transaction, int64 key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_INT64_TYPE) - return B_BAD_TYPE; - return Insert(transaction, (uint8 *)&key, sizeof(key), value); -} - -inline status_t -BPlusTree::Insert(Transaction *transaction, uint64 key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_UINT64_TYPE) - return B_BAD_TYPE; - return Insert(transaction, (uint8 *)&key, sizeof(key), value); -} - -inline status_t -BPlusTree::Insert(Transaction *transaction, float key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_FLOAT_TYPE) - return B_BAD_TYPE; - return Insert(transaction, (uint8 *)&key, sizeof(key), value); -} - -inline status_t -BPlusTree::Insert(Transaction *transaction, double key, off_t value) -{ - if (fHeader->data_type != BPLUSTREE_DOUBLE_TYPE) - return B_BAD_TYPE; - return Insert(transaction, (uint8 *)&key, sizeof(key), value); -} - - -/************************ TreeIterator inline functions ************************/ -// #pragma mark - - -inline status_t -TreeIterator::Rewind() -{ - return Goto(BPLUSTREE_BEGIN); -} - -inline status_t -TreeIterator::GetNextEntry(void *key, uint16 *keyLength, uint16 maxLength, - off_t *value, uint16 *duplicate) -{ - return Traverse(BPLUSTREE_FORWARD, key, keyLength, maxLength, value, duplicate); -} - -inline status_t -TreeIterator::GetPreviousEntry(void *key, uint16 *keyLength, uint16 maxLength, - off_t *value, uint16 *duplicate) -{ - return Traverse(BPLUSTREE_BACKWARD, key, keyLength, maxLength, value, duplicate); -} - -/************************ bplustree_header inline functions ************************/ -// #pragma mark - - - -inline bool -bplustree_header::IsValidLink(off_t link) -{ - return link == BPLUSTREE_NULL || (link > 0 && link <= MaximumSize() - NodeSize()); -} - - -/************************ bplustree_node inline functions ************************/ -// #pragma mark - - - -inline uint16 * -bplustree_node::KeyLengths() const -{ - return (uint16 *)(((char *)this) + round_up(sizeof(bplustree_node) + AllKeyLength())); -} - - -inline off_t * -bplustree_node::Values() const -{ - return (off_t *)((char *)KeyLengths() + NumKeys() * sizeof(uint16)); -} - - -inline uint8 * -bplustree_node::Keys() const -{ - return (uint8 *)this + sizeof(bplustree_node); -} - - -inline int32 -bplustree_node::Used() const -{ - return round_up(sizeof(bplustree_node) + AllKeyLength()) + NumKeys() * (sizeof(uint16) + sizeof(off_t)); -} - - -inline bool -bplustree_node::IsLeaf() const -{ - return OverflowLink() == BPLUSTREE_NULL; -} - - -inline duplicate_array * -bplustree_node::FragmentAt(int8 index) -{ - return (duplicate_array *)((off_t *)this + index * (NUM_FRAGMENT_VALUES + 1)); -} - - -inline duplicate_array * -bplustree_node::DuplicateArray() -{ - return (duplicate_array *)&this->overflow_link; -} - - -inline uint8 -bplustree_node::LinkType(off_t link) -{ - return *(uint64 *)&link >> 62; -} - - -inline off_t -bplustree_node::MakeLink(uint8 type, off_t link, uint32 fragmentIndex) -{ - return ((off_t)type << 62) | (link & 0x3ffffffffffffc00LL) | (fragmentIndex & 0x3ff); -} - - -inline bool -bplustree_node::IsDuplicate(off_t link) -{ - return (LinkType(link) & (BPLUSTREE_DUPLICATE_NODE | BPLUSTREE_DUPLICATE_FRAGMENT)) > 0; -} - - -inline off_t -bplustree_node::FragmentOffset(off_t link) -{ - return link & 0x3ffffffffffffc00LL; -} - - -inline uint32 -bplustree_node::FragmentIndex(off_t link) -{ - return (uint32)(link & 0x3ff); -} - -#endif /* B_PLUS_TREE_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/BlockAllocator.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/BlockAllocator.cpp deleted file mode 100644 index b53e5013b7..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/BlockAllocator.cpp +++ /dev/null @@ -1,1250 +0,0 @@ -/* BlockAllocator - block bitmap handling and allocation policies -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include "Debug.h" -#include "BlockAllocator.h" -#include "Volume.h" -#include "Inode.h" -#include "BPlusTree.h" -#include "Stack.h" -#include "bfs_control.h" - -#include -#include - -#ifdef USER -# define spawn_kernel_thread spawn_thread -#endif - - -// Things the BlockAllocator should do: - -// - find a range of blocks of a certain size nearby a specific position -// - allocating an unsharp range of blocks for pre-allocation -// - free blocks -// - know how to deal with each allocation, special handling for directories, -// files, symlinks, etc. (type sensitive allocation policies) - -// What makes the code complicated is the fact that we are not just reading -// in the whole bitmap and operate on that in memory - e.g. a 13 GB partition -// with a block size of 2048 bytes already has a 800kB bitmap, and the size -// of partitions will grow even more - so that's not an option. -// Instead we are reading in every block when it's used - since an allocation -// group can span several blocks in the block bitmap, the AllocationBlock -// class is there to make handling those easier. - -// The current implementation is only slightly optimized and could probably -// be improved a lot. Furthermore, the allocation policies used here should -// have some real world tests. - - -struct check_cookie { - check_cookie() {} - - block_run current; - Inode *parent; - mode_t parent_mode; - Stack stack; - TreeIterator *iterator; -}; - - -class AllocationBlock : public CachedBlock { - public: - AllocationBlock(Volume *volume); - - void Allocate(uint16 start, uint16 numBlocks); - void Free(uint16 start, uint16 numBlocks); - inline bool IsUsed(uint16 block); - - status_t SetTo(AllocationGroup &group, uint16 block); - - uint32 NumBlockBits() const { return fNumBits; } - uint32 &Block(int32 index) { return ((uint32 *)fBlock)[index]; } - - private: - uint32 fNumBits; -}; - - -class AllocationGroup { - public: - AllocationGroup(); - - void AddFreeRange(int32 start, int32 blocks); - bool IsFull() const { return fFreeBits == 0; } - - status_t Allocate(Transaction *transaction, uint16 start, int32 length); - status_t Free(Transaction *transaction, uint16 start, int32 length); - - uint32 fNumBits; - int32 fStart; - int32 fFirstFree, fLargest, fLargestFirst; - // ToDo: fLargest & fLargestFirst are not maintained (and therefore used) yet! - int32 fFreeBits; -}; - - -AllocationBlock::AllocationBlock(Volume *volume) - : CachedBlock(volume) -{ -} - - -status_t -AllocationBlock::SetTo(AllocationGroup &group, uint16 block) -{ - // 8 blocks per byte - fNumBits = fVolume->BlockSize() << 3; - // the last group may have less bits in the last block - if ((block + 1) * fNumBits > group.fNumBits) - fNumBits = group.fNumBits % fNumBits; - - return CachedBlock::SetTo(group.fStart + block) != NULL ? B_OK : B_ERROR; -} - - -bool -AllocationBlock::IsUsed(uint16 block) -{ - if (block > fNumBits) - return true; - // the block bitmap is accessed in 32-bit blocks - return Block(block >> 5) & HOST_ENDIAN_TO_BFS_INT32(1UL << (block % 32)); -} - - -void -AllocationBlock::Allocate(uint16 start, uint16 numBlocks) -{ - ASSERT(start < fNumBits); - ASSERT(uint32(start + numBlocks) <= fNumBits); - - if (uint32(start + numBlocks) > fNumBits) { - FATAL(("Allocation::Allocate(): tried to allocate too many blocks: %u (numBlocks = %lu)!\n", numBlocks, fNumBits)); - DIE(("Allocation::Allocate(): tried to allocate too many blocks")); - } - - int32 block = start >> 5; - - while (numBlocks > 0) { - uint32 mask = 0; - for (int32 i = start % 32; i < 32 && numBlocks; i++, numBlocks--) - mask |= 1UL << (i % 32); - -#ifdef DEBUG - // check for already set blocks - if (HOST_ENDIAN_TO_BFS_INT32(mask) & ((uint32 *)fBlock)[block]) { - FATAL(("AllocationBlock::Allocate(): some blocks are already allocated, start = %u, numBlocks = %u\n", start, numBlocks)); - DEBUGGER(("blocks already set!")); - } -#endif - Block(block++) |= HOST_ENDIAN_TO_BFS_INT32(mask); - start = 0; - } -} - - -void -AllocationBlock::Free(uint16 start, uint16 numBlocks) -{ - ASSERT(start < fNumBits); - ASSERT(uint32(start + numBlocks) <= fNumBits); - - if (uint32(start + numBlocks) > fNumBits) { - FATAL(("Allocation::Free(): tried to free too many blocks: %u (numBlocks = %lu)!\n", numBlocks, fNumBits)); - DIE(("Allocation::Free(): tried to free too many blocks")); - } - - int32 block = start >> 5; - - while (numBlocks > 0) { - uint32 mask = 0; - for (int32 i = start % 32; i < 32 && numBlocks; i++, numBlocks--) - mask |= 1UL << (i % 32); - - Block(block++) &= HOST_ENDIAN_TO_BFS_INT32(~mask); - start = 0; - } -} - - -// #pragma mark - - - -AllocationGroup::AllocationGroup() - : - fFirstFree(-1), - fLargest(-1), - fLargestFirst(-1), - fFreeBits(0) -{ -} - - -void -AllocationGroup::AddFreeRange(int32 start, int32 blocks) -{ - //D(if (blocks > 512) - // PRINT(("range of %ld blocks starting at %ld\n",blocks,start))); - - if (fFirstFree == -1) - fFirstFree = start; - - if (fLargest < blocks) { - fLargest = blocks; - fLargestFirst = start; - } - - fFreeBits += blocks; -} - - -/** Allocates the specified run in the allocation group. - * Doesn't check if the run is valid or already allocated - * partially, nor does it maintain the free ranges hints - * or the volume's used blocks count. - * It only does the low-level work of allocating some bits - * in the block bitmap. - * Assumes that the block bitmap lock is hold. - */ - -status_t -AllocationGroup::Allocate(Transaction *transaction, uint16 start, int32 length) -{ - if (start > fNumBits) - return B_ERROR; - - // Update the allocation group info - // ToDo: this info will be incorrect if something goes wrong later - // Note, the fFirstFree block doesn't have to be really free - if (start == fFirstFree) - fFirstFree = start + length; - fFreeBits -= length; - - Volume *volume = transaction->GetVolume(); - - // calculate block in the block bitmap and position within - uint32 bitsPerBlock = volume->BlockSize() << 3; - uint32 block = start / bitsPerBlock; - start = start % bitsPerBlock; - - AllocationBlock cached(volume); - - while (length > 0) { - if (cached.SetTo(*this, block) < B_OK) - RETURN_ERROR(B_IO_ERROR); - - uint32 numBlocks = length; - if (start + numBlocks > cached.NumBlockBits()) - numBlocks = cached.NumBlockBits() - start; - - cached.Allocate(start, numBlocks); - - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - - length -= numBlocks; - start = 0; - block++; - } - - return B_OK; -} - - -/** Frees the specified run in the allocation group. - * Doesn't check if the run is valid or was not completely - * allocated, nor does it maintain the free ranges hints - * or the volume's used blocks count. - * It only does the low-level work of freeing some bits - * in the block bitmap. - * Assumes that the block bitmap lock is hold. - */ - -status_t -AllocationGroup::Free(Transaction *transaction, uint16 start, int32 length) -{ - if (start > fNumBits) - return B_ERROR; - - // Update the allocation group info - // ToDo: this info will be incorrect if something goes wrong later - if (fFirstFree > start) - fFirstFree = start; - fFreeBits += length; - - Volume *volume = transaction->GetVolume(); - - // calculate block in the block bitmap and position within - uint32 bitsPerBlock = volume->BlockSize() << 3; - uint32 block = start / bitsPerBlock; - start = start % bitsPerBlock; - - AllocationBlock cached(volume); - - while (length > 0) { - if (cached.SetTo(*this, block) < B_OK) - RETURN_ERROR(B_IO_ERROR); - - uint16 freeLength = length; - if (uint32(start + length) > cached.NumBlockBits()) - freeLength = cached.NumBlockBits() - start; - - cached.Free(start, freeLength); - - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - - length -= freeLength; - start = 0; - block++; - } - return B_OK; -} - - -// #pragma mark - - - -BlockAllocator::BlockAllocator(Volume *volume) - : - fVolume(volume), - fLock("bfs allocator"), - fGroups(NULL), - fCheckBitmap(NULL) -{ -} - - -BlockAllocator::~BlockAllocator() -{ - delete[] fGroups; -} - - -status_t -BlockAllocator::Initialize(bool full) -{ - if (fLock.InitCheck() < B_OK) - return B_ERROR; - - fNumGroups = fVolume->AllocationGroups(); - fBlocksPerGroup = fVolume->SuperBlock().BlocksPerAllocationGroup(); - fGroups = new AllocationGroup[fNumGroups]; - if (fGroups == NULL) - return B_NO_MEMORY; - - if (!full) - return B_OK; - - fLock.Lock(); - // the lock will be released by the initialize() function - - thread_id id = spawn_kernel_thread((thread_func)BlockAllocator::initialize, - "bfs block allocator", B_LOW_PRIORITY, (void *)this); - if (id < B_OK) - return initialize(this); - - return resume_thread(id); -} - - -status_t -BlockAllocator::InitializeAndClearBitmap(Transaction &transaction) -{ - status_t status = Initialize(false); - if (status < B_OK) - return status; - - uint32 blocks = fBlocksPerGroup; - uint32 numBits = 8 * blocks * fVolume->BlockSize(); - - uint32 *buffer = (uint32 *)malloc(numBits >> 3); - if (buffer == NULL) - RETURN_ERROR(B_NO_MEMORY); - - memset(buffer, 0, numBits >> 3); - - off_t offset = 1; - - // initialize the AllocationGroup objects and clear the on-disk bitmap - - for (int32 i = 0; i < fNumGroups; i++) { - if (cached_write(fVolume->Device(), offset, buffer, blocks, fVolume->BlockSize()) < B_OK) - return B_ERROR; - - // the last allocation group may contain less blocks than the others - fGroups[i].fNumBits = i == fNumGroups - 1 ? fVolume->NumBlocks() - i * numBits : numBits; - fGroups[i].fStart = offset; - fGroups[i].fFirstFree = fGroups[i].fLargestFirst = 0; - fGroups[i].fFreeBits = fGroups[i].fLargest = fGroups[i].fNumBits; - - offset += blocks; - } - free(buffer); - - // reserve the boot block, the log area, and the block bitmap itself - uint32 reservedBlocks = fVolume->Log().Start() + fVolume->Log().Length(); - - if (fGroups[0].Allocate(&transaction, 0, reservedBlocks) < B_OK) { - FATAL(("could not allocate reserved space for block bitmap/log!\n")); - return B_ERROR; - } - fVolume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(reservedBlocks); - - return B_OK; -} - - -status_t -BlockAllocator::initialize(BlockAllocator *allocator) -{ - // The lock must already be held at this point - - Volume *volume = allocator->fVolume; - uint32 blocks = allocator->fBlocksPerGroup; - uint32 numBits = 8 * blocks * volume->BlockSize(); - off_t freeBlocks = 0; - - uint32 *buffer = (uint32 *)malloc(numBits >> 3); - if (buffer == NULL) { - allocator->fLock.Unlock(); - RETURN_ERROR(B_NO_MEMORY); - } - - AllocationGroup *groups = allocator->fGroups; - off_t offset = 1; - int32 num = allocator->fNumGroups; - - for (int32 i = 0;i < num;i++) { - if (cached_read(volume->Device(), offset, buffer, blocks, volume->BlockSize()) < B_OK) - break; - - // the last allocation group may contain less blocks than the others - groups[i].fNumBits = i == num - 1 ? volume->NumBlocks() - i * numBits : numBits; - groups[i].fStart = offset; - - // finds all free ranges in this allocation group - int32 start = -1, range = 0; - int32 size = groups[i].fNumBits, num = 0; - - for (int32 k = 0;k < (size >> 2);k++) { - for (int32 j = 0; j < 32 && num < size; j++, num++) { - if (buffer[k] & (1UL << j)) { - if (range > 0) { - groups[i].AddFreeRange(start, range); - range = 0; - } - } else if (range++ == 0) - start = num; - } - } - if (range) - groups[i].AddFreeRange(start, range); - - freeBlocks += groups[i].fFreeBits; - - offset += blocks; - } - free(buffer); - - // check if block bitmap and log area are reserved - uint32 reservedBlocks = volume->Log().Start() + volume->Log().Length(); - if (allocator->CheckBlockRun(block_run::Run(0, 0, reservedBlocks)) < B_OK) { - Transaction transaction(volume, 0); - if (groups[0].Allocate(&transaction, 0, reservedBlocks) < B_OK) { - FATAL(("could not allocate reserved space for block bitmap/log!\n")); - volume->Panic(); - } else { - FATAL(("space for block bitmap or log area was not reserved!\n")); - - transaction.Done(); - } - } - - off_t usedBlocks = volume->NumBlocks() - freeBlocks; - if (volume->UsedBlocks() != usedBlocks) { - // If the disk in a dirty state at mount time, it's - // normal that the values don't match - INFORM(("volume reports %Ld used blocks, correct is %Ld\n", volume->UsedBlocks(), usedBlocks)); - volume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(usedBlocks); - } - - allocator->fLock.Unlock(); - return B_OK; -} - - -status_t -BlockAllocator::AllocateBlocks(Transaction *transaction, int32 group, uint16 start, - uint16 maximum, uint16 minimum, block_run &run) -{ - if (maximum == 0) - return B_BAD_VALUE; - - FUNCTION_START(("group = %ld, start = %u, maximum = %u, minimum = %u\n", group, start, maximum, minimum)); - - AllocationBlock cached(fVolume); - Locker lock(fLock); - - // the first scan through all allocation groups will look for the - // wanted maximum of blocks, the second scan will just look to - // satisfy the minimal requirement - uint16 numBlocks = maximum; - - for (int32 i = 0; i < fNumGroups * 2; i++, group++, start = 0) { - group = group % fNumGroups; - - if (start >= fGroups[group].fNumBits || fGroups[group].IsFull()) - continue; - - if (i >= fNumGroups) { - // if the minimum is the same as the maximum, it's not necessary to - // search for in the allocation groups a second time - if (maximum == minimum) - return B_DEVICE_FULL; - - numBlocks = minimum; - } - - // The wanted maximum is smaller than the largest free block in the group - // or already smaller than the minimum - // ToDo: disabled because it's currently not maintained after the first allocation - //if (numBlocks > fGroups[group].fLargest) - // continue; - - if (start < fGroups[group].fFirstFree) - start = fGroups[group].fFirstFree; - - // there may be more than one block per allocation group - and - // we iterate through it to find a place for the allocation. - // (one allocation can't exceed one allocation group) - - uint32 block = start / (fVolume->BlockSize() << 3); - int32 range = 0, rangeStart = 0; - - for (; block < fBlocksPerGroup; block++) { - if (cached.SetTo(fGroups[group], block) < B_OK) - RETURN_ERROR(B_ERROR); - - // find a block large enough to hold the allocation - for (uint32 bit = start % cached.NumBlockBits(); bit < cached.NumBlockBits(); bit++) { - if (!cached.IsUsed(bit)) { - if (range == 0) { - // start new range - rangeStart = block * cached.NumBlockBits() + bit; - } - - // have we found a range large enough to hold numBlocks? - if (++range >= maximum) - break; - } else if (i >= fNumGroups && range >= minimum) { - // we have found a block larger than the required minimum (second pass) - break; - } else { - // end of a range - range = 0; - } - } - - // if we found a suitable block, mark the blocks as in use, and write - // the updated block bitmap back to disk - if (range >= numBlocks) { - // adjust allocation size - if (numBlocks < maximum) - numBlocks = range; - - if (fGroups[group].Allocate(transaction, rangeStart, numBlocks) < B_OK) - RETURN_ERROR(B_IO_ERROR); - - run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(group); - run.start = HOST_ENDIAN_TO_BFS_INT16(rangeStart); - run.length = HOST_ENDIAN_TO_BFS_INT16(numBlocks); - - fVolume->SuperBlock().used_blocks = - HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() + numBlocks); - // We are not writing back the disk's superblock - it's - // either done by the journaling code, or when the disk - // is unmounted. - // If the value is not correct at mount time, it will be - // fixed anyway. - - return B_OK; - } - - // start from the beginning of the next block - start = 0; - } - } - return B_DEVICE_FULL; -} - - -status_t -BlockAllocator::AllocateForInode(Transaction *transaction, const block_run *parent, - mode_t type, block_run &run) -{ - // apply some allocation policies here (AllocateBlocks() will break them - // if necessary) - we will start with those described in Dominic Giampaolo's - // "Practical File System Design", and see how good they work - - // files are going in the same allocation group as its parent, sub-directories - // will be inserted 8 allocation groups after the one of the parent - uint16 group = parent->AllocationGroup(); - if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY) - group += 8; - - return AllocateBlocks(transaction, group, 0, 1, 1, run); -} - - -status_t -BlockAllocator::Allocate(Transaction *transaction, const Inode *inode, off_t numBlocks, - block_run &run, uint16 minimum) -{ - if (numBlocks <= 0) - return B_ERROR; - - // one block_run can't hold more data than there is in one allocation group - if (numBlocks > fGroups[0].fNumBits) - numBlocks = fGroups[0].fNumBits; - - // since block_run.length is uint16, the largest number of blocks that - // can be covered by a block_run is 65535 - // ToDo: if we drop compatibility, couldn't we do this any better? - // There are basically two possibilities: - // a) since a length of zero doesn't have any sense, take that for 65536 - - // but that could cause many problems (bugs) in other areas - // b) reduce the maximum amount of blocks per block_run, so that the remaining - // number of free blocks can be used in a useful manner (like 4 blocks) - - // but that would also reduce the maximum file size - if (numBlocks > MAX_BLOCK_RUN_LENGTH) - numBlocks = MAX_BLOCK_RUN_LENGTH; - - // apply some allocation policies here (AllocateBlocks() will break them - // if necessary) - uint16 group = inode->BlockRun().AllocationGroup(); - uint16 start = 0; - - // are there already allocated blocks? (then just try to allocate near the last one) - if (inode->Size() > 0) { - data_stream *data = &inode->Node()->data; - // ToDo: we currently don't care for when the data stream - // is already grown into the indirect ranges - if (data->max_double_indirect_range == 0 - && data->max_indirect_range == 0) { - // Since size > 0, there must be a valid block run in this stream - int32 last = 0; - for (; last < NUM_DIRECT_BLOCKS - 1; last++) - if (data->direct[last + 1].IsZero()) - break; - - group = data->direct[last].AllocationGroup(); - start = data->direct[last].Start() + data->direct[last].Length(); - } - } else if (inode->IsContainer() || inode->IsSymLink()) { - // directory and symbolic link data will go in the same allocation - // group as the inode is in but after the inode data - start = inode->BlockRun().Start(); - } else { - // file data will start in the next allocation group - group = inode->BlockRun().AllocationGroup() + 1; - } - - return AllocateBlocks(transaction, group, start, numBlocks, minimum, run); -} - - -status_t -BlockAllocator::Free(Transaction *transaction, block_run run) -{ - Locker lock(fLock); - - int32 group = run.AllocationGroup(); - uint16 start = run.Start(); - uint16 length = run.Length(); - - FUNCTION_START(("group = %ld, start = %u, length = %u\n", group, start, length)); - - // doesn't use Volume::IsValidBlockRun() here because it can check better - // against the group size (the last group may have a different length) - if (group < 0 || group >= fNumGroups - || start > fGroups[group].fNumBits - || uint32(start + length) > fGroups[group].fNumBits - || length == 0) { - FATAL(("tried to free an invalid block_run (%ld, %u, %u)\n", group, start, length)); - DEBUGGER(("tried to free invalid block_run")); - return B_BAD_VALUE; - } - // check if someone tries to free reserved areas at the beginning of the drive - if (group == 0 && start < uint32(fVolume->Log().Start() + fVolume->Log().Length())) { - FATAL(("tried to free a reserved block_run (%ld, %u, %u)\n", group, start, length)); - DEBUGGER(("tried to free reserved block")); - return B_BAD_VALUE; - } -#ifdef DEBUG - if (CheckBlockRun(run) < B_OK) - return B_BAD_DATA; -#endif - - if (fGroups[group].Free(transaction, start, length) < B_OK) - RETURN_ERROR(B_IO_ERROR); - -#ifdef DEBUG - if (CheckBlockRun(run, NULL, NULL, false) < B_OK) - DEBUGGER(("CheckBlockRun() reports allocated blocks (which were just freed)\n")); -#endif - - fVolume->SuperBlock().used_blocks = - HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() - run.Length()); - return B_OK; -} - - -size_t -BlockAllocator::BitmapSize() const -{ - return fVolume->BlockSize() * fNumGroups * fBlocksPerGroup; -} - - -// #pragma mark - -// Functions to check the validity of the bitmap - they are used from -// the "chkbfs" command - - -bool -BlockAllocator::IsValidCheckControl(check_control *control) -{ - if (control == NULL - || control->magic != BFS_IOCTL_CHECK_MAGIC) { - FATAL(("invalid check_control (%p)!\n", control)); - return false; - } - - return true; -} - - -status_t -BlockAllocator::StartChecking(check_control *control) -{ - if (!IsValidCheckControl(control)) - return B_BAD_VALUE; - - status_t status = fLock.Lock(); - if (status < B_OK) - return status; - - size_t size = BitmapSize(); - fCheckBitmap = (uint32 *)malloc(size); - if (fCheckBitmap == NULL) { - fLock.Unlock(); - return B_NO_MEMORY; - } - - check_cookie *cookie = new check_cookie(); - if (cookie == NULL) { - free(fCheckBitmap); - fCheckBitmap = NULL; - fLock.Unlock(); - - return B_NO_MEMORY; - } - - // initialize bitmap - memset(fCheckBitmap, 0, size); - for (int32 block = fVolume->Log().Start() + fVolume->Log().Length(); block-- > 0;) - SetCheckBitmapAt(block); - - cookie->stack.Push(fVolume->Root()); - cookie->stack.Push(fVolume->Indices()); - cookie->iterator = NULL; - control->cookie = cookie; - - fCheckCookie = cookie; - // to be able to restore nicely if "chkbfs" exited abnormally - - // ToDo: check reserved area in bitmap! - - return B_OK; -} - - -status_t -BlockAllocator::StopChecking(check_control *control) -{ - check_cookie *cookie; - if (control == NULL) - cookie = fCheckCookie; - else - cookie = (check_cookie *)control->cookie; - - if (cookie == NULL) - return B_ERROR; - - if (cookie->iterator != NULL) { - delete cookie->iterator; - cookie->iterator = NULL; - - // the current directory inode is still locked in memory - put_vnode(fVolume->ID(), fVolume->ToVnode(cookie->current)); - } - - // if CheckNextNode() could completely work through, we can - // fix any damages of the bitmap - if (control != NULL && control->status == B_ENTRY_NOT_FOUND) { - // calculate the number of used blocks in the check bitmap - size_t size = fVolume->BlockSize() * fNumGroups * fBlocksPerGroup; - off_t usedBlocks = 0LL; - - // ToDo: update the allocation groups used blocks info - for (uint32 i = size >> 2; i-- > 0;) { - uint32 compare = 1; - for (int16 j = 0; j < 32; j++, compare <<= 1) { - if (compare & fCheckBitmap[i]) - usedBlocks++; - } - } - - control->stats.freed = fVolume->UsedBlocks() - usedBlocks + control->stats.missing; - if (control->stats.freed < 0) - control->stats.freed = 0; - - // Should we fix errors? Were there any errors we can fix? - if (control->flags & BFS_FIX_BITMAP_ERRORS - && (control->stats.freed != 0 || control->stats.missing != 0)) { - // if so, write the check bitmap back over the original one, - // and use transactions here to play safe - we even use several - // transactions, so that we don't blow the maximum log size - // on large disks; since we don't need to make this atomic - fVolume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(usedBlocks); - - int32 blocksInBitmap = fNumGroups * fBlocksPerGroup; - int32 blockSize = fVolume->BlockSize(); - - for (int32 i = 0; i < blocksInBitmap; i += 512) { - Transaction transaction(fVolume, 1 + i); - - int32 blocksToWrite = 512; - if (blocksToWrite + i > blocksInBitmap) - blocksToWrite = blocksInBitmap - i; - - status_t status = transaction.WriteBlocks(1 + i, - (uint8 *)fCheckBitmap + i * blockSize, blocksToWrite); - if (status < B_OK) { - FATAL(("error writing bitmap: %s\n", strerror(status))); - break; - } - transaction.Done(); - } - } - } else - FATAL(("BlockAllocator::CheckNextNode() didn't run through\n")); - - free(fCheckBitmap); - fCheckBitmap = NULL; - fCheckCookie = NULL; - delete cookie; - fLock.Unlock(); - - return B_OK; -} - - -status_t -BlockAllocator::CheckNextNode(check_control *control) -{ - if (!IsValidCheckControl(control)) - return B_BAD_VALUE; - - check_cookie *cookie = (check_cookie *)control->cookie; - - while (true) { - if (cookie->iterator == NULL) { - if (!cookie->stack.Pop(&cookie->current)) { - // no more runs on the stack, we are obviously finished! - control->status = B_ENTRY_NOT_FOUND; - return B_ENTRY_NOT_FOUND; - } - - // get iterator for the next directory - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(fVolume->Lock()); -#endif - Vnode vnode(fVolume, cookie->current); - Inode *inode; - if (vnode.Get(&inode) < B_OK) { - FATAL(("check: Could not open inode at %Ld\n", fVolume->ToBlock(cookie->current))); - continue; - } - - if (!inode->IsContainer()) { - FATAL(("check: inode at %Ld should have been a directory\n", fVolume->ToBlock(cookie->current))); - continue; - } - - BPlusTree *tree; - if (inode->GetTree(&tree) != B_OK) { - FATAL(("check: could not open b+tree from inode at %Ld\n", fVolume->ToBlock(cookie->current))); - continue; - } - - cookie->parent = inode; - cookie->parent_mode = inode->Mode(); - - cookie->iterator = new TreeIterator(tree); - if (cookie->iterator == NULL) - RETURN_ERROR(B_NO_MEMORY); - - // the inode must stay locked in memory until the iterator is freed - vnode.Keep(); - - // check the inode of the directory - control->errors = 0; - control->status = CheckInode(inode, control); - - if (inode->GetName(control->name) < B_OK) - strcpy(control->name, "(node has no name)"); - - control->inode = inode->ID(); - control->mode = inode->Mode(); - - return B_OK; - } - - char name[B_FILE_NAME_LENGTH]; - uint16 length; - vnode_id id; - - status_t status = cookie->iterator->GetNextEntry(name, &length, B_FILE_NAME_LENGTH, &id); - if (status == B_ENTRY_NOT_FOUND) { - // there are no more entries in this iterator, free it and go on - delete cookie->iterator; - cookie->iterator = NULL; - - // unlock the directory's inode from memory - put_vnode(fVolume->ID(), fVolume->ToVnode(cookie->current)); - - continue; - } else if (status == B_OK) { - // ignore "." and ".." entries - if (!strcmp(name, ".") || !strcmp(name, "..")) - continue; - - // fill in the control data as soon as we have them - strlcpy(control->name, name, B_FILE_NAME_LENGTH); - control->inode = id; - control->errors = 0; - - Vnode vnode(fVolume, id); - Inode *inode; - if (vnode.Get(&inode) < B_OK) { - FATAL(("Could not open inode ID %Ld!\n", id)); - control->errors |= BFS_COULD_NOT_OPEN; - control->status = B_ERROR; - return B_OK; - } - - // check if the inode's name is the same as in the b+tree - if (inode->IsRegularNode()) { - SimpleLocker locker(inode->SmallDataLock()); - - const char *localName = inode->Name(); - if (localName == NULL || strcmp(localName, name)) { - control->errors |= BFS_NAMES_DONT_MATCH; - FATAL(("Names differ: tree \"%s\", inode \"%s\"\n", name, localName)); - } - } - - control->mode = inode->Mode(); - - // Check for the correct mode of the node (if the mode of the - // file don't fit to its parent, there is a serious problem) - if ((cookie->parent_mode & S_ATTR_DIR && !inode->IsAttribute()) - || (cookie->parent_mode & S_INDEX_DIR && !inode->IsIndex()) - || ((cookie->parent_mode & S_DIRECTORY | S_ATTR_DIR | S_INDEX_DIR) == S_DIRECTORY - && inode->Mode() & (S_ATTR | S_ATTR_DIR | S_INDEX_DIR))) { - FATAL(("inode at %Ld is of wrong type: %o (parent %o at %Ld)!\n", - inode->BlockNumber(), inode->Mode(), cookie->parent_mode, cookie->parent->BlockNumber())); - - // if we are allowed to fix errors, we should remove the file - if (control->flags & BFS_REMOVE_WRONG_TYPES - && control->flags & BFS_FIX_BITMAP_ERRORS) { -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(fVolume->Lock()); -#endif - // it's safe to start a transaction, because Inode::Remove() - // won't touch the block bitmap (which we hold the lock for) - // if we set the INODE_DONT_FREE_SPACE flag - since we fix - // the bitmap anyway - Transaction transaction(fVolume, cookie->parent->BlockNumber()); - - inode->Node()->flags |= INODE_DONT_FREE_SPACE; - status = cookie->parent->Remove(&transaction, name, NULL, inode->IsContainer()); - if (status == B_OK) - transaction.Done(); - } else - status = B_ERROR; - - control->errors |= BFS_WRONG_TYPE; - control->status = status; - return B_OK; - } - - // If the inode has an attribute directory, push it on the stack - if (!inode->Attributes().IsZero()) - cookie->stack.Push(inode->Attributes()); - - // push the directory on the stack so that it will be scanned later - if (inode->IsContainer() && !inode->IsIndex()) - cookie->stack.Push(inode->BlockRun()); - else { - // check it now - control->status = CheckInode(inode, control); - - return B_OK; - } - } - } - // is never reached -} - - -bool -BlockAllocator::CheckBitmapIsUsedAt(off_t block) const -{ - size_t size = BitmapSize(); - uint32 index = block / 32; // 32bit resolution - if (index > size / 4) - return false; - - return BFS_ENDIAN_TO_HOST_INT32(fCheckBitmap[index]) & (1UL << (block & 0x1f)); -} - - -void -BlockAllocator::SetCheckBitmapAt(off_t block) -{ - size_t size = BitmapSize(); - uint32 index = block / 32; // 32bit resolution - if (index > size / 4) - return; - - fCheckBitmap[index] |= HOST_ENDIAN_TO_BFS_INT32(1UL << (block & 0x1f)); -} - - -status_t -BlockAllocator::CheckBlockRun(block_run run, const char *type, check_control *control, bool allocated) -{ - if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups - || run.Start() > fGroups[run.AllocationGroup()].fNumBits - || uint32(run.Start() + run.Length()) > fGroups[run.AllocationGroup()].fNumBits - || run.length == 0) { - PRINT(("%s: block_run(%ld, %u, %u) is invalid!\n", type, run.AllocationGroup(), run.Start(), run.Length())); - if (control == NULL) - return B_BAD_DATA; - - control->errors |= BFS_INVALID_BLOCK_RUN; - return B_OK; - } - - uint32 bitsPerBlock = fVolume->BlockSize() << 3; - uint32 block = run.Start() / bitsPerBlock; - uint32 pos = run.Start() % bitsPerBlock; - int32 length = 0; - off_t firstMissing = -1, firstSet = -1; - off_t firstGroupBlock = (off_t)run.AllocationGroup() << fVolume->AllocationGroupShift(); - - AllocationBlock cached(fVolume); - - for (; block < fBlocksPerGroup && length < run.Length(); block++, pos = 0) { - if (cached.SetTo(fGroups[run.AllocationGroup()], block) < B_OK) - RETURN_ERROR(B_IO_ERROR); - - if (pos >= cached.NumBlockBits()) { - // something very strange has happened... - RETURN_ERROR(B_ERROR); - } - - while (length < run.Length() && pos < cached.NumBlockBits()) { - if (cached.IsUsed(pos) != allocated) { - if (control == NULL) { - PRINT(("%s: block_run(%ld, %u, %u) is only partially allocated (pos = %ld, length = %ld)!\n", - type, run.AllocationGroup(), run.Start(), run.Length(), pos, length)); - return B_BAD_DATA; - } - if (firstMissing == -1) { - firstMissing = firstGroupBlock + pos + block * bitsPerBlock; - control->errors |= BFS_MISSING_BLOCKS; - } - control->stats.missing++; - } else if (firstMissing != -1) { - PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are %sallocated!\n", - type, run.allocation_group, run.start, run.length, firstMissing, - firstGroupBlock + pos + block * bitsPerBlock - 1, allocated ? "not " : "")); - firstMissing = -1; - } - - if (fCheckBitmap != NULL) { - // Set the block in the check bitmap as well, but have a look if it - // is already allocated first - uint32 offset = pos + block * bitsPerBlock; - if (CheckBitmapIsUsedAt(firstGroupBlock + offset)) { - if (firstSet == -1) { - firstSet = firstGroupBlock + offset; - control->errors |= BFS_BLOCKS_ALREADY_SET; - } - control->stats.already_set++; - } else { - if (firstSet != -1) { - FATAL(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are already set!\n", - type, run.AllocationGroup(), run.Start(), run.Length(), firstSet, firstGroupBlock + offset - 1)); - firstSet = -1; - } - SetCheckBitmapAt(firstGroupBlock + offset); - } - } - length++; - pos++; - } - - if (block + 1 >= fBlocksPerGroup || length >= run.Length()) { - if (firstMissing != -1) - PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are not allocated!\n", type, run.AllocationGroup(), run.Start(), run.Length(), firstMissing, firstGroupBlock + pos + block * bitsPerBlock - 1)); - if (firstSet != -1) - FATAL(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are already set!\n", type, run.AllocationGroup(), run.Start(), run.Length(), firstSet, firstGroupBlock + pos + block * bitsPerBlock - 1)); - } - } - - return B_OK; -} - - -status_t -BlockAllocator::CheckInode(Inode *inode, check_control *control) -{ - if (control != NULL && fCheckBitmap == NULL) - return B_NO_INIT; - if (inode == NULL) - return B_BAD_VALUE; - - status_t status = CheckBlockRun(inode->BlockRun(), "inode", control); - if (status < B_OK) - return status; - - if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) { - // symlinks may not have a valid data stream - if (strlen(inode->Node()->short_symlink) >= SHORT_SYMLINK_NAME_LENGTH) - return B_BAD_DATA; - - return B_OK; - } - - data_stream *data = &inode->Node()->data; - - // check the direct range - - if (data->max_direct_range) { - for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) { - if (data->direct[i].IsZero()) - break; - - status = CheckBlockRun(data->direct[i], "direct", control); - if (status < B_OK) - return status; - } - } - - CachedBlock cached(fVolume); - - // check the indirect range - - if (data->max_indirect_range) { - status = CheckBlockRun(data->indirect, "indirect", control); - if (status < B_OK) - return status; - - off_t block = fVolume->ToBlock(data->indirect); - - for (int32 i = 0; i < data->indirect.Length(); i++) { - block_run *runs = (block_run *)cached.SetTo(block + i); - if (runs == NULL) - RETURN_ERROR(B_IO_ERROR); - - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - int32 index = 0; - for (; index < runsPerBlock; index++) { - if (runs[index].IsZero()) - break; - - status = CheckBlockRun(runs[index], "indirect->run", control); - if (status < B_OK) - return status; - } - if (index < runsPerBlock) - break; - } - } - - // check the double indirect range - - if (data->max_double_indirect_range) { - status = CheckBlockRun(data->double_indirect, "double indirect", control); - if (status < B_OK) - return status; - - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT; - - CachedBlock cachedDirect(fVolume); - int32 maxIndirectIndex = (data->double_indirect.Length() << fVolume->BlockShift()) - / sizeof(block_run); - - for (int32 indirectIndex = 0; indirectIndex < maxIndirectIndex; indirectIndex++) { - // get the indirect array block - block_run *array = (block_run *)cached.SetTo(fVolume->ToBlock(data->double_indirect) - + indirectIndex / runsPerBlock); - if (array == NULL) - return B_IO_ERROR; - - block_run indirect = array[indirectIndex % runsPerBlock]; - // are we finished yet? - if (indirect.IsZero()) - return B_OK; - - status = CheckBlockRun(indirect, "double indirect->runs", control); - if (status < B_OK) - return status; - - int32 maxIndex = (indirect.Length() << fVolume->BlockShift()) / sizeof(block_run); - - for (int32 index = 0; index < maxIndex; ) { - block_run *runs = (block_run *)cachedDirect.SetTo(fVolume->ToBlock(indirect) - + index / runsPerBlock); - if (runs == NULL) - return B_IO_ERROR; - - do { - // are we finished yet? - if (runs[index % runsPerBlock].IsZero()) - return B_OK; - - status = CheckBlockRun(runs[index % runsPerBlock], "double indirect->runs->run", control); - if (status < B_OK) - return status; - } while ((++index % runsPerArray) != 0); - } - } - } - - return B_OK; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/BlockAllocator.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/BlockAllocator.h deleted file mode 100644 index 3467bea59d..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/BlockAllocator.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef BLOCK_ALLOCATOR_H -#define BLOCK_ALLOCATOR_H -/* BlockAllocator - block bitmap handling and allocation policies -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include "Lock.h" - - -class AllocationGroup; -class Transaction; -class Volume; -class Inode; -struct disk_super_block; -struct block_run; -struct check_control; -struct check_cookie; - - -class BlockAllocator { - public: - BlockAllocator(Volume *volume); - ~BlockAllocator(); - - status_t Initialize(bool full = true); - status_t InitializeAndClearBitmap(Transaction &transaction); - - status_t AllocateForInode(Transaction *transaction, const block_run *parent, - mode_t type, block_run &run); - status_t Allocate(Transaction *transaction, const Inode *inode, off_t numBlocks, - block_run &run, uint16 minimum = 1); - status_t Free(Transaction *transaction, block_run run); - - status_t AllocateBlocks(Transaction *transaction, int32 group, uint16 start, - uint16 numBlocks, uint16 minimum, block_run &run); - - status_t StartChecking(check_control *control); - status_t StopChecking(check_control *control); - status_t CheckNextNode(check_control *control); - - status_t CheckBlockRun(block_run run, const char *type = NULL, check_control *control = NULL, bool allocated = true); - status_t CheckInode(Inode *inode, check_control *control = NULL); - - size_t BitmapSize() const; - - private: - bool IsValidCheckControl(check_control *control); - bool CheckBitmapIsUsedAt(off_t block) const; - void SetCheckBitmapAt(off_t block); - - static status_t initialize(BlockAllocator *); - - Volume *fVolume; - Semaphore fLock; - AllocationGroup *fGroups; - int32 fNumGroups; - uint32 fBlocksPerGroup; - - uint32 *fCheckBitmap; - check_cookie *fCheckCookie; -}; - -#endif /* BLOCK_ALLOCATOR_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/BufferPool.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/BufferPool.cpp deleted file mode 100644 index 77ad8ad229..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/BufferPool.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* BufferPool - a buffer pool for uncached file access -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include "BufferPool.h" -#include "Debug.h" - -#include - - -const uint32 kNumBuffers = 8; - - -BufferPool::BufferPool() - : - fFirstFree(NULL) -{ - fLock = create_sem(1, "buffer lock"); - fFreeBuffers = create_sem(0, "free buffers"); - -#ifndef USER - set_sem_owner(fLock, B_SYSTEM_TEAM); - set_sem_owner(fFreeBuffers, B_SYSTEM_TEAM); -#endif -} - - -BufferPool::~BufferPool() -{ - delete_sem(fFreeBuffers); - - acquire_sem(fLock); - // the return value doesn't interest us anymore - - void **buffer = fFirstFree; - while (buffer != NULL) { - void **nextBuffer = (void **)*buffer; - free(buffer); - buffer = nextBuffer; - } - - delete_sem(fLock); -} - - -status_t -BufferPool::InitCheck() -{ - if (fLock < B_OK - || fFreeBuffers < B_OK) - return B_ERROR; - - return B_OK; -} - - -status_t -BufferPool::RequestBuffers(uint32 blockSize) -{ - void **buffers[kNumBuffers]; - - // allocate and connect buffers - - for (uint32 i = 0; i < kNumBuffers; i++) { - buffers[i] = (void **)malloc(blockSize); - if (buffers[i] == NULL) { - // free already allocated buffers - for (;i-- > 0; i++) - free(buffers[i]); - RETURN_ERROR(B_NO_MEMORY); - } - if (i > 0) - *(buffers[i]) = buffers[i - 1]; - } - - // add the buffers to the free buffers queue - - status_t status = acquire_sem(fLock); - if (status == B_OK) { - *(buffers[0]) = fFirstFree; - fFirstFree = buffers[kNumBuffers - 1]; - release_sem(fLock); - release_sem_etc(fFreeBuffers, kNumBuffers, B_DO_NOT_RESCHEDULE); - } else { - for (uint32 i = 0; i < kNumBuffers; i++) - free(buffers[i]); - } - - RETURN_ERROR(status); -} - - -status_t -BufferPool::ReleaseBuffers() -{ - status_t status = acquire_sem_etc(fFreeBuffers, kNumBuffers, 0, 0); - if (status < B_OK) - return status; - - status = acquire_sem(fLock); - if (status < B_OK) - return status; - - void **buffer = fFirstFree; - for (uint32 i = 0; i < kNumBuffers && buffer; i++) { - void **nextBuffer = (void **)*buffer; - - free(buffer); - buffer = nextBuffer; - } - fFirstFree = buffer; - - release_sem(fLock); - return B_OK; -} - - -status_t -BufferPool::GetBuffer(void **_buffer) -{ - status_t status = acquire_sem(fFreeBuffers); - if (status < B_OK) - return status; - - if ((status = acquire_sem(fLock)) < B_OK) { - release_sem(fFreeBuffers); - return status; - } - - void **buffer = fFirstFree; - fFirstFree = (void **)*buffer; - - release_sem(fLock); - - *_buffer = (void *)buffer; - return B_OK; -} - - -status_t -BufferPool::PutBuffer(void *_buffer) -{ - void **buffer = (void **)_buffer; - if (buffer == NULL) - return B_BAD_VALUE; - - status_t status = acquire_sem(fLock); - if (status < B_OK) - return status; - - *buffer = fFirstFree; - fFirstFree = buffer; - - release_sem(fLock); - release_sem(fFreeBuffers); - - return B_OK; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/BufferPool.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/BufferPool.h deleted file mode 100644 index 1653e970be..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/BufferPool.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef BUFFER_POOL_H -#define BUFFER_POOL_H -/* BufferPool - a buffer pool for uncached file access -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include - - -class BufferPool { - public: - BufferPool(); - ~BufferPool(); - - status_t InitCheck(); - - status_t RequestBuffers(uint32 blockSize); - status_t ReleaseBuffers(); - - status_t GetBuffer(void **_buffer); - status_t PutBuffer(void *buffer); - - private: - sem_id fLock, fFreeBuffers; - void **fFirstFree; -}; - -#endif /* BUFFER_POOL_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Chain.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Chain.h deleted file mode 100644 index bea177eeb0..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Chain.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef CHAIN_H -#define CHAIN_H -/* Chain - a chain implementation; it's used for the callback management -** throughout the code (currently TreeIterator, and AttributeIterator). -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -/** The Link class you want to use with the Chain class needs to have - * a "fNext" member which is accessable from within the Chain class. - */ - -template class Chain { - public: - Chain() - : - fFirst(NULL) - { - } - - void Add(Link *link) - { - link->fNext = fFirst; - fFirst = link; - } - - void Remove(Link *link) - { - // search list for the correct callback to remove - Link *last = NULL,*entry; - for (entry = fFirst;link != entry;entry = entry->fNext) - last = entry; - if (link == entry) { - if (last) - last->fNext = link->fNext; - else - fFirst = link->fNext; - } - } - - Link *Next(Link *last) - { - if (last == NULL) - return fFirst; - - return last->fNext; - } - - private: - Link *fFirst; -}; - -#endif /* CHAIN_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Debug.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/Debug.cpp deleted file mode 100644 index 4537fa25c7..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Debug.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* Debug - debug stuff -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** Some code is based on work previously done by Marcus Overhagen -** -** This file may be used under the terms of the MIT License. -*/ - - -#include "Debug.h" -#include "BPlusTree.h" -#include "Inode.h" - -#include - -#include - -#define Print __out - - -char * -get_tupel(uint32 id) -{ - static unsigned char tupel[5]; - - tupel[0] = 0xff & (id >> 24); - tupel[1] = 0xff & (id >> 16); - tupel[2] = 0xff & (id >> 8); - tupel[3] = 0xff & (id); - tupel[4] = 0; - for (int16 i = 0;i < 4;i++) { - if (tupel[i] < ' ' || tupel[i] > 128) - tupel[i] = '.'; - } - - return (char *)tupel; -} - - -void -dump_block_run(const char *prefix, block_run &run) -{ - Print("%s(%ld, %d, %d)\n", prefix, run.allocation_group, run.start, run.length); -} - - -void -dump_inode(Inode &inode) -{ - Print("Inode (%p) {\n", &inode); - Print("\tfVolume = %p\n", inode.fVolume); - Print("\tfBlockNumber = 0x%16Lx\n", inode.fBlockNumber); - Print("\tfNode = %p\n", inode.Node()); - Print("\tfTree = %p\n", inode.fTree); - Print("\tfAttributes = %p\n", inode.fAttributes); - Print("\tfOldSize = 0x%16Lx\n", inode.fOldSize); - Print("\tfOldLastModified = 0x%16Lx\n", inode.fOldLastModified); - Print("}\n"); -} - - -void -dump_super_block(disk_super_block *superBlock) -{ - Print("disk_super_block:\n"); - Print(" name = %s\n", superBlock->name); - Print(" magic1 = %#08lx (%s) %s\n", superBlock->magic1, get_tupel(superBlock->magic1), (superBlock->magic1 == SUPER_BLOCK_MAGIC1 ? "valid" : "INVALID")); - Print(" fs_byte_order = %#08lx (%s)\n", superBlock->fs_byte_order, get_tupel(superBlock->fs_byte_order)); - Print(" block_size = %lu\n", superBlock->block_size); - Print(" block_shift = %lu\n", superBlock->block_shift); - Print(" num_blocks = %Lu\n", superBlock->num_blocks); - Print(" used_blocks = %Lu\n", superBlock->used_blocks); - Print(" inode_size = %lu\n", superBlock->inode_size); - Print(" magic2 = %#08lx (%s) %s\n", superBlock->magic2, get_tupel(superBlock->magic2), (superBlock->magic2 == (int)SUPER_BLOCK_MAGIC2 ? "valid" : "INVALID")); - Print(" blocks_per_ag = %lu\n", superBlock->blocks_per_ag); - Print(" ag_shift = %lu (%ld bytes)\n", superBlock->ag_shift, 1L << superBlock->ag_shift); - Print(" num_ags = %lu\n", superBlock->num_ags); - Print(" flags = %#08lx (%s)\n", superBlock->flags, get_tupel(superBlock->flags)); - dump_block_run(" log_blocks = ", superBlock->log_blocks); - Print(" log_start = %Lu\n", superBlock->log_start); - Print(" log_end = %Lu\n", superBlock->log_end); - Print(" magic3 = %#08lx (%s) %s\n", superBlock->magic3, get_tupel(superBlock->magic3), (superBlock->magic3 == SUPER_BLOCK_MAGIC3 ? "valid" : "INVALID")); - dump_block_run(" root_dir = ", superBlock->root_dir); - dump_block_run(" indices = ", superBlock->indices); -} - - -void -dump_data_stream(data_stream *stream) -{ - Print("data_stream:\n"); - for (int i = 0; i < NUM_DIRECT_BLOCKS; i++) { - if (!stream->direct[i].IsZero()) { - Print(" direct[%02d] = ",i); - dump_block_run("",stream->direct[i]); - } - } - Print(" max_direct_range = %Lu\n", stream->max_direct_range); - - if (!stream->indirect.IsZero()) - dump_block_run(" indirect = ", stream->indirect); - - Print(" max_indirect_range = %Lu\n", stream->max_indirect_range); - - if (!stream->double_indirect.IsZero()) - dump_block_run(" double_indirect = ", stream->double_indirect); - - Print(" max_double_indirect_range = %Lu\n", stream->max_double_indirect_range); - Print(" size = %Lu\n", stream->size); -} - - -void -dump_inode(bfs_inode *inode) -{ - Print("inode:\n"); - Print(" magic1 = %08lx (%s) %s\n", inode->magic1, - get_tupel(inode->magic1), (inode->magic1 == INODE_MAGIC1 ? "valid" : "INVALID")); - dump_block_run( " inode_num = ", inode->inode_num); - Print(" uid = %lu\n", inode->uid); - Print(" gid = %lu\n", inode->gid); - Print(" mode = %08lx\n", inode->mode); - Print(" flags = %08lx\n", inode->flags); - Print(" create_time = %Ld (%Ld)\n", inode->create_time, - inode->create_time >> INODE_TIME_SHIFT); - Print(" last_modified_time = %Ld (%Ld)\n", inode->last_modified_time, - inode->last_modified_time >> INODE_TIME_SHIFT); - dump_block_run( " parent = ", inode->parent); - dump_block_run( " attributes = ", inode->attributes); - Print(" type = %lu\n", inode->type); - Print(" inode_size = %lu\n", inode->inode_size); - Print(" etc = %#08lx\n", inode->etc); - Print(" short_symlink = %s\n", - S_ISLNK(inode->mode) && (inode->flags & INODE_LONG_SYMLINK) == 0 ? - inode->short_symlink : "-"); - dump_data_stream(&(inode->data)); - Print(" --\n pad[0] = %08lx\n", inode->pad[0]); - Print(" pad[1] = %08lx\n", inode->pad[1]); - Print(" pad[2] = %08lx\n", inode->pad[2]); - Print(" pad[3] = %08lx\n", inode->pad[3]); -} - - -void -dump_bplustree_header(bplustree_header *header) -{ - Print("bplustree_header:\n"); - Print(" magic = %#08lx (%s) %s\n", header->magic, - get_tupel(header->magic), (header->magic == BPLUSTREE_MAGIC ? "valid" : "INVALID")); - Print(" node_size = %lu\n", header->node_size); - Print(" max_number_of_levels = %lu\n", header->max_number_of_levels); - Print(" data_type = %lu\n", header->data_type); - Print(" root_node_pointer = %Ld\n", header->root_node_pointer); - Print(" free_node_pointer = %Ld\n", header->free_node_pointer); - Print(" maximum_size = %Lu\n", header->maximum_size); -} - - -#define DUMPED_BLOCK_SIZE 16 - -void -dump_block(const char *buffer,int size) -{ - for(int i = 0;i < size;) { - int start = i; - - for(;i < start+DUMPED_BLOCK_SIZE;i++) { - if (!(i % 4)) - Print(" "); - - if (i >= size) - Print(" "); - else - Print("%02x",*(unsigned char *)(buffer+i)); - } - Print(" "); - - for(i = start;i < start + DUMPED_BLOCK_SIZE;i++) { - if (i < size) { - char c = *(buffer+i); - - if (c < 30) - Print("."); - else - Print("%c",c); - } else - break; - } - Print("\n"); - } -} - - -void -dump_bplustree_node(bplustree_node *node,bplustree_header *header,Volume *volume) -{ - Print("bplustree_node:\n"); - Print(" left_link = %Ld\n", node->left_link); - Print(" right_link = %Ld\n", node->right_link); - Print(" overflow_link = %Ld\n", node->overflow_link); - Print(" all_key_count = %u\n", node->all_key_count); - Print(" all_key_length = %u\n", node->all_key_length); - - if (header == NULL) - return; - - if (node->all_key_count > node->all_key_length - || uint32(node->all_key_count * 10) > (uint32)header->node_size - || node->all_key_count == 0) { - Print("\n"); - dump_block((char *)node, header->node_size/*, sizeof(off_t)*/); - return; - } - - Print("\n"); - for (int32 i = 0;i < node->all_key_count;i++) { - uint16 length; - char buffer[256], *key = (char *)node->KeyAt(i, &length); - if (length > 255 || length == 0) { - Print(" %2ld. Invalid length (%u)!!\n", i, length); - dump_block((char *)node, header->node_size/*, sizeof(off_t)*/); - break; - } - memcpy(buffer, key, length); - buffer[length] = '\0'; - - off_t *value = node->Values() + i; - if ((uint32)value < (uint32)node || (uint32)value > (uint32)node + header->node_size) - Print(" %2ld. Invalid Offset!!\n", i); - else { - Print(" %2ld. ", i); - if (header->data_type == BPLUSTREE_STRING_TYPE) - Print("\"%s\"", buffer); - else if (header->data_type == BPLUSTREE_INT32_TYPE) - Print("int32 = %ld (0x%lx)", *(int32 *)&buffer, *(int32 *)&buffer); - else if (header->data_type == BPLUSTREE_UINT32_TYPE) - Print("uint32 = %lu (0x%lx)", *(uint32 *)&buffer, *(uint32 *)&buffer); - else if (header->data_type == BPLUSTREE_INT64_TYPE) - Print("int64 = %Ld (0x%Lx)", *(int64 *)&buffer, *(int64 *)&buffer); - else - Print("???"); - - off_t offset = *value & 0x3fffffffffffffffLL; - Print(" (%d bytes) -> %Ld", length, offset); - if (volume != NULL) { - block_run run = volume->ToBlockRun(offset); - Print(" (%ld, %d)", run.allocation_group, run.start); - } - if (bplustree_node::LinkType(*value) == BPLUSTREE_DUPLICATE_FRAGMENT) - Print(" (duplicate fragment %Ld)\n", *value & 0x3ff); - else if (bplustree_node::LinkType(*value) == BPLUSTREE_DUPLICATE_NODE) - Print(" (duplicate node)\n"); - else - Print("\n"); - } - } -} - - -// #pragma mark - - - -#ifndef USER -//#warn Don't mount more than once... would register twice the debugger commands! - -static int -dbg_inode(int argc, char **argv) -{ - if (argc < 2) { - kprintf("usage: obfsinode ptr-to-inode\n"); - return 0; - } - - Inode *inode = (Inode *)parse_expression(argv[1]); - dump_inode(*inode); - - return B_OK; -} - -#endif - -void -remove_debugger_commands() -{ -#ifndef USER - remove_debugger_command("obfsinode", dbg_inode); -#endif -} - - -void -add_debugger_commands() -{ -#ifndef USER - add_debugger_command("obfsinode", dbg_inode, "dump an Inode object"); -#endif -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Debug.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Debug.h deleted file mode 100644 index 6cb2d5fbf5..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Debug.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved. - * This file may be used under the terms of the MIT License. - */ -#ifndef DEBUG_H -#define DEBUG_H - - -#include - -#ifdef DEBUG -# include -#endif - -#ifdef USER -# include -# define __out printf -#else -# include -# define __out dprintf -#endif - -#define _KERNEL_DEBUG_H - // to work around problems with private/kernel/debug.h - -// Which debugger should be used when? -// The DEBUGGER() macro actually has no effect if DEBUG is not defined, -// use the DIE() macro if you really want to die. -#ifdef DEBUG -# ifdef USER -# define DEBUGGER(x) debugger x -# else -# define DEBUGGER(x) kernel_debugger x -# endif -#else -# define DEBUGGER(x) ; -#endif - -#ifdef USER -# define DIE(x) debugger x -#else -# define DIE(x) kernel_debugger x -#endif - -// Short overview over the debug output macros: -// PRINT() -// is for general messages that very unlikely should appear in a release build -// FATAL() -// this is for fatal messages, when something has really gone wrong -// INFORM() -// general information, as disk size, etc. -// REPORT_ERROR(status_t) -// prints out error information -// RETURN_ERROR(status_t) -// calls REPORT_ERROR() and return the value -// D() -// the statements in D() are only included if DEBUG is defined - -#ifdef DEBUG - #define PRINT(x) { __out("bfs: "); __out x; } - #define REPORT_ERROR(status) \ - __out("bfs: %s:%d: %s\n", __FUNCTION__, __LINE__, strerror(status)); - #define RETURN_ERROR(err) { status_t _status = err; if (_status < B_OK) REPORT_ERROR(_status); return _status;} - #define FATAL(x) { __out("bfs: "); __out x; } - #define INFORM(x) { __out("bfs: "); __out x; } -// #define FUNCTION() __out("bfs: %s()\n",__FUNCTION__); - #define FUNCTION_START(x) { __out("bfs: %s() ",__FUNCTION__); __out x; } - #define FUNCTION() ; -// #define FUNCTION_START(x) ; - #define D(x) {x;}; - #define ASSERT(x) { if (!(x)) DEBUGGER(("bfs: assert failed: " #x "\n")); } -#else - #define PRINT(x) ; - #define REPORT_ERROR(status) ; - #define RETURN_ERROR(status) return status; - #define FATAL(x) { __out("bfs: "); __out x; } - #define INFORM(x) { __out("bfs: "); __out x; } - #define FUNCTION() ; - #define FUNCTION_START(x) ; - #define D(x) ; - #define ASSERT(x) ; -#endif - -#ifdef DEBUG - struct block_run; - struct bplustree_header; - struct bplustree_node; - struct data_stream; - struct bfs_inode; - struct disk_super_block; - class Inode; - class Volume; - - // some structure dump functions - extern void dump_block_run(const char *prefix, block_run &run); - extern void dump_inode(Inode &inode); - extern void dump_super_block(disk_super_block *superBlock); - extern void dump_data_stream(data_stream *stream); - extern void dump_inode(bfs_inode *inode); - extern void dump_bplustree_header(bplustree_header *header); - extern void dump_bplustree_node(bplustree_node *node, - bplustree_header *header = NULL, Volume *volume = NULL); - extern void dump_block(const char *buffer, int size); - - extern void remove_debugger_commands(); - extern void add_debugger_commands(); -#endif - -#endif /* DEBUG_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Index.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/Index.cpp deleted file mode 100644 index d5976ed98e..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Index.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* Index - index access functions -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include "Debug.h" -#include "Index.h" -#include "Volume.h" -#include "Inode.h" -#include "BPlusTree.h" - -#include -#include - -// B_MIME_STRING_TYPE is defined in storage/Mime.h, but we -// don't need the whole file here; the type can't change anyway -#ifndef _MIME_H -# define B_MIME_STRING_TYPE 'MIMS' -#endif - - -Index::Index(Volume *volume) - : - fVolume(volume), - fNode(NULL) -{ -} - - -Index::~Index() -{ - if (fNode == NULL) - return; - - put_vnode(fVolume->ID(), fNode->ID()); -} - - -void -Index::Unset() -{ - if (fNode == NULL) - return; - - put_vnode(fVolume->ID(), fNode->ID()); - fNode = NULL; - fName = NULL; -} - - -/** Sets the index to specified one. Returns an error if the index could - * not be found or initialized. - * Note, Index::Update() may be called on the object even if this method - * failed previously. In this case, it will only update live queries for - * the updated attribute. - */ - -status_t -Index::SetTo(const char *name) -{ - // remove the old node, if the index is set for the second time - Unset(); - - fName = name; - // only stores the pointer, so it assumes that it will stay constant - // in further comparisons (currently only used in Index::Update()) - - // Note, the name is saved even if the index couldn't be initialized! - // This is used to optimize Index::Update() in case there is no index - - Inode *indices = fVolume->IndicesNode(); - if (indices == NULL) - return B_ENTRY_NOT_FOUND; - - BPlusTree *tree; - if (indices->GetTree(&tree) != B_OK) - return B_BAD_VALUE; - - vnode_id id; - status_t status = tree->Find((uint8 *)name, (uint16)strlen(name), &id); - if (status != B_OK) - return status; - - Vnode vnode(fVolume, id); - if (vnode.Get(&fNode) != B_OK) - return B_ENTRY_NOT_FOUND; - - if (fNode == NULL) { - FATAL(("fatal error at Index::InitCheck(), get_vnode() returned NULL pointer\n")); - return B_ERROR; - } - - vnode.Keep(); - return B_OK; -} - - -/** Returns a standard type code for the stat() index type codes. Returns - * zero if the type is not known (can only happen if the mode field is - * corrupted somehow or not that of an index). - */ - -uint32 -Index::Type() -{ - if (fNode == NULL) - return 0; - - switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX | - S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX)) { - case S_INT_INDEX: - return B_INT32_TYPE; - case S_UINT_INDEX: - return B_UINT32_TYPE; - case S_LONG_LONG_INDEX: - return B_INT64_TYPE; - case S_ULONG_LONG_INDEX: - return B_UINT64_TYPE; - case S_FLOAT_INDEX: - return B_FLOAT_TYPE; - case S_DOUBLE_INDEX: - return B_DOUBLE_TYPE; - case S_STR_INDEX: - return B_STRING_TYPE; - } - FATAL(("index has unknown type!\n")); - return 0; -} - - -size_t -Index::KeySize() -{ - if (fNode == NULL) - return 0; - - int32 mode = fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX | - S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX); - - if (mode == S_STR_INDEX) - // string indices don't have a fixed key size - return 0; - - switch (mode) { - case S_INT_INDEX: - case S_UINT_INDEX: - return sizeof(int32); - case S_LONG_LONG_INDEX: - case S_ULONG_LONG_INDEX: - return sizeof(int64); - case S_FLOAT_INDEX: - return sizeof(float); - case S_DOUBLE_INDEX: - return sizeof(double); - } - FATAL(("index has unknown type!\n")); - return 0; -} - - -status_t -Index::Create(Transaction *transaction, const char *name, uint32 type) -{ - Unset(); - - int32 mode = 0; - switch (type) { - case B_INT32_TYPE: - mode = S_INT_INDEX; - break; - case B_UINT32_TYPE: - mode = S_UINT_INDEX; - break; - case B_INT64_TYPE: - mode = S_LONG_LONG_INDEX; - break; - case B_UINT64_TYPE: - mode = S_ULONG_LONG_INDEX; - break; - case B_FLOAT_TYPE: - mode = S_FLOAT_INDEX; - break; - case B_DOUBLE_TYPE: - mode = S_DOUBLE_INDEX; - break; - case B_STRING_TYPE: - case B_MIME_STRING_TYPE: - // B_MIME_STRING_TYPE is the only supported non-standard type, but - // will be handled like a B_STRING_TYPE internally - mode = S_STR_INDEX; - break; - default: - return B_BAD_TYPE; - } - - - // do we need to create the index directory first? - if (fVolume->IndicesNode() == NULL) { - status_t status = fVolume->CreateIndicesRoot(transaction); - if (status < B_OK) - RETURN_ERROR(status); - } - - // Inode::Create() will keep the inode locked for us - return Inode::Create(transaction, fVolume->IndicesNode(), name, - S_INDEX_DIR | S_DIRECTORY | mode, 0, type, NULL, &fNode); -} - - -/** Updates the specified index, the oldKey will be removed from, the newKey - * inserted into the tree. - * If the method returns B_BAD_INDEX, it means the index couldn't be found - - * the most common reason will be that the index doesn't exist. - * You may not want to let the whole transaction fail because of that. - */ - -status_t -Index::Update(Transaction *transaction, const char *name, int32 type, const uint8 *oldKey, - uint16 oldLength, const uint8 *newKey, uint16 newLength, Inode *inode) -{ - if (name == NULL - || oldKey == NULL && newKey == NULL - || oldKey != NULL && oldLength == 0 - || newKey != NULL && newLength == 0) - return B_BAD_VALUE; - - // B_MIME_STRING_TYPE is the only supported non-standard type - if (type == B_MIME_STRING_TYPE) - type = B_STRING_TYPE; - - // If the two keys are identical, don't do anything - only compare if the - // type has been set, until we have a real type code, we can't do much - // about the comparison here - if (type != 0 && !compareKeys(type, oldKey, oldLength, newKey, newLength)) - return B_OK; - - // update all live queries about the change, if they have an index or not - if (type != 0) - fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength, newKey, newLength); - - status_t status; - if (((name != fName || strcmp(name, fName)) && (status = SetTo(name)) < B_OK) - || fNode == NULL) - return B_BAD_INDEX; - - // now that we have the type, check again for equality - if (type == 0 && !compareKeys(Type(), oldKey, oldLength, newKey, newLength)) - return B_OK; - - // same for the live query update - if (type == 0) - fVolume->UpdateLiveQueries(inode, name, Type(), oldKey, oldLength, newKey, newLength); - - BPlusTree *tree; - if ((status = Node()->GetTree(&tree)) < B_OK) - return status; - - // remove the old key from the tree - - if (oldKey != NULL) { - status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength, inode->ID()); - if (status == B_ENTRY_NOT_FOUND) { - // That's not nice, but should be no reason to let the whole thing fail - FATAL(("Could not find value in index \"%s\"!\n", name)); - } else if (status < B_OK) - return status; - } - - // add the new key to the tree - - if (newKey != NULL) - status = tree->Insert(transaction, (const uint8 *)newKey, newLength, inode->ID()); - - return status; -} - - -status_t -Index::InsertName(Transaction *transaction, const char *name, Inode *inode) -{ - return UpdateName(transaction, NULL, name, inode); -} - - -status_t -Index::RemoveName(Transaction *transaction, const char *name, Inode *inode) -{ - return UpdateName(transaction, name, NULL, inode); -} - - -status_t -Index::UpdateName(Transaction *transaction, const char *oldName, const char *newName, Inode *inode) -{ - uint16 oldLength = oldName ? strlen(oldName) : 0; - uint16 newLength = newName ? strlen(newName) : 0; - return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName, oldLength, - (uint8 *)newName, newLength, inode); -} - - -status_t -Index::InsertSize(Transaction *transaction, Inode *inode) -{ - off_t size = inode->Size(); - return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, sizeof(int64), inode); -} - - -status_t -Index::RemoveSize(Transaction *transaction, Inode *inode) -{ - // Inode::OldSize() is the size that's in the index - off_t size = inode->OldSize(); - return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size, sizeof(int64), NULL, 0, inode); -} - - -status_t -Index::UpdateSize(Transaction *transaction, Inode *inode) -{ - off_t oldSize = inode->OldSize(); - off_t newSize = inode->Size(); - - status_t status = Update(transaction, "size", B_INT64_TYPE, (uint8 *)&oldSize, - sizeof(int64), (uint8 *)&newSize, sizeof(int64), inode); - if (status == B_OK) - inode->UpdateOldSize(); - - return status; -} - - -status_t -Index::InsertLastModified(Transaction *transaction, Inode *inode) -{ - off_t modified = inode->LastModified(); - return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0, - (uint8 *)&modified, sizeof(int64), inode); -} - - -status_t -Index::RemoveLastModified(Transaction *transaction, Inode *inode) -{ - // Inode::OldLastModified() is the value which is in the index - off_t modified = inode->OldLastModified(); - return Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&modified, - sizeof(int64), NULL, 0, inode); -} - - -status_t -Index::UpdateLastModified(Transaction *transaction, Inode *inode, off_t modified) -{ - off_t oldModified = inode->OldLastModified(); - if (modified == -1) - modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; - modified |= fVolume->GetUniqueID() & INODE_TIME_MASK; - - status_t status = Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&oldModified, - sizeof(int64), (uint8 *)&modified, sizeof(int64), inode); - - inode->Node()->last_modified_time = modified; - if (status == B_OK) - inode->UpdateOldLastModified(); - - return status; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Index.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Index.h deleted file mode 100644 index 0c8843ee8d..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Index.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef INDEX_H -#define INDEX_H -/* Index - index access functions -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include - -class Transaction; -class Volume; -class Inode; - - -class Index { - public: - Index(Volume *volume); - ~Index(); - - status_t SetTo(const char *name); - void Unset(); - - Inode *Node() const { return fNode; }; - uint32 Type(); - size_t KeySize(); - - status_t Create(Transaction *transaction, const char *name, uint32 type); - - status_t Update(Transaction *transaction, const char *name, int32 type, const uint8 *oldKey, uint16 oldLength, const uint8 *newKey, uint16 newLength, Inode *inode); - - status_t InsertName(Transaction *transaction,const char *name,Inode *inode); - status_t RemoveName(Transaction *transaction,const char *name,Inode *inode); - status_t UpdateName(Transaction *transaction,const char *oldName,const char *newName,Inode *inode); - - status_t InsertSize(Transaction *transaction, Inode *inode); - status_t RemoveSize(Transaction *transaction, Inode *inode); - status_t UpdateSize(Transaction *transaction, Inode *inode); - - status_t InsertLastModified(Transaction *transaction, Inode *inode); - status_t RemoveLastModified(Transaction *transaction, Inode *inode); - status_t UpdateLastModified(Transaction *transaction, Inode *inode,off_t modified = -1); - - private: - Index(const Index &); - Index &operator=(const Index &); - // no implementation - - Volume *fVolume; - Inode *fNode; - const char *fName; -}; - -#endif /* INDEX_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Inode.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/Inode.cpp deleted file mode 100644 index b0cfe63f78..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Inode.cpp +++ /dev/null @@ -1,2254 +0,0 @@ -/* - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de - * This file may be used under the terms of the MIT License. - */ - -//! inode access functions - - -#include "Debug.h" -#include "Inode.h" -#include "BPlusTree.h" -#include "Stream.h" -#include "Index.h" - -#include - -#include -#include - - -class InodeAllocator { - public: - InodeAllocator(Transaction *transaction); - ~InodeAllocator(); - - status_t New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode); - status_t CreateTree(); - status_t Keep(); - - private: - Transaction *fTransaction; - block_run fRun; - Inode *fInode; -}; - - -InodeAllocator::InodeAllocator(Transaction *transaction) - : - fTransaction(transaction), - fInode(NULL) -{ -} - - -InodeAllocator::~InodeAllocator() -{ - if (fTransaction != NULL) { - if (fInode != NULL) { - fInode->Node()->flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE | INODE_NOT_READY); - // this unblocks any pending bfs_read_vnode() calls - fInode->Free(fTransaction); - } else - fTransaction->GetVolume()->Free(fTransaction, fRun); - } - - delete fInode; -} - - -status_t -InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode) -{ - Volume *volume = fTransaction->GetVolume(); - - status_t status = volume->AllocateForInode(fTransaction, parentRun, mode, fRun); - if (status < B_OK) { - // don't free the space in the destructor, because - // the allocation failed - fTransaction = NULL; - RETURN_ERROR(status); - } - - run = fRun; - fInode = new Inode(volume, volume->ToVnode(run), true); - if (fInode == NULL) - RETURN_ERROR(B_NO_MEMORY); - - // initialize the on-disk bfs_inode structure - - bfs_inode *node = fInode->Node(); - - node->magic1 = HOST_ENDIAN_TO_BFS_INT32(INODE_MAGIC1); - node->inode_num = run; - node->mode = HOST_ENDIAN_TO_BFS_INT32(mode); - node->flags = HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE | INODE_NOT_READY); - // INODE_NOT_READY prevents the inode from being opened - it is - // cleared in InodeAllocator::Keep() - node->etc = (uint32)fInode; - // this is temporarily set along INODE_NOT_READY and lets bfs_read_vnode() - // find the associated Inode object - - node->create_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL) << INODE_TIME_SHIFT); - node->last_modified_time = HOST_ENDIAN_TO_BFS_INT64(node->create_time - | (volume->GetUniqueID() & INODE_TIME_MASK)); - // we use Volume::GetUniqueID() to avoid having too many duplicates in the - // last_modified index - - node->inode_size = HOST_ENDIAN_TO_BFS_INT32(volume->InodeSize()); - - *_inode = fInode; - return B_OK; -} - - -status_t -InodeAllocator::CreateTree() -{ - Volume *volume = fTransaction->GetVolume(); - - // force S_STR_INDEX to be set, if no type is set - if ((fInode->Mode() & S_INDEX_TYPES) == 0) - fInode->Node()->mode |= HOST_ENDIAN_TO_BFS_INT32(S_STR_INDEX); - - BPlusTree *tree = fInode->fTree = new BPlusTree(fTransaction, fInode); - if (tree == NULL || tree->InitCheck() < B_OK) - return B_ERROR; - - if (fInode->IsRegularNode()) { - if (tree->Insert(fTransaction, ".", fInode->ID()) < B_OK - || tree->Insert(fTransaction, "..", volume->ToVnode(fInode->Parent())) < B_OK) - return B_ERROR; - } - return B_OK; -} - - -status_t -InodeAllocator::Keep() -{ - ASSERT(fInode != NULL && fTransaction != NULL); - - fInode->Node()->flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_NOT_READY); - status_t status = fInode->WriteBack(fTransaction); - - fTransaction = NULL; - fInode = NULL; - - return status; -} - - -// #pragma mark - - - -status_t -bfs_inode::InitCheck(Volume *volume) -{ - if (Flags() & INODE_NOT_READY) { - // the other fields may not yet contain valid values - return B_BUSY; - } - if (Flags() & INODE_DELETED) - return B_NOT_ALLOWED; - - if (Magic1() != INODE_MAGIC1 - || !(Flags() & INODE_IN_USE) - || inode_num.Length() != 1 - // matches inode size? - || (uint32)InodeSize() != volume->InodeSize() - // parent resides on disk? - || parent.AllocationGroup() > int32(volume->AllocationGroups()) - || parent.AllocationGroup() < 0 - || parent.Start() > (1L << volume->AllocationGroupShift()) - || parent.Length() != 1 - // attributes, too? - || attributes.AllocationGroup() > int32(volume->AllocationGroups()) - || attributes.AllocationGroup() < 0 - || attributes.Start() > (1L << volume->AllocationGroupShift())) - RETURN_ERROR(B_BAD_DATA); - - // ToDo: Add some tests to check the integrity of the other stuff here, - // especially for the data_stream! - - return B_OK; -} - - -// #pragma mark - - - -Inode::Inode(Volume *volume, vnode_id id, bool empty, uint8 reenter) - : CachedBlock(volume, volume->VnodeToBlock(id), empty), - fTree(NULL), - fLock() -{ - PRINT(("Inode::Inode(%p, %Ld, %s, %s) @ %p\n", - volume, id, empty ? "empty" : "not-empty", reenter ? "reenter":"not-reenter", this)); - - Initialize(); -} - - -Inode::Inode(CachedBlock *cached) - : CachedBlock(cached), - fTree(NULL), - fLock() -{ - PRINT(("Inode::Inode(%p) @ %p\n", cached, this)); - - Initialize(); -} - - -Inode::~Inode() -{ - PRINT(("Inode::~Inode() @ %p\n", this)); - - delete fTree; -} - - -void -Inode::Initialize() -{ - char lockName[32]; - sprintf(lockName, "bfs inode %ld.%d", BlockRun().AllocationGroup(), BlockRun().Start()); - fLock.Initialize(lockName); - - Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(INODE_PERMANENT_FLAGS); - - // these two will help to maintain the indices - fOldSize = Size(); - fOldLastModified = LastModified(); - - if (IsContainer()) - fTree = new BPlusTree(this); - - fCache = NULL; -} - - -status_t -Inode::InitCheck(bool checkNode) -{ - if (!Node()) - RETURN_ERROR(B_IO_ERROR); - - // test inode magic and flags - if (checkNode) { - status_t status = Node()->InitCheck(fVolume); - if (status == B_BUSY) - return B_BUSY; - - if (status < B_OK) { - FATAL(("inode at block %Ld corrupt!\n", fBlockNumber)); - RETURN_ERROR(B_BAD_DATA); - } - } - - if (IsContainer()) { - // inodes that have a - if (fTree == NULL) - RETURN_ERROR(B_NO_MEMORY); - - status_t status = fTree->InitCheck(); - if (status < B_OK) { - FATAL(("inode tree at block %Ld corrupt!\n", fBlockNumber)); - RETURN_ERROR(B_BAD_DATA); - } - } - - // it's more important to know that the inode is corrupt - // so we check for the lock not until here - return fLock.InitCheck(); -} - - -status_t -Inode::CheckPermissions(int accessMode) const -{ - uid_t user = geteuid(); - gid_t group = getegid(); - - // you never have write access to a read-only volume - if (accessMode & W_OK && fVolume->IsReadOnly()) - return B_READ_ONLY_DEVICE; - - // root users always have full access (but they can't execute anything) - if (user == 0 && !((accessMode & X_OK) && (Mode() & S_IXUSR) == 0)) - return B_OK; - - // shift mode bits, to check directly against accessMode - mode_t mode = Mode(); - if (user == (uid_t)Node()->UserID()) - mode >>= 6; - else if (group == (gid_t)Node()->GroupID()) - mode >>= 3; - - if (accessMode & ~(mode & S_IRWXO)) - return B_NOT_ALLOWED; - - return B_OK; -} - - -// #pragma mark - - - -void -Inode::AddIterator(AttributeIterator *iterator) -{ - if (fSmallDataLock.Lock() < B_OK) - return; - - fIterators.Add(iterator); - - fSmallDataLock.Unlock(); -} - - -void -Inode::RemoveIterator(AttributeIterator *iterator) -{ - if (fSmallDataLock.Lock() < B_OK) - return; - - fIterators.Remove(iterator); - - fSmallDataLock.Unlock(); -} - - -/** Tries to free up "bytes" space in the small_data section by moving - * attributes to real files. Used for system attributes like the name. - * You need to hold the fSmallDataLock when you call this method - */ - -status_t -Inode::MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 bytes) -{ - ASSERT(fSmallDataLock.IsLocked()); - - while (bytes > 0) { - small_data *item = Node()->SmallDataStart(), *max = NULL; - int32 index = 0, maxIndex = 0; - for (; !item->IsLast(Node()); item = item->Next(), index++) { - // should not remove those - if (*item->Name() == FILE_NAME_NAME || !strcmp(name, item->Name())) - continue; - - if (max == NULL || max->Size() < item->Size()) { - maxIndex = index; - max = item; - } - - // remove the first one large enough to free the needed amount of bytes - if (bytes < (int32)item->Size()) - break; - } - - if (item->IsLast(Node()) || (int32)item->Size() < bytes) - return B_ERROR; - - bytes -= max->Size(); - - // Move the attribute to a real attribute file - // Luckily, this doesn't cause any index updates - - Inode *attribute; - status_t status = CreateAttribute(transaction, item->Name(), item->Type(), &attribute); - if (status < B_OK) - RETURN_ERROR(status); - - size_t length = item->DataSize(); - status = attribute->WriteAt(transaction, 0, item->Data(), &length); - - ReleaseAttribute(attribute); - - if (status < B_OK) { - Vnode vnode(fVolume,Attributes()); - Inode *attributes; - if (vnode.Get(&attributes) < B_OK - || attributes->Remove(transaction, name) < B_OK) { - FATAL(("Could not remove newly created attribute!\n")); - } - - RETURN_ERROR(status); - } - - RemoveSmallData(max, maxIndex); - } - return B_OK; -} - - -/** Private function which removes the given attribute from the small_data - * section. - * You need to hold the fSmallDataLock when you call this method - */ - -status_t -Inode::RemoveSmallData(small_data *item, int32 index) -{ - ASSERT(fSmallDataLock.IsLocked()); - - small_data *next = item->Next(); - if (!next->IsLast(Node())) { - // find the last attribute - small_data *last = next; - while (!last->IsLast(Node())) - last = last->Next(); - - int32 size = (uint8 *)last - (uint8 *)next; - if (size < 0 || size > (uint8 *)Node() + fVolume->BlockSize() - (uint8 *)next) - return B_BAD_DATA; - - memmove(item, next, size); - - // Move the "last" one to its new location and - // correctly terminate the small_data section - last = (small_data *)((uint8 *)last - ((uint8 *)next - (uint8 *)item)); - memset(last, 0, (uint8 *)Node() + fVolume->BlockSize() - (uint8 *)last); - } else - memset(item, 0, item->Size()); - - // update all current iterators - AttributeIterator *iterator = NULL; - while ((iterator = fIterators.Next(iterator)) != NULL) - iterator->Update(index, -1); - - return B_OK; -} - - -/** Removes the given attribute from the small_data section. - * Note that you need to write back the inode yourself after having called - * that method. - */ - -status_t -Inode::RemoveSmallData(Transaction *transaction, const char *name) -{ - if (name == NULL) - return B_BAD_VALUE; - - SimpleLocker locker(fSmallDataLock); - - // search for the small_data item - - small_data *item = Node()->SmallDataStart(); - int32 index = 0; - while (!item->IsLast(Node()) && strcmp(item->Name(), name)) { - item = item->Next(); - index++; - } - - if (item->IsLast(Node())) - return B_ENTRY_NOT_FOUND; - - return RemoveSmallData(item, index); -} - - -/** Try to place the given attribute in the small_data section - if the - * new attribute is too big to fit in that section, it returns B_DEVICE_FULL. - * In that case, the attribute should be written to a real attribute file; - * if the attribute was already part of the small_data section, but the new - * one wouldn't fit, the old one is automatically removed from the small_data - * section. - * Note that you need to write back the inode yourself after having called that - * method - it's a bad API decision that it needs a transaction but enforces you - * to write back the inode all by yourself, but it's just more efficient in most - * cases... - */ - -status_t -Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, - const uint8 *data, size_t length, bool force) -{ - if (name == NULL || data == NULL || type == 0) - return B_BAD_VALUE; - - // reject any requests that can't fit into the small_data section - uint32 nameLength = strlen(name); - uint32 spaceNeeded = sizeof(small_data) + nameLength + 3 + length + 1; - if (spaceNeeded > fVolume->InodeSize() - sizeof(bfs_inode)) - return B_DEVICE_FULL; - - SimpleLocker locker(fSmallDataLock); - - small_data *item = Node()->SmallDataStart(); - int32 index = 0; - while (!item->IsLast(Node()) && strcmp(item->Name(), name)) { - item = item->Next(); - index++; - } - - // is the attribute already in the small_data section? - // then just replace the data part of that one - if (!item->IsLast(Node())) { - // find last attribute - small_data *last = item; - while (!last->IsLast(Node())) - last = last->Next(); - - // try to change the attributes value - if (item->data_size > length - || force - || ((uint8 *)last + length - item->DataSize()) <= ((uint8 *)Node() + fVolume->InodeSize())) { - // make room for the new attribute if needed (and we are forced to do so) - if (force - && ((uint8 *)last + length - item->DataSize()) > ((uint8 *)Node() + fVolume->InodeSize())) { - // We also take the free space at the end of the small_data section - // into account, and request only what's really needed - uint32 needed = length - item->DataSize() - - (uint32)((uint8 *)Node() + fVolume->InodeSize() - (uint8 *)last); - - if (MakeSpaceForSmallData(transaction, name, needed) < B_OK) - return B_ERROR; - - // reset our pointers - item = Node()->SmallDataStart(); - index = 0; - while (!item->IsLast(Node()) && strcmp(item->Name(), name)) { - item = item->Next(); - index++; - } - - last = item; - while (!last->IsLast(Node())) - last = last->Next(); - } - - // Normally, we can just overwrite the attribute data as the size - // is specified by the type and does not change that often - if (length != item->DataSize()) { - // move the attributes after the current one - small_data *next = item->Next(); - if (!next->IsLast(Node())) - memmove((uint8 *)item + spaceNeeded, next, (uint8 *)last - (uint8 *)next); - - // Move the "last" one to its new location and - // correctly terminate the small_data section - last = (small_data *)((uint8 *)last - ((uint8 *)next - ((uint8 *)item + spaceNeeded))); - if ((uint8 *)last < (uint8 *)Node() + fVolume->BlockSize()) - memset(last, 0, (uint8 *)Node() + fVolume->BlockSize() - (uint8 *)last); - - item->data_size = HOST_ENDIAN_TO_BFS_INT16(length); - } - - item->type = HOST_ENDIAN_TO_BFS_INT32(type); - memcpy(item->Data(), data, length); - item->Data()[length] = '\0'; - - return B_OK; - } - - // Could not replace the old attribute, so remove it to let - // let the calling function create an attribute file for it - if (RemoveSmallData(item, index) < B_OK) - return B_ERROR; - - return B_DEVICE_FULL; - } - - // try to add the new attribute! - - if ((uint8 *)item + spaceNeeded > (uint8 *)Node() + fVolume->InodeSize()) { - // there is not enough space for it! - if (!force) - return B_DEVICE_FULL; - - // make room for the new attribute - if (MakeSpaceForSmallData(transaction, name, spaceNeeded) < B_OK) - return B_ERROR; - - // get new last item! - item = Node()->SmallDataStart(); - index = 0; - while (!item->IsLast(Node())) { - item = item->Next(); - index++; - } - } - - memset(item, 0, spaceNeeded); - item->type = HOST_ENDIAN_TO_BFS_INT32(type); - item->name_size = HOST_ENDIAN_TO_BFS_INT16(nameLength); - item->data_size = HOST_ENDIAN_TO_BFS_INT16(length); - strcpy(item->Name(), name); - memcpy(item->Data(), data, length); - - // correctly terminate the small_data section - item = item->Next(); - if (!item->IsLast(Node())) - memset(item, 0, (uint8 *)Node() + fVolume->InodeSize() - (uint8 *)item); - - // update all current iterators - AttributeIterator *iterator = NULL; - while ((iterator = fIterators.Next(iterator)) != NULL) - iterator->Update(index, 1); - - return B_OK; -} - - -/** Iterates through the small_data section of an inode. - * To start at the beginning of this section, you let smallData - * point to NULL, like: - * small_data *data = NULL; - * while (inode->GetNextSmallData(&data) { ... } - * - * This function is reentrant and doesn't allocate any memory; - * you can safely stop calling it at any point (you don't need - * to iterate through the whole list). - * You need to hold the fSmallDataLock when you call this method - */ - -status_t -Inode::GetNextSmallData(small_data **_smallData) const -{ - if (!Node()) - RETURN_ERROR(B_ERROR); - - ASSERT(fSmallDataLock.IsLocked()); - - small_data *data = *_smallData; - - // begin from the start? - if (data == NULL) - data = Node()->SmallDataStart(); - else - data = data->Next(); - - // is already last item? - if (data->IsLast(Node())) - return B_ENTRY_NOT_FOUND; - - *_smallData = data; - - return B_OK; -} - - -/** Finds the attribute "name" in the small data section, and - * returns a pointer to it (or NULL if it doesn't exist). - * You need to hold the fSmallDataLock when you call this method - */ - -small_data * -Inode::FindSmallData(const char *name) const -{ - ASSERT(fSmallDataLock.IsLocked()); - - small_data *smallData = NULL; - while (GetNextSmallData(&smallData) == B_OK) { - if (!strcmp(smallData->Name(), name)) - return smallData; - } - return NULL; -} - - -/** Returns a pointer to the node's name if present in the small data - * section, NULL otherwise. - * You need to hold the fSmallDataLock when you call this method - */ - -const char * -Inode::Name() const -{ - ASSERT(fSmallDataLock.IsLocked()); - - small_data *smallData = NULL; - while (GetNextSmallData(&smallData) == B_OK) { - if (*smallData->Name() == FILE_NAME_NAME && smallData->NameSize() == FILE_NAME_NAME_LENGTH) - return (const char *)smallData->Data(); - } - return NULL; -} - - -/** Copies the node's name into the provided buffer. - * The buffer must be B_FILE_NAME_LENGTH bytes large. - */ - -status_t -Inode::GetName(char *buffer) const -{ - SimpleLocker locker(fSmallDataLock); - - const char *name = Name(); - if (name == NULL) - return B_ENTRY_NOT_FOUND; - - strlcpy(buffer, name, B_FILE_NAME_LENGTH); - return B_OK; -} - - -/** Changes or set the name of a file: in the inode small_data section only, it - * doesn't change it in the parent directory's b+tree. - * Note that you need to write back the inode yourself after having called - * that method. It suffers from the same API decision as AddSmallData() does - * (and for the same reason). - */ - -status_t -Inode::SetName(Transaction *transaction, const char *name) -{ - if (name == NULL || *name == '\0') - return B_BAD_VALUE; - - const char nameTag[2] = {FILE_NAME_NAME, 0}; - - return AddSmallData(transaction, nameTag, FILE_NAME_TYPE, (uint8 *)name, strlen(name), true); -} - - -/** Reads data from the specified attribute. - * This is a high-level attribute function that understands attributes - * in the small_data section as well as real attribute files. - */ - -status_t -Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, size_t *_length) -{ - if (pos < 0) - pos = 0; - - // search in the small_data section (which has to be locked first) - { - SimpleLocker locker(fSmallDataLock); - - small_data *smallData = FindSmallData(name); - if (smallData != NULL) { - size_t length = *_length; - if (pos >= smallData->data_size) { - *_length = 0; - return B_OK; - } - if (length + pos > smallData->DataSize()) - length = smallData->DataSize() - pos; - - memcpy(buffer, smallData->Data() + pos, length); - *_length = length; - return B_OK; - } - } - - // search in the attribute directory - Inode *attribute; - status_t status = GetAttribute(name, &attribute); - if (status == B_OK) { - if (attribute->Lock().Lock() == B_OK) { - status = attribute->ReadAt(pos, (uint8 *)buffer, _length); - attribute->Lock().Unlock(); - } else - status = B_ERROR; - - ReleaseAttribute(attribute); - } - - RETURN_ERROR(status); -} - - -/** Writes data to the specified attribute. - * This is a high-level attribute function that understands attributes - * in the small_data section as well as real attribute files. - */ - -status_t -Inode::WriteAttribute(Transaction *transaction, const char *name, int32 type, off_t pos, - const uint8 *buffer, size_t *_length) -{ - // needed to maintain the index - uint8 oldBuffer[BPLUSTREE_MAX_KEY_LENGTH], *oldData = NULL; - size_t oldLength = 0; - - // ToDo: we actually depend on that the contents of "buffer" are constant. - // If they get changed during the write (hey, user programs), we may mess - // up our index trees! - - Index index(fVolume); - index.SetTo(name); - - Inode *attribute = NULL; - status_t status = B_OK; - - if (GetAttribute(name, &attribute) < B_OK) { - // save the old attribute data - fSmallDataLock.Lock(); - - small_data *smallData = FindSmallData(name); - if (smallData != NULL) { - oldLength = smallData->DataSize(); - if (oldLength > 0) { - if (oldLength > BPLUSTREE_MAX_KEY_LENGTH) - oldLength = BPLUSTREE_MAX_KEY_LENGTH; - memcpy(oldData = oldBuffer, smallData->Data(), oldLength); - } - } - fSmallDataLock.Unlock(); - - // if the attribute doesn't exist yet (as a file), try to put it in the - // small_data section first - if that fails (due to insufficent space), - // create a real attribute file - status = AddSmallData(transaction, name, type, buffer, *_length); - if (status == B_DEVICE_FULL) { - status = CreateAttribute(transaction, name, type, &attribute); - if (status < B_OK) - RETURN_ERROR(status); - } else if (status == B_OK) - status = WriteBack(transaction); - } - - if (attribute != NULL) { - if (attribute->Lock().LockWrite() == B_OK) { - // save the old attribute data (if this fails, oldLength will reflect it) - if (fVolume->CheckForLiveQuery(name) && attribute->Size() > 0) { - oldLength = BPLUSTREE_MAX_KEY_LENGTH; - if (attribute->ReadAt(0, oldBuffer, &oldLength) == B_OK) - oldData = oldBuffer; - } - // ToDo: check if the data fits in the inode now and delete the attribute file if so - status = attribute->WriteAt(transaction, pos, buffer, _length); - if (status == B_OK) { - // The attribute type might have been changed - we need to adopt - // the new one - attribute->Node()->type = HOST_ENDIAN_TO_BFS_INT32(type); - status = attribute->WriteBack(transaction); - } - - attribute->Lock().UnlockWrite(); - } else - status = B_ERROR; - - ReleaseAttribute(attribute); - } - - // ToDo: find a better way than this "pos" thing (the begin of the old key - // must be copied to the start of the new one for a comparison) - if (status == B_OK && pos == 0) { - // index only the first BPLUSTREE_MAX_KEY_LENGTH bytes - uint16 length = *_length; - if (length > BPLUSTREE_MAX_KEY_LENGTH) - length = BPLUSTREE_MAX_KEY_LENGTH; - - // Update index. Note, Index::Update() may be called even if initializing - // the index failed - it will just update the live queries in this case - if (pos < length || pos < oldLength) - index.Update(transaction, name, type, oldData, oldLength, buffer, length, this); - } - return status; -} - - -/** Removes the specified attribute from the inode. - * This is a high-level attribute function that understands attributes - * in the small_data section as well as real attribute files. - */ - -status_t -Inode::RemoveAttribute(Transaction *transaction, const char *name) -{ - Index index(fVolume); - bool hasIndex = index.SetTo(name) == B_OK; - - // update index for attributes in the small_data section - { - fSmallDataLock.Lock(); - - small_data *smallData = FindSmallData(name); - if (smallData != NULL) { - uint32 length = smallData->DataSize(); - if (length > BPLUSTREE_MAX_KEY_LENGTH) - length = BPLUSTREE_MAX_KEY_LENGTH; - index.Update(transaction, name, smallData->Type(), smallData->Data(), length, NULL, 0, this); - } - fSmallDataLock.Unlock(); - } - - status_t status = RemoveSmallData(transaction, name); - if (status == B_OK) { - status = WriteBack(transaction); - } else if (status == B_ENTRY_NOT_FOUND && !Attributes().IsZero()) { - // remove the attribute file if it exists - Vnode vnode(fVolume, Attributes()); - Inode *attributes; - if ((status = vnode.Get(&attributes)) < B_OK) - return status; - - // update index - Inode *attribute; - if ((hasIndex || fVolume->CheckForLiveQuery(name)) - && GetAttribute(name, &attribute) == B_OK) { - uint8 data[BPLUSTREE_MAX_KEY_LENGTH]; - size_t length = BPLUSTREE_MAX_KEY_LENGTH; - if (attribute->ReadAt(0, data, &length) == B_OK) - index.Update(transaction, name, attribute->Type(), data, length, NULL, 0, this); - - ReleaseAttribute(attribute); - } - - if ((status = attributes->Remove(transaction, name)) < B_OK) - return status; - - if (attributes->IsEmpty()) { - // remove attribute directory (don't fail if that can't be done) - if (remove_vnode(fVolume->ID(), attributes->ID()) == B_OK) { - // update the inode, so that no one will ever doubt it's deleted :-) - attributes->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DELETED); - if (attributes->WriteBack(transaction) == B_OK) { - Attributes().SetTo(0, 0, 0); - WriteBack(transaction); - } else - unremove_vnode(fVolume->ID(), attributes->ID()); - } - } - } - return status; -} - - -status_t -Inode::GetAttribute(const char *name, Inode **_attribute) -{ - // does this inode even have attributes? - if (Attributes().IsZero()) - return B_ENTRY_NOT_FOUND; - - Vnode vnode(fVolume, Attributes()); - Inode *attributes; - if (vnode.Get(&attributes) < B_OK) { - FATAL(("get_vnode() failed in Inode::GetAttribute(name = \"%s\")\n", name)); - return B_ERROR; - } - - BPlusTree *tree; - status_t status = attributes->GetTree(&tree); - if (status == B_OK) { - vnode_id id; - if ((status = tree->Find((uint8 *)name, (uint16)strlen(name), &id)) == B_OK) { - Vnode vnode(fVolume, id); - Inode *inode; - // Check if the attribute is really an attribute - if (vnode.Get(&inode) < B_OK || !inode->IsAttribute()) - return B_ERROR; - - *_attribute = inode; - vnode.Keep(); - return B_OK; - } - } - return status; -} - - -void -Inode::ReleaseAttribute(Inode *attribute) -{ - if (attribute == NULL) - return; - - put_vnode(fVolume->ID(), attribute->ID()); -} - - -status_t -Inode::CreateAttribute(Transaction *transaction, const char *name, uint32 type, Inode **attribute) -{ - // do we need to create the attribute directory first? - if (Attributes().IsZero()) { - status_t status = Inode::Create(transaction, this, NULL, - S_ATTR_DIR | S_DIRECTORY | 0666, 0, 0, NULL); - if (status < B_OK) - RETURN_ERROR(status); - } - Vnode vnode(fVolume, Attributes()); - Inode *attributes; - if (vnode.Get(&attributes) < B_OK) - return B_ERROR; - - // Inode::Create() locks the inode for us - return Inode::Create(transaction, attributes, name, - S_ATTR | S_FILE | 0666, 0, type, NULL, attribute); -} - - -// #pragma mark - - - -/** Gives the caller direct access to the b+tree for a given directory. - * The tree is no longer created on demand, but when the inode is first - * created. That will report any potential errors upfront, saves locking, - * and should work as good (though a bit slower). - */ - -status_t -Inode::GetTree(BPlusTree **tree) -{ - if (fTree) { - *tree = fTree; - return B_OK; - } - - RETURN_ERROR(B_BAD_VALUE); -} - - -bool -Inode::IsEmpty() -{ - BPlusTree *tree; - status_t status = GetTree(&tree); - if (status < B_OK) - return status; - - TreeIterator iterator(tree); - - // index and attribute directories are really empty when they are - // empty - directories for standard files always contain ".", and - // "..", so we need to ignore those two - - uint32 count = 0; - char name[BPLUSTREE_MAX_KEY_LENGTH]; - uint16 length; - vnode_id id; - while (iterator.GetNextEntry(name, &length, B_FILE_NAME_LENGTH, &id) == B_OK) { - if (Mode() & (S_ATTR_DIR | S_INDEX_DIR)) - return false; - - if (++count > 2 || strcmp(".", name) && strcmp("..", name)) - return false; - } - return true; -} - - -/** Finds the block_run where "pos" is located in the data_stream of - * the inode. - * If successful, "offset" will then be set to the file offset - * of the block_run returned; so "pos - offset" is for the block_run - * what "pos" is for the whole stream. - * The caller has to make sure that "pos" is inside the stream. - */ - -status_t -Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset) -{ - // The BPlusTree class will call this function, we'll provide - // standard cached access only from here - return ((Stream *)this)->FindBlockRun(pos, run, offset); -} - - -status_t -Inode::ReadAt(off_t pos, uint8 *buffer, size_t *_length) -{ - // call the right ReadAt() method, depending on the inode flags - - if (Flags() & INODE_NO_CACHE) - return ((Stream *)this)->ReadAt(pos, buffer, _length); - - if (Flags() & INODE_LOGGED) - return ((Stream *)this)->ReadAt(pos, buffer, _length); - - return ((Stream *)this)->ReadAt(pos, buffer, _length); -} - - -status_t -Inode::WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *_length) -{ - // call the right WriteAt() method, depending on the inode flags - - // update the last modification time in memory, it will be written - // back to the inode, and the index when the file is closed - // ToDo: should update the internal last modified time only at this point! - Node()->last_modified_time = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; - - if (Flags() & INODE_NO_CACHE) - return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); - - if (Flags() & INODE_LOGGED) - return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); - - return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); -} - - -/** Fills the gap between the old file size and the new file size - * with zeros. - * It's more or less a copy of Inode::WriteAt() but it can handle - * length differences of more than just 4 GB, and it never uses - * the log, even if the INODE_LOGGED flag is set. - */ - -status_t -Inode::FillGapWithZeros(off_t pos, off_t newSize) -{ - // ToDo: we currently do anything here, same as original BFS! - //if (pos >= newSize) - return B_OK; - - block_run run; - off_t offset; - if (FindBlockRun(pos, run, offset) < B_OK) - RETURN_ERROR(B_BAD_VALUE); - - off_t length = newSize - pos; - uint32 bytesWritten = 0; - uint32 blockSize = fVolume->BlockSize(); - uint32 blockShift = fVolume->BlockShift(); - uint8 *block; - - // the first block_run we write could not be aligned to the block_size boundary - // (write partial block at the beginning) - - // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 - if (pos % blockSize != 0) { - run.start += (pos - offset) / blockSize; - run.length -= (pos - offset) / blockSize; - - CachedBlock cached(fVolume,run); - if ((block = cached.Block()) == NULL) - RETURN_ERROR(B_BAD_VALUE); - - bytesWritten = blockSize - (pos % blockSize); - if (length < bytesWritten) - bytesWritten = length; - - memset(block + (pos % blockSize), 0, bytesWritten); - if (fVolume->WriteBlocks(cached.BlockNumber(), block, 1) < B_OK) - RETURN_ERROR(B_IO_ERROR); - - pos += bytesWritten; - - length -= bytesWritten; - if (length == 0) - return B_OK; - - if (FindBlockRun(pos, run, offset) < B_OK) - RETURN_ERROR(B_BAD_VALUE); - } - - while (length > 0) { - // offset is the offset to the current pos in the block_run - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - CachedBlock cached(fVolume); - off_t blockNumber = fVolume->ToBlock(run); - for (int32 i = 0; i < run.Length(); i++) { - if ((block = cached.SetTo(blockNumber + i, true)) == NULL) - RETURN_ERROR(B_IO_ERROR); - - if (fVolume->WriteBlocks(cached.BlockNumber(), block, 1) < B_OK) - RETURN_ERROR(B_IO_ERROR); - } - - int32 bytes = run.Length() << blockShift; - length -= bytes; - bytesWritten += bytes; - - // since we don't respect a last partial block, length can be lower - if (length <= 0) - break; - - pos += bytes; - - if (FindBlockRun(pos, run, offset) < B_OK) - RETURN_ERROR(B_BAD_VALUE); - } - return B_OK; -} - - -/** Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing - * the indirect and double indirect range uses this method. - * The allocated block_run is saved in "run" - */ - -status_t -Inode::AllocateBlockArray(Transaction *transaction, block_run &run) -{ - if (!run.IsZero()) - return B_BAD_VALUE; - - status_t status = fVolume->Allocate(transaction, this, NUM_ARRAY_BLOCKS, run, NUM_ARRAY_BLOCKS); - if (status < B_OK) - return status; - - // make sure those blocks are empty - CachedBlock cached(fVolume); - off_t block = fVolume->ToBlock(run); - - for (int32 i = 0; i < run.Length(); i++) { - block_run *runs = (block_run *)cached.SetTo(block + i, true); - if (runs == NULL) - return B_IO_ERROR; - - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - } - return B_OK; -} - - -status_t -Inode::GrowStream(Transaction *transaction, off_t size) -{ - data_stream *data = &Node()->data; - - // is the data stream already large enough to hold the new size? - // (can be the case with preallocated blocks) - if (size < data->MaxDirectRange() - || size < data->MaxIndirectRange() - || size < data->MaxDoubleIndirectRange()) { - data->size = HOST_ENDIAN_TO_BFS_INT64(size); - return B_OK; - } - - // how many bytes are still needed? (unused ranges are always zero) - uint16 minimum = 1; - off_t bytes; - if (data->Size() < data->MaxDoubleIndirectRange()) { - bytes = size - data->MaxDoubleIndirectRange(); - // the double indirect range can only handle multiple of NUM_ARRAY_BLOCKS - minimum = NUM_ARRAY_BLOCKS; - } else if (data->Size() < data->MaxIndirectRange()) - bytes = size - data->MaxIndirectRange(); - else if (data->Size() < data->MaxDirectRange()) - bytes = size - data->MaxDirectRange(); - else - bytes = size - data->Size(); - - // do we have enough free blocks on the disk? - off_t blocksRequested = (bytes + fVolume->BlockSize() - 1) >> fVolume->BlockShift(); - if (blocksRequested > fVolume->FreeBlocks()) - return B_DEVICE_FULL; - - off_t blocksNeeded = blocksRequested; - // because of preallocations and partial allocations, the number of - // blocks we need to allocate may be different from the one we request - // from the block allocator - - // Should we preallocate some blocks (currently, always 64k)? - // Attributes, attribute directories, and long symlinks usually won't get that big, - // and should stay close to the inode - preallocating could be counterproductive. - // Also, if free disk space is tight, we probably don't want to do this as well. - if (!IsAttribute() && !IsAttributeDirectory() && !IsSymLink() - && blocksRequested < (65536 >> fVolume->BlockShift()) - && fVolume->FreeBlocks() > 128) - blocksRequested = 65536 >> fVolume->BlockShift(); - - while (blocksNeeded > 0) { - // the requested blocks do not need to be returned with a - // single allocation, so we need to iterate until we have - // enough blocks allocated - block_run run; - status_t status = fVolume->Allocate(transaction, this, blocksRequested, run, minimum); - if (status < B_OK) - return status; - - // okay, we have the needed blocks, so just distribute them to the - // different ranges of the stream (direct, indirect & double indirect) - - // ToDo: if anything goes wrong here, we probably want to free the - // blocks that couldn't be distributed into the stream! - - blocksNeeded -= run.Length(); - // don't preallocate if the first allocation was already too small - blocksRequested = blocksNeeded; - if (minimum > 1) { - // make sure that "blocks" is a multiple of minimum - blocksRequested = (blocksRequested + minimum - 1) & ~(minimum - 1); - } - - // Direct block range - - if (data->Size() <= data->MaxDirectRange()) { - // let's try to put them into the direct block range - int32 free = 0; - for (; free < NUM_DIRECT_BLOCKS; free++) - if (data->direct[free].IsZero()) - break; - - if (free < NUM_DIRECT_BLOCKS) { - // can we merge the last allocated run with the new one? - int32 last = free - 1; - if (free > 0 && data->direct[last].MergeableWith(run)) - data->direct[last].length = HOST_ENDIAN_TO_BFS_INT16(data->direct[last].Length() + run.Length()); - else - data->direct[free] = run; - - data->max_direct_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxDirectRange() + run.Length() * fVolume->BlockSize()); - data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0 ? data->max_direct_range : size); - continue; - } - } - - // Indirect block range - - if (data->Size() <= data->MaxIndirectRange() || !data->MaxIndirectRange()) { - CachedBlock cached(fVolume); - block_run *runs = NULL; - uint32 free = 0; - off_t block; - - // if there is no indirect block yet, create one - if (data->indirect.IsZero()) { - status = AllocateBlockArray(transaction, data->indirect); - if (status < B_OK) - return status; - - data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxDirectRange()); - // insert the block_run in the first block - runs = (block_run *)cached.SetTo(data->indirect); - } else { - uint32 numberOfRuns = fVolume->BlockSize() / sizeof(block_run); - block = fVolume->ToBlock(data->indirect); - - // search first empty entry - int32 i = 0; - for (; i < data->indirect.Length(); i++) { - if ((runs = (block_run *)cached.SetTo(block + i)) == NULL) - return B_IO_ERROR; - - for (free = 0; free < numberOfRuns; free++) - if (runs[free].IsZero()) - break; - - if (free < numberOfRuns) - break; - } - if (i == data->indirect.Length()) - runs = NULL; - } - - if (runs != NULL) { - // try to insert the run to the last one - note that this doesn't - // take block borders into account, so it could be further optimized - int32 last = free - 1; - if (free > 0 && runs[last].MergeableWith(run)) - runs[last].length = HOST_ENDIAN_TO_BFS_INT16(runs[last].Length() + run.Length()); - else - runs[free] = run; - - data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxIndirectRange() + (run.Length() << fVolume->BlockShift())); - data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0 ? data->MaxIndirectRange() : size); - - cached.WriteBack(transaction); - continue; - } - } - - // Double indirect block range - - if (data->Size() <= data->MaxDoubleIndirectRange() || !data->max_double_indirect_range) { - while ((run.Length() % NUM_ARRAY_BLOCKS) != 0) { - // The number of allocated blocks isn't a multiple of NUM_ARRAY_BLOCKS, - // so we have to change this. This can happen the first time the stream - // grows into the double indirect range. - // First, free the remaining blocks that don't fit into a multiple - // of NUM_ARRAY_BLOCKS - int32 rest = run.Length() % NUM_ARRAY_BLOCKS; - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - rest); - - status = fVolume->Free(transaction, block_run::Run(run.AllocationGroup(), - run.Start() + run.Length(), rest)); - if (status < B_OK) - return status; - - blocksNeeded += rest; - blocksRequested = (blocksNeeded + NUM_ARRAY_BLOCKS - 1) & ~(NUM_ARRAY_BLOCKS - 1); - minimum = NUM_ARRAY_BLOCKS; - // we make sure here that we have at minimum NUM_ARRAY_BLOCKS allocated, - // so if the allocation succeeds, we don't run into an endless loop - - // Are there any blocks left in the run? If not, allocate a new one - if (run.length == 0) - continue; - } - - // if there is no double indirect block yet, create one - if (data->double_indirect.IsZero()) { - status = AllocateBlockArray(transaction, data->double_indirect); - if (status < B_OK) - return status; - - data->max_double_indirect_range = data->max_indirect_range; - } - - // calculate the index where to insert the new blocks - - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - int32 indirectSize = ((1L << INDIRECT_BLOCKS_SHIFT) << fVolume->BlockShift()) - * runsPerBlock; - int32 directSize = NUM_ARRAY_BLOCKS << fVolume->BlockShift(); - int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT; - - off_t start = data->MaxDoubleIndirectRange() - data->MaxIndirectRange(); - int32 indirectIndex = start / indirectSize; - int32 index = start / directSize; - - // distribute the blocks to the array and allocate - // new array blocks when needed - - CachedBlock cached(fVolume); - CachedBlock cachedDirect(fVolume); - block_run *array = NULL; - uint32 runLength = run.Length(); - - // ToDo: the following code is commented - it could be used to - // preallocate all needed block arrays to see in advance if the - // allocation will succeed. - // I will probably remove it later, because it's no perfect solution - // either: if the allocation was broken up before (blocksNeeded != 0), - // it doesn't guarantee anything. - // And since failing in this case is not that common, it doesn't have - // to be optimized in that way. - // Anyway, I wanted to have it in CVS - all those lines, and they will - // be removed soon :-) -/* - // allocate new block arrays if needed - - off_t block = -1; - - for (int32 i = 0;i < needed;i++) { - // get the block to insert the run into - block = fVolume->ToBlock(data->double_indirect) + i + indirectIndex / runsPerBlock; - if (cached.BlockNumber() != block) - array = (block_run *)cached.SetTo(block); - - if (array == NULL) - return B_ERROR; - - status = AllocateBlockArray(transaction, array[i + indirectIndex % runsPerBlock]); - if (status < B_OK) - return status; - } -*/ - - while (run.length != 0) { - // get the indirect array block - if (array == NULL) { - if (cached.Block() != NULL - && cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - - array = (block_run *)cached.SetTo(fVolume->ToBlock(data->double_indirect) - + indirectIndex / runsPerBlock); - if (array == NULL) - return B_IO_ERROR; - } - - do { - // do we need a new array block? - if (array[indirectIndex % runsPerBlock].IsZero()) { - status = AllocateBlockArray(transaction, array[indirectIndex % runsPerBlock]); - if (status < B_OK) - return status; - } - - block_run *runs = (block_run *)cachedDirect.SetTo( - fVolume->ToBlock(array[indirectIndex % runsPerBlock]) - + index / runsPerBlock); - if (runs == NULL) - return B_IO_ERROR; - - do { - // insert the block_run into the array - runs[index % runsPerBlock] = run; - runs[index % runsPerBlock].length = HOST_ENDIAN_TO_BFS_INT16(NUM_ARRAY_BLOCKS); - - // alter the remaining block_run - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + NUM_ARRAY_BLOCKS); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - NUM_ARRAY_BLOCKS); - } while ((++index % runsPerBlock) != 0 && run.length); - - if (cachedDirect.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - } while ((index % runsPerArray) != 0 && run.length); - - if (++indirectIndex % runsPerBlock == 0) { - array = NULL; - index = 0; - } - } - - data->max_double_indirect_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxDoubleIndirectRange() + (runLength << fVolume->BlockShift())); - data->size = blocksNeeded > 0 ? HOST_ENDIAN_TO_BFS_INT64(data->max_double_indirect_range) : size; - - continue; - } - - RETURN_ERROR(EFBIG); - } - // update the size of the data stream - data->size = HOST_ENDIAN_TO_BFS_INT64(size); - - return B_OK; -} - - -status_t -Inode::FreeStaticStreamArray(Transaction *transaction, int32 level, block_run run, - off_t size, off_t offset, off_t &max) -{ - int32 indirectSize = 0; - if (level == 0) - indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + fVolume->BlockShift())) - * (fVolume->BlockSize() / sizeof(block_run)); - else if (level == 1) - indirectSize = 4 << fVolume->BlockShift(); - - off_t start; - if (size > offset) - start = size - offset; - else - start = 0; - - int32 index = start / indirectSize; - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - - CachedBlock cached(fVolume); - off_t blockNumber = fVolume->ToBlock(run); - - // set the file offset to the current block run - offset += (off_t)index * indirectSize; - - for (int32 i = index / runsPerBlock; i < run.Length(); i++) { - block_run *array = (block_run *)cached.SetTo(blockNumber + i); - if (array == NULL) - RETURN_ERROR(B_ERROR); - - for (index = index % runsPerBlock; index < runsPerBlock; index++) { - if (array[index].IsZero()) { - // we also want to break out of the outer loop - i = run.Length(); - break; - } - - status_t status = B_OK; - if (level == 0) - status = FreeStaticStreamArray(transaction, 1, array[index], size, offset, max); - else if (offset >= size) - status = fVolume->Free(transaction, array[index]); - else - max = HOST_ENDIAN_TO_BFS_INT64(offset + indirectSize); - - if (status < B_OK) - RETURN_ERROR(status); - - if (offset >= size) - array[index].SetTo(0, 0, 0); - - offset += indirectSize; - } - index = 0; - - cached.WriteBack(transaction); - } - return B_OK; -} - - -/** Frees all block_runs in the array which come after the specified size. - * It also trims the last block_run that contain the size. - * "offset" and "max" are maintained until the last block_run that doesn't - * have to be freed - after this, the values won't be correct anymore, but - * will still assure correct function for all subsequent calls. - * "max" is considered to be in file system byte order. - */ - -status_t -Inode::FreeStreamArray(Transaction *transaction, block_run *array, uint32 arrayLength, - off_t size, off_t &offset, off_t &max) -{ - off_t newOffset = offset; - uint32 i = 0; - for (; i < arrayLength; i++, offset = newOffset) { - if (array[i].IsZero()) - break; - - newOffset += (off_t)array[i].Length() << fVolume->BlockShift(); - if (newOffset <= size) - continue; - - block_run run = array[i]; - - // determine the block_run to be freed - if (newOffset > size && offset < size) { - // free partial block_run (and update the original block_run) - run.start = array[i].start + ((size - offset) >> fVolume->BlockShift()) + 1; - array[i].length = HOST_ENDIAN_TO_BFS_INT16(run.Start() - array[i].Start()); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - array[i].Length()); - - if (run.length == 0) - continue; - - // update maximum range - max = HOST_ENDIAN_TO_BFS_INT64(offset + ((off_t)array[i].Length() << fVolume->BlockShift())); - } else { - // free the whole block_run - array[i].SetTo(0, 0, 0); - - if ((off_t)BFS_ENDIAN_TO_HOST_INT64(max) > offset) - max = HOST_ENDIAN_TO_BFS_INT64(offset); - } - - if (fVolume->Free(transaction, run) < B_OK) - return B_IO_ERROR; - } - return B_OK; -} - - -status_t -Inode::ShrinkStream(Transaction *transaction, off_t size) -{ - data_stream *data = &Node()->data; - - if (data->MaxDoubleIndirectRange() > size) { - off_t *maxDoubleIndirect = &data->max_double_indirect_range; - // gcc 4 work-around: "error: cannot bind packed field - // 'data->data_stream::max_double_indirect_range' to 'off_t&'" - FreeStaticStreamArray(transaction, 0, data->double_indirect, size, - data->MaxIndirectRange(), *maxDoubleIndirect); - - if (size <= data->MaxIndirectRange()) { - fVolume->Free(transaction, data->double_indirect); - data->double_indirect.SetTo(0, 0, 0); - data->max_double_indirect_range = 0; - } - } - if (data->MaxIndirectRange() > size) { - CachedBlock cached(fVolume); - off_t block = fVolume->ToBlock(data->indirect); - off_t offset = data->MaxDirectRange(); - - for (int32 i = 0; i < data->indirect.Length(); i++) { - block_run *array = (block_run *)cached.SetTo(block + i); - if (array == NULL) - break; - - off_t *maxIndirect = &data->max_indirect_range; - // gcc 4 work-around: "error: cannot bind packed field - // 'data->data_stream::max_indirect_range' to 'off_t&'" - if (FreeStreamArray(transaction, array, fVolume->BlockSize() / sizeof(block_run), - size, offset, *maxIndirect) == B_OK) - cached.WriteBack(transaction); - } - if (data->max_direct_range == data->max_indirect_range) { - fVolume->Free(transaction, data->indirect); - data->indirect.SetTo(0, 0, 0); - data->max_indirect_range = 0; - } - } - if (data->MaxDirectRange() > size) { - off_t offset = 0; - off_t *maxDirect = &data->max_direct_range; - // gcc 4 work-around: "error: cannot bind packed field - // 'data->data_stream::max_indirect_range' to 'off_t&'" - FreeStreamArray(transaction, data->direct, NUM_DIRECT_BLOCKS, size, offset, - *maxDirect); - } - - data->size = HOST_ENDIAN_TO_BFS_INT64(size); - return B_OK; -} - - -status_t -Inode::SetFileSize(Transaction *transaction, off_t size) -{ - if (size < 0 - // uncached files can't be resized (Stream::WriteAt() specifically - // denies growing uncached files because of efficiency, so it had to be - // adapted if this ever changes [which will probably happen in OpenBeOS]). - || Flags() & INODE_NO_CACHE) - return B_BAD_VALUE; - - off_t oldSize = Size(); - - if (size == oldSize) - return B_OK; - - // should the data stream grow or shrink? - status_t status; - if (size > oldSize) { - status = GrowStream(transaction, size); - if (status < B_OK) { - // if the growing of the stream fails, the whole operation - // fails, so we should shrink the stream to its former size - ShrinkStream(transaction, oldSize); - } - } - else - status = ShrinkStream(transaction, size); - - if (status < B_OK) - return status; - - return WriteBack(transaction); -} - - -status_t -Inode::Append(Transaction *transaction, off_t bytes) -{ - return SetFileSize(transaction, Size() + bytes); -} - - -status_t -Inode::Trim(Transaction *transaction) -{ - status_t status = ShrinkStream(transaction, Size()); - if (status < B_OK) - return status; - - return WriteBack(transaction); -} - - -status_t -Inode::Free(Transaction *transaction) -{ - FUNCTION(); - - // Perhaps there should be an implementation of Inode::ShrinkStream() that - // just frees the data_stream, but doesn't change the inode (since it is - // freed anyway) - that would make an undelete command possible - status_t status = SetFileSize(transaction, 0); - if (status < B_OK) - return status; - - // Free all attributes, and remove their indices - { - // We have to limit the scope of AttributeIterator, so that its - // destructor is not called after the inode is deleted - AttributeIterator iterator(this); - - char name[B_FILE_NAME_LENGTH]; - uint32 type; - size_t length; - vnode_id id; - while ((status = iterator.GetNext(name, &length, &type, &id)) == B_OK) - RemoveAttribute(transaction, name); - } - - if (WriteBack(transaction) < B_OK) - return B_ERROR; - - return fVolume->Free(transaction, BlockRun()); -} - - -status_t -Inode::Sync() -{ - // We may also want to flush the attribute's data stream to - // disk here... (do we?) - - data_stream *data = &Node()->data; - status_t status; - - // flush direct range - - for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) { - if (data->direct[i].IsZero()) - return B_OK; - - status = flush_blocks(fVolume->Device(), fVolume->ToBlock(data->direct[i]), - data->direct[i].Length()); - if (status != B_OK) - return status; - } - - // flush indirect range - - if (data->max_indirect_range == 0) - return B_OK; - - CachedBlock cached(fVolume); - off_t block = fVolume->ToBlock(data->indirect); - int32 count = fVolume->BlockSize() / sizeof(block_run); - - for (int32 j = 0; j < data->indirect.Length(); j++) { - block_run *runs = (block_run *)cached.SetTo(block + j); - if (runs == NULL) - break; - - for (int32 i = 0; i < count; i++) { - if (runs[i].IsZero()) - return B_OK; - - status = flush_blocks(fVolume->Device(), fVolume->ToBlock(runs[i]), runs[i].Length()); - if (status != B_OK) - return status; - } - } - - // flush double indirect range - - if (data->max_double_indirect_range == 0) - return B_OK; - - off_t indirectBlock = fVolume->ToBlock(data->double_indirect); - - for (int32 l = 0; l < data->double_indirect.Length(); l++) { - block_run *indirectRuns = (block_run *)cached.SetTo(indirectBlock + l); - if (indirectRuns == NULL) - return B_FILE_ERROR; - - CachedBlock directCached(fVolume); - - for (int32 k = 0; k < count; k++) { - if (indirectRuns[k].IsZero()) - return B_OK; - - block = fVolume->ToBlock(indirectRuns[k]); - for (int32 j = 0; j < indirectRuns[k].Length(); j++) { - block_run *runs = (block_run *)directCached.SetTo(block + j); - if (runs == NULL) - return B_FILE_ERROR; - - for (int32 i = 0; i < count; i++) { - if (runs[i].IsZero()) - return B_OK; - - // ToDo: combine single block_runs to bigger ones when - // they are adjacent - status = flush_blocks(fVolume->Device(), fVolume->ToBlock(runs[i]), - runs[i].Length()); - if (status != B_OK) - return status; - } - } - } - } - return B_OK; -} - - -status_t -Inode::Remove(Transaction *transaction, const char *name, off_t *_id, bool isDirectory) -{ - BPlusTree *tree; - if (GetTree(&tree) != B_OK) - RETURN_ERROR(B_BAD_VALUE); - - RecursiveLocker locker(fVolume->Lock()); - - // does the file even exist? - off_t id; - if (tree->Find((uint8 *)name, (uint16)strlen(name), &id) < B_OK) - return B_ENTRY_NOT_FOUND; - - if (_id) - *_id = id; - - Vnode vnode(fVolume, id); - Inode *inode; - status_t status = vnode.Get(&inode); - if (status < B_OK) { - REPORT_ERROR(status); - return B_ENTRY_NOT_FOUND; - } - - // You can't unlink a mounted image or the VM file while it is being used - while - // this is not really necessary, it copies the behaviour of the original BFS - // and let you and me feel a little bit safer - if (inode->Flags() & INODE_NO_CACHE) - return B_NOT_ALLOWED; - - // Inode::IsContainer() is true also for indices (furthermore, the S_IFDIR - // bit is set for indices in BFS, not for attribute directories) - but you - // should really be able to do whatever you want with your indices - // without having to remove all files first :) - if (!inode->IsIndex()) { - // if it's not of the correct type, don't delete it! - if (inode->IsContainer() != isDirectory) - return isDirectory ? B_NOT_A_DIRECTORY : B_IS_A_DIRECTORY; - - // only delete empty directories - if (isDirectory && !inode->IsEmpty()) - return B_DIRECTORY_NOT_EMPTY; - } - - // remove_vnode() allows the inode to be accessed until the last put_vnode() - if (remove_vnode(fVolume->ID(), id) != B_OK) - return B_ERROR; - - if (tree->Remove(transaction, name, id) < B_OK) { - unremove_vnode(fVolume->ID(), id); - RETURN_ERROR(B_ERROR); - } - -#ifdef DEBUG - if (tree->Find((uint8 *)name, (uint16)strlen(name), &id) == B_OK) { - DIE(("deleted entry still there")); - } -#endif - - // update the inode, so that no one will ever doubt it's deleted :-) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DELETED); - inode->Node()->flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE); - - // In balance to the Inode::Create() method, the main indices - // are updated here (name, size, & last_modified) - - Index index(fVolume); - if (inode->IsRegularNode()) { - index.RemoveName(transaction, name, inode); - // If removing from the index fails, it is not regarded as a - // fatal error and will not be reported back! - // Deleted inodes won't be visible in queries anyway. - } - - if ((inode->Mode() & (S_FILE | S_SYMLINK)) != 0) { - if (inode->IsFile()) - index.RemoveSize(transaction, inode); - index.RemoveLastModified(transaction, inode); - } - - if (inode->WriteBack(transaction) < B_OK) - return B_ERROR; - - return B_OK; -} - - -/** Creates the inode with the specified parent directory, and automatically - * adds the created inode to that parent directory. If an attribute directory - * is created, it will also automatically added to the parent inode as such. - * However, the indices root node, and the regular root node won't be added - * to the superblock. - * It will also create the initial B+tree for the inode if it's a directory - * of any kind. - * If the "_id" or "_inode" variable is given and non-NULL to store the inode's - * ID, the inode stays locked - you have to call put_vnode() if you don't use it - * anymore. - */ - -status_t -Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 mode, - int omode, uint32 type, off_t *_id, Inode **_inode) -{ - FUNCTION(); - - block_run parentRun = parent ? parent->BlockRun() : block_run::Run(0, 0, 0); - Volume *volume = transaction->GetVolume(); - BPlusTree *tree = NULL; - - RecursiveLocker locker(volume->Lock()); - // ToDo: it would be nicer to only lock the parent directory, if possible - // (but that lock will already be held during any B+tree action) - - if (parent && (mode & S_ATTR_DIR) == 0 && parent->IsContainer()) { - // check if the file already exists in the directory - if (parent->GetTree(&tree) != B_OK) - RETURN_ERROR(B_BAD_VALUE); - - // does the file already exist? - off_t offset; - if (tree->Find((uint8 *)name, (uint16)strlen(name), &offset) == B_OK) { - // return if the file should be a directory or opened in exclusive mode - if (mode & S_DIRECTORY || omode & O_EXCL) - return B_FILE_EXISTS; - - Vnode vnode(volume, offset); - Inode *inode; - status_t status = vnode.Get(&inode); - if (status < B_OK) { - REPORT_ERROR(status); - return B_ENTRY_NOT_FOUND; - } - - // if it's a directory, bail out! - if (inode->IsDirectory()) - return B_IS_A_DIRECTORY; - - // if it is a mounted device or the VM file, we don't allow to delete it - // while it is open and in use - if (inode->Flags() & INODE_NO_CACHE) - return B_NOT_ALLOWED; - - // if omode & O_TRUNC, truncate the existing file - if (omode & O_TRUNC) { - WriteLocked locked(inode->Lock()); - - status_t status = inode->SetFileSize(transaction, 0); - if (status < B_OK) - return status; - } - - if (_id) - *_id = offset; - if (_inode) - *_inode = inode; - - // only keep the vnode in memory if the _id or _inode pointer is provided - if (_id != NULL || _inode != NULL) - vnode.Keep(); - - return B_OK; - } - } else if (parent && (mode & S_ATTR_DIR) == 0) - return B_BAD_VALUE; - - // allocate space for the new inode - InodeAllocator allocator(transaction); - block_run run; - Inode *inode; - status_t status = allocator.New(&parentRun, mode, run, &inode); - if (status < B_OK) - return status; - - // Initialize the parts of the bfs_inode structure that - // InodeAllocator::New() hasn't touched yet - - bfs_inode *node = inode->Node(); - - if (parent == NULL) { - // we set the parent to itself in this case - // (only happens for the root and indices node) - node->parent = run; - } else - node->parent = parentRun; - - node->uid = HOST_ENDIAN_TO_BFS_INT32(geteuid()); - node->gid = HOST_ENDIAN_TO_BFS_INT32(parent ? parent->Node()->gid : getegid()); - // the group ID is inherited from the parent, if available - - node->type = HOST_ENDIAN_TO_BFS_INT32(type); - - // only add the name to regular files, directories, or symlinks - // don't add it to attributes, or indices - if (tree && inode->IsRegularNode() && inode->SetName(transaction, name) < B_OK) - return B_ERROR; - - // Initialize b+tree if it's a directory (and add "." & ".." if it's - // a standard directory for files - not for attributes or indices) - if (inode->IsContainer()) { - status = allocator.CreateTree(); - if (status < B_OK) - return status; - } - - // Add a link to the inode from the parent, depending on its type - // (the INODE_NOT_READY flag is set, so it is safe to make the inode - // accessable to the file system here) - if (tree) { - status = tree->Insert(transaction, name, inode->ID()); - } else if (parent && (mode & S_ATTR_DIR) != 0) { - parent->Attributes() = run; - status = parent->WriteBack(transaction); - } - - // Note, we only care if the inode could be made accessable for the - // two cases above; the root node or the indices root node must - // handle this case on their own (or other cases where "parent" is - // NULL) - if (status < B_OK) - RETURN_ERROR(status); - - // Update the main indices (name, size & last_modified) - // (live queries might want to access us after this) - - Index index(volume); - if (inode->IsRegularNode() && name != NULL) { - // the name index only contains regular files - // (but not the root node where name == NULL) - status = index.InsertName(transaction, name, inode); - if (status < B_OK && status != B_BAD_INDEX) { - // We have to remove the node from the parent at this point, - // because the InodeAllocator destructor can't handle this - // case (and if it fails, we can't do anything about it...) - if (tree) - tree->Remove(transaction, name, inode->ID()); - else if (parent != NULL && (mode & S_ATTR_DIR) != 0) - parent->Node()->attributes.SetTo(0, 0, 0); - - return status; - } - } - - inode->UpdateOldLastModified(); - - // The "size" & "last_modified" indices don't contain directories - if (inode->IsFile() || inode->IsSymLink()) { - // if adding to these indices fails, the inode creation will not be harmed; - // they are considered less important than the "name" index - if (inode->IsFile()) - index.InsertSize(transaction, inode); - index.InsertLastModified(transaction, inode); - } - - // Everything worked well until this point, we have a fully - // initialized inode, and we want to keep it - allocator.Keep(); - - // We hold the volume lock to make sure that bfs_read_vnode() - // won't succeed in the meantime (between the call right - // above and below)! - - if ((status = new_vnode(volume->ID(), inode->ID(), inode)) != B_OK) { - // this is a really fatal error, and we can't recover from that - // The only exception is that the volume is being initialized. - if (volume->ID() >= 0) { - FATAL(("new_vnode() failed with: %s\n", strerror(status))); - DIE(("new_vnode() failed for inode!")); - } - } - - if (_id != NULL) - *_id = inode->ID(); - if (_inode != NULL) - *_inode = inode; - - // if either _id or _inode is passed, we will keep the inode locked - if (_id == NULL && _inode == NULL) - put_vnode(volume->ID(), inode->ID()); - - return B_OK; -} - - -// #pragma mark - - - -AttributeIterator::AttributeIterator(Inode *inode) - : - fCurrentSmallData(0), - fInode(inode), - fAttributes(NULL), - fIterator(NULL), - fBuffer(NULL) -{ - inode->AddIterator(this); -} - - -AttributeIterator::~AttributeIterator() -{ - if (fAttributes) - put_vnode(fAttributes->GetVolume()->ID(), fAttributes->ID()); - - delete fIterator; - fInode->RemoveIterator(this); -} - - -status_t -AttributeIterator::Rewind() -{ - fCurrentSmallData = 0; - - if (fIterator != NULL) - fIterator->Rewind(); - - return B_OK; -} - - -status_t -AttributeIterator::GetNext(char *name, size_t *_length, uint32 *_type, vnode_id *_id) -{ - // read attributes out of the small data section - - if (fCurrentSmallData >= 0) { - small_data *item = fInode->Node()->SmallDataStart(); - - fInode->SmallDataLock().Lock(); - - int32 i = 0; - for (;;item = item->Next()) { - if (item->IsLast(fInode->Node())) - break; - - if (item->NameSize() == FILE_NAME_NAME_LENGTH - && *item->Name() == FILE_NAME_NAME) - continue; - - if (i++ == fCurrentSmallData) - break; - } - - if (!item->IsLast(fInode->Node())) { - strncpy(name, item->Name(), B_FILE_NAME_LENGTH); - *_type = item->Type(); - *_length = item->NameSize(); - *_id = (vnode_id)fCurrentSmallData; - - fCurrentSmallData = i; - } - else { - // stop traversing the small_data section - fCurrentSmallData = -1; - } - - fInode->SmallDataLock().Unlock(); - - if (fCurrentSmallData != -1) - return B_OK; - } - - // read attributes out of the attribute directory - - if (fInode->Attributes().IsZero()) - return B_ENTRY_NOT_FOUND; - - Volume *volume = fInode->GetVolume(); - - // if you haven't yet access to the attributes directory, get it - if (fAttributes == NULL) { -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - if (get_vnode(volume->ID(), volume->ToVnode(fInode->Attributes()), - (void **)&fAttributes) != 0 - || fAttributes == NULL) { - FATAL(("get_vnode() failed in AttributeIterator::GetNext(vnode_id = %Ld,name = \"%s\")\n",fInode->ID(),name)); - return B_ENTRY_NOT_FOUND; - } - - BPlusTree *tree; - if (fAttributes->GetTree(&tree) < B_OK - || (fIterator = new TreeIterator(tree)) == NULL) { - FATAL(("could not get tree in AttributeIterator::GetNext(vnode_id = %Ld,name = \"%s\")\n",fInode->ID(),name)); - return B_ENTRY_NOT_FOUND; - } - } - - uint16 length; - vnode_id id; - status_t status = fIterator->GetNextEntry(name, &length, B_FILE_NAME_LENGTH, &id); - if (status < B_OK) - return status; - - Vnode vnode(volume,id); - Inode *attribute; - if ((status = vnode.Get(&attribute)) == B_OK) { - *_type = attribute->Type(); - *_length = attribute->Size(); - *_id = id; - } - - return status; -} - - -void -AttributeIterator::Update(uint16 index, int8 change) -{ - // fCurrentSmallData points already to the next item - if (index < fCurrentSmallData) - fCurrentSmallData += change; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Inode.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Inode.h deleted file mode 100644 index 6633596638..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Inode.h +++ /dev/null @@ -1,402 +0,0 @@ -/* Inode - inode access functions - * - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de - * This file may be used under the terms of the MIT License. - */ -#ifndef INODE_H -#define INODE_H - - -#include -#ifdef USER -# include -#endif - -#include "cache.h" -#include "fsproto.h" - -#include -#include - -#include "Volume.h" -#include "Journal.h" -#include "Lock.h" -#include "Chain.h" -#include "Debug.h" - - -class BPlusTree; -class TreeIterator; -class AttributeIterator; -class InodeAllocator; - - -enum inode_type { - S_DIRECTORY = S_IFDIR, - S_FILE = S_IFREG, - S_SYMLINK = S_IFLNK, - - S_INDEX_TYPES = (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX - | S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX) -}; - - -// The CachedBlock class is completely implemented as inlines. -// It should be used when cache single blocks to make sure they -// will be properly released after use (and it's also very -// convenient to use them). - -class CachedBlock { - public: - CachedBlock(Volume *volume); - CachedBlock(Volume *volume, off_t block, bool empty = false); - CachedBlock(Volume *volume, block_run run, bool empty = false); - CachedBlock(CachedBlock *cached); - ~CachedBlock(); - - inline void Keep(); - inline void Unset(); - inline uint8 *SetTo(off_t block, bool empty = false); - inline uint8 *SetTo(block_run run, bool empty = false); - inline status_t WriteBack(Transaction *transaction); - - uint8 *Block() const { return fBlock; } - off_t BlockNumber() const { return fBlockNumber; } - uint32 BlockSize() const { return fVolume->BlockSize(); } - uint32 BlockShift() const { return fVolume->BlockShift(); } - - private: - CachedBlock(const CachedBlock &); - CachedBlock &operator=(const CachedBlock &); - // no implementation - - protected: - Volume *fVolume; - off_t fBlockNumber; - uint8 *fBlock; -}; - -//-------------------------------------- - -class Inode : public CachedBlock { - public: - Inode(Volume *volume, vnode_id id, bool empty = false, uint8 reenter = 0); - Inode(CachedBlock *cached); - ~Inode(); - - bfs_inode *Node() const { return (bfs_inode *)fBlock; } - vnode_id ID() const { return fVolume->ToVnode(fBlockNumber); } - - ReadWriteLock &Lock() { return fLock; } - SimpleLock &SmallDataLock() { return fSmallDataLock; } - - mode_t Mode() const { return Node()->Mode(); } - uint32 Type() const { return Node()->Type(); } - int32 Flags() const { return Node()->Flags(); } - bool IsContainer() const { return Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR); } - // note, that this test will also be true for S_IFBLK (not that it's used in the fs :) - bool IsDirectory() const { return (Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY; } - bool IsIndex() const { return (Mode() & (S_INDEX_DIR | 0777)) == S_INDEX_DIR; } - // that's a stupid check, but AFAIK the only possible method... - - bool IsDeleted() const { return (Flags() & INODE_DELETED) != 0; } - - bool IsAttributeDirectory() const { return (Mode() & S_ATTR_DIR) != 0; } - bool IsAttribute() const { return Mode() & S_ATTR; } - bool IsFile() const { return (Mode() & (S_IFMT | S_ATTR)) == S_FILE; } - bool IsRegularNode() const { return (Mode() & (S_ATTR_DIR | S_INDEX_DIR | S_ATTR)) == 0; } - // a regular node in the standard namespace (i.e. not an index or attribute) - bool IsSymLink() const { return S_ISLNK(Mode()); } - bool HasUserAccessableStream() const { return IsFile(); } - // currently only files can be accessed with bfs_read()/bfs_write() - - off_t Size() const { return Node()->data.Size(); } - off_t LastModified() const { return Node()->last_modified_time; } - - block_run &BlockRun() const { return Node()->inode_num; } - block_run &Parent() const { return Node()->parent; } - block_run &Attributes() const { return Node()->attributes; } - Volume *GetVolume() const { return fVolume; } - - status_t InitCheck(bool checkNode = true); - - status_t CheckPermissions(int accessMode) const; - - // small_data access methods - status_t MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 length); - status_t RemoveSmallData(Transaction *transaction, const char *name); - status_t AddSmallData(Transaction *transaction, const char *name, uint32 type, - const uint8 *data, size_t length, bool force = false); - status_t GetNextSmallData(small_data **_smallData) const; - small_data *FindSmallData(const char *name) const; - const char *Name() const; - status_t GetName(char *buffer) const; - status_t SetName(Transaction *transaction, const char *name); - - // high-level attribute methods - status_t ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, size_t *_length); - status_t WriteAttribute(Transaction *transaction, const char *name, int32 type, off_t pos, const uint8 *buffer, size_t *_length); - status_t RemoveAttribute(Transaction *transaction, const char *name); - - // attribute methods - status_t GetAttribute(const char *name, Inode **attribute); - void ReleaseAttribute(Inode *attribute); - status_t CreateAttribute(Transaction *transaction, const char *name, uint32 type, Inode **attribute); - - // for directories only: - status_t GetTree(BPlusTree **); - bool IsEmpty(); - - // manipulating the data stream - status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); - - status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); - status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length); - status_t FillGapWithZeros(off_t oldSize, off_t newSize); - - status_t SetFileSize(Transaction *transaction, off_t size); - status_t Append(Transaction *transaction, off_t bytes); - status_t Trim(Transaction *transaction); - - status_t Free(Transaction *transaction); - status_t Sync(); - - // create/remove inodes - status_t Remove(Transaction *transaction, const char *name, off_t *_id = NULL, - bool isDirectory = false); - static status_t Create(Transaction *transaction, Inode *parent, const char *name, - int32 mode, int omode, uint32 type, off_t *_id = NULL, Inode **_inode = NULL); - - // index maintaining helper - void UpdateOldSize() { fOldSize = Size(); } - void UpdateOldLastModified() { fOldLastModified = Node()->LastModifiedTime(); } - off_t OldSize() { return fOldSize; } - off_t OldLastModified() { return fOldLastModified; } - - // file cache - void *FileCache() const { return fCache; } - void SetFileCache(void *cache) { fCache = cache; } - - private: - Inode(const Inode &); - Inode &operator=(const Inode &); - // no implementation - - friend void dump_inode(Inode &inode); - friend class AttributeIterator; - friend class InodeAllocator; - - void Initialize(); - - status_t RemoveSmallData(small_data *item, int32 index); - - void AddIterator(AttributeIterator *iterator); - void RemoveIterator(AttributeIterator *iterator); - - status_t FreeStaticStreamArray(Transaction *transaction, int32 level, block_run run, - off_t size, off_t offset, off_t &max); - status_t FreeStreamArray(Transaction *transaction, block_run *array, uint32 arrayLength, - off_t size, off_t &offset, off_t &max); - status_t AllocateBlockArray(Transaction *transaction, block_run &run); - status_t GrowStream(Transaction *transaction, off_t size); - status_t ShrinkStream(Transaction *transaction, off_t size); - - BPlusTree *fTree; - Inode *fAttributes; - ReadWriteLock fLock; - off_t fOldSize; // we need those values to ensure we will remove - off_t fOldLastModified; // the correct keys from the indices - void *fCache; - - mutable SimpleLock fSmallDataLock; - Chain fIterators; -}; - - -// The Vnode class provides a convenience layer upon get_vnode(), so that -// you don't have to call put_vnode() anymore, which may make code more -// readable in some cases - -class Vnode { - public: - Vnode(Volume *volume, vnode_id id) - : - fVolume(volume), - fID(id) - { - } - - Vnode(Volume *volume, block_run run) - : - fVolume(volume), - fID(volume->ToVnode(run)) - { - } - - ~Vnode() - { - Put(); - } - - status_t Get(Inode **inode) - { - // should we check inode against NULL here? it should not be necessary -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(fVolume->Lock()); -#endif - return get_vnode(fVolume->ID(), fID, (void **)inode); - } - - void Put() - { - if (fVolume) - put_vnode(fVolume->ID(), fID); - fVolume = NULL; - } - - void Keep() - { - fVolume = NULL; - } - - private: - Volume *fVolume; - vnode_id fID; -}; - - -class AttributeIterator { - public: - AttributeIterator(Inode *inode); - ~AttributeIterator(); - - status_t Rewind(); - status_t GetNext(char *name, size_t *length, uint32 *type, vnode_id *id); - - private: - int32 fCurrentSmallData; - Inode *fInode, *fAttributes; - TreeIterator *fIterator; - void *fBuffer; - - private: - friend class Chain; - friend class Inode; - - void Update(uint16 index, int8 change); - AttributeIterator *fNext; -}; - - -//-------------------------------------- -// inlines - - -inline -CachedBlock::CachedBlock(Volume *volume) - : - fVolume(volume), - fBlockNumber(-1), - fBlock(NULL) -{ -} - - -inline -CachedBlock::CachedBlock(Volume *volume, off_t block, bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(block, empty); -} - - -inline -CachedBlock::CachedBlock(Volume *volume, block_run run, bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(volume->ToBlock(run), empty); -} - - -inline -CachedBlock::CachedBlock(CachedBlock *cached) - : - fVolume(cached->fVolume), - fBlockNumber(cached->BlockNumber()), - fBlock(cached->fBlock) -{ - cached->Keep(); -} - - -inline -CachedBlock::~CachedBlock() -{ - Unset(); -} - - -inline void -CachedBlock::Keep() -{ - fBlock = NULL; -} - - -inline void -CachedBlock::Unset() -{ - if (fBlock != NULL) - release_block(fVolume->Device(), fBlockNumber); -} - - -inline uint8 * -CachedBlock::SetTo(off_t block, bool empty) -{ - Unset(); - fBlockNumber = block; - return fBlock = empty ? (uint8 *)get_empty_block(fVolume->Device(), block, BlockSize()) - : (uint8 *)get_block(fVolume->Device(), block, BlockSize()); -} - - -inline uint8 * -CachedBlock::SetTo(block_run run, bool empty) -{ - return SetTo(fVolume->ToBlock(run), empty); -} - - -inline status_t -CachedBlock::WriteBack(Transaction *transaction) -{ - if (transaction == NULL || fBlock == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return transaction->WriteBlocks(fBlockNumber, fBlock); -} - - -/** Converts the "omode", the open flags given to bfs_open(), into - * access modes, e.g. since O_RDONLY requires read access to the - * file, it will be converted to R_OK. - */ - -inline int -oModeToAccess(int omode) -{ - omode &= O_RWMASK; - if (omode == O_RDONLY) - return R_OK; - else if (omode == O_WRONLY) - return W_OK; - - return R_OK | W_OK; -} - -#endif /* INODE_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Jamfile b/src/tests/add-ons/kernel/file_systems/bfs/r5/Jamfile deleted file mode 100644 index 7a22934a15..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Jamfile +++ /dev/null @@ -1,80 +0,0 @@ -SubDir HAIKU_TOP src tests add-ons kernel file_systems bfs r5 ; - -SetSubDirSupportedPlatformsBeOSCompatible ; - -# save original optimization level -oldOPTIM = $(OPTIM) ; - -# set some additional defines -{ - local defines = - KEEP_WRONG_DIRENT_RECLEN - UNSAFE_GET_VNODE - #BFS_BIG_ENDIAN_ONLY - ; - - # By default, the R5 API version is used unless you define this - if $(COMPILE_FOR_ZETA) { - defines += COMPILE_FOR_ZETA ; - } - - # Enable OpenBFS to be compiled as a full BFS replacement. Will - # report itself as "bfs" instead of "obfs" (only R5 version) - if $(BFS_REPLACEMENT) { - defines += BFS_REPLACEMENT ; - bfsAddOnName = bfs ; - } else { - bfsAddOnName = obfs ; - } - - if $(DEBUG) = 0 { - # the gcc on BeOS doesn't compile BFS correctly with -O2 or more - OPTIM = -O1 ; - } - - defines = [ FDefines $(defines) ] ; - SubDirCcFlags $(defines) ; - SubDirC++Flags $(defines) -fno-rtti ; -} - -UsePrivateHeaders [ FDirName kernel ] ; # For kernel_cpp.cpp - -KernelStaticLibrary libbfs : - BlockAllocator.cpp - BPlusTree.cpp - kernel_cpp.cpp - Debug.cpp - Index.cpp - Inode.cpp - Journal.cpp - Query.cpp - Utility.cpp - Volume.cpp - BufferPool.cpp - ; - -KernelAddon $(bfsAddOnName)_r5 : - kernel_interface_r5.cpp - : libbfs.a - ; - -SEARCH on [ FGristFiles - kernel_cpp.cpp - ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; - -#----------------------------------------------------- - -rule InstallBFS -{ - Depends $(<) : $(>) ; -} - -actions ignore InstallBFS -{ - cp $(>) /boot/home/config/add-ons/kernel/file_systems/ -} - -InstallBFS install : obfs ; - -# restore original optimization level -OPTIM = $(oldOPTIM) ; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Journal.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/Journal.cpp deleted file mode 100644 index 1656afae5d..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Journal.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de - * This file may be used under the terms of the MIT License. - */ - -//! Transaction and logging - -#include "Journal.h" -#include "Inode.h" - -#include -#include - - -struct run_array { - int32 count; - int32 max_runs; - block_run runs[0]; - - int32 CountRuns() const { return BFS_ENDIAN_TO_HOST_INT32(count); } - int32 MaxRuns() const { return BFS_ENDIAN_TO_HOST_INT32(max_runs); } - const block_run &RunAt(int32 i) const { return runs[i]; } - - static int32 MaxRuns(int32 blockSize) - { return (blockSize - sizeof(run_array)) / sizeof(block_run); } -}; - -class LogEntry : public DoublyLinkedListLinkImpl { - public: - LogEntry(Journal *journal, uint32 logStart); - ~LogEntry(); - - status_t InitCheck() const { return fArray != NULL ? B_OK : B_NO_MEMORY; } - - uint32 Start() const { return fStart; } - uint32 Length() const { return fLength; } - - Journal *GetJournal() { return fJournal; } - - bool InsertBlock(off_t blockNumber); - bool NotifyBlocks(int32 count); - - run_array *Array() const { return fArray; } - int32 CountRuns() const { return fArray->CountRuns(); } - int32 MaxRuns() const { return fArray->MaxRuns() - 1; } - // the -1 is an off-by-one error in Be's BFS implementation - const block_run &RunAt(int32 i) const { return fArray->RunAt(i); } - - private: - Journal *fJournal; - uint32 fStart; - uint32 fLength; - uint32 fCachedBlocks; - run_array *fArray; -}; - - -// #pragma mark - - - -LogEntry::LogEntry(Journal *journal, uint32 start) - : - fJournal(journal), - fStart(start), - fLength(1), - fCachedBlocks(0) -{ - int32 blockSize = fJournal->GetVolume()->BlockSize(); - fArray = (run_array *)malloc(blockSize); - if (fArray == NULL) - return; - - memset(fArray, 0, blockSize); - fArray->max_runs = HOST_ENDIAN_TO_BFS_INT32(run_array::MaxRuns(blockSize)); -} - - -LogEntry::~LogEntry() -{ - free(fArray); -} - - -/** Adds the specified block into the array. - */ - -bool -LogEntry::InsertBlock(off_t blockNumber) -{ - // Be's BFS log replay routine can only deal with block_runs of size 1 - // A pity, isn't it? Too sad we have to be compatible. - - if (CountRuns() >= MaxRuns()) - return false; - - block_run run = fJournal->GetVolume()->ToBlockRun(blockNumber); - - fArray->runs[CountRuns()] = run; - fArray->count = HOST_ENDIAN_TO_BFS_INT32(CountRuns() + 1); - fLength++; - fCachedBlocks++; - return true; -} - - -bool -LogEntry::NotifyBlocks(int32 count) -{ - fCachedBlocks -= count; - return fCachedBlocks == 0; -} - - -// #pragma mark - - - -Journal::Journal(Volume *volume) - : - fVolume(volume), - fLock("bfs journal"), - fOwner(NULL), - fArray(volume->BlockSize()), - fLogSize(volume->Log().Length()), - fMaxTransactionSize(fLogSize / 4 - 5), - fUsed(0), - fTransactionsInEntry(0) -{ - if (fMaxTransactionSize > fLogSize / 2) - fMaxTransactionSize = fLogSize / 2 - 5; -} - - -Journal::~Journal() -{ - FlushLogAndBlocks(); -} - - -status_t -Journal::InitCheck() -{ - if (fVolume->LogStart() != fVolume->LogEnd()) { - if (fVolume->SuperBlock().flags != SUPER_BLOCK_DISK_DIRTY) - FATAL(("log_start and log_end differ, but disk is marked clean - trying to replay log...\n")); - - return ReplayLog(); - } - - return B_OK; -} - - -status_t -Journal::_CheckRunArray(const run_array *array) -{ - int32 maxRuns = run_array::MaxRuns(fVolume->BlockSize()); - if (array->MaxRuns() != maxRuns - || array->CountRuns() > maxRuns - || array->CountRuns() <= 0) { - FATAL(("Log entry has broken header!\n")); - return B_ERROR; - } - - for (int32 i = 0; i < array->CountRuns(); i++) { - if (fVolume->ValidateBlockRun(array->RunAt(i)) != B_OK) - return B_ERROR; - } - - PRINT(("Log entry has %ld entries (%Ld)\n", array->CountRuns())); - return B_OK; -} - - -/** Replays an entry in the log. - * \a _start points to the entry in the log, and will be bumped to the next - * one if replaying succeeded. - */ - -status_t -Journal::_ReplayRunArray(int32 *_start) -{ - PRINT(("ReplayRunArray(start = %ld)\n", *_start)); - - off_t logOffset = fVolume->ToBlock(fVolume->Log()); - off_t blockNumber = *_start % fLogSize; - int32 blockSize = fVolume->BlockSize(); - int32 count = 1; - - CachedBlock cachedArray(fVolume); - - const run_array *array = (const run_array *)cachedArray.SetTo(logOffset + blockNumber); - if (array == NULL) - return B_IO_ERROR; - - if (_CheckRunArray(array) < B_OK) - return B_BAD_DATA; - - blockNumber = (blockNumber + 1) % fLogSize; - - CachedBlock cached(fVolume); - for (int32 index = 0; index < array->CountRuns(); index++) { - const block_run &run = array->RunAt(index); - PRINT(("replay block run %lu:%u:%u in log at %Ld!\n", run.AllocationGroup(), - run.Start(), run.Length(), blockNumber)); - - off_t offset = fVolume->ToOffset(run); - for (int32 i = 0; i < run.Length(); i++) { - const uint8 *data = cached.SetTo(logOffset + blockNumber); - if (data == NULL) - RETURN_ERROR(B_IO_ERROR); - - ssize_t written = write_pos(fVolume->Device(), - offset + (i * blockSize), data, blockSize); - if (written != blockSize) - RETURN_ERROR(B_IO_ERROR); - - blockNumber = (blockNumber + 1) % fLogSize; - count++; - } - } - - *_start += count; - return B_OK; -} - - -/** Replays all log entries - this will put the disk into a - * consistent and clean state, if it was not correctly unmounted - * before. - * This method is called by Journal::InitCheck() if the log start - * and end pointer don't match. - */ - -status_t -Journal::ReplayLog() -{ - INFORM(("Replay log, disk was not correctly unmounted...\n")); - - int32 start = fVolume->LogStart(); - int32 lastStart = -1; - while (true) { - // stop if the log is completely flushed - if (start == fVolume->LogEnd()) - break; - - if (start == lastStart) { - // strange, flushing the log hasn't changed the log_start pointer - return B_ERROR; - } - lastStart = start; - - status_t status = _ReplayRunArray(&start); - if (status < B_OK) { - FATAL(("replaying log entry from %ld failed: %s\n", start, strerror(status))); - return B_ERROR; - } - start = start % fLogSize; - } - - PRINT(("replaying worked fine!\n")); - fVolume->SuperBlock().log_start = HOST_ENDIAN_TO_BFS_INT64(fVolume->LogEnd()); - fVolume->LogStart() = fVolume->LogEnd(); - fVolume->SuperBlock().flags = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_DISK_CLEAN); - - return fVolume->WriteSuperBlock(); -} - - -/** This is a callback function that is called by the cache, whenever - * a block is flushed to disk that was updated as part of a transaction. - * This is necessary to keep track of completed transactions, to be - * able to update the log start pointer. - */ - -void -Journal::blockNotify(off_t blockNumber, size_t numBlocks, void *arg) -{ - LogEntry *logEntry = (LogEntry *)arg; - - if (!logEntry->NotifyBlocks(numBlocks)) { - // nothing to do yet... - return; - } - - Journal *journal = logEntry->GetJournal(); - disk_super_block &superBlock = journal->fVolume->SuperBlock(); - bool update = false; - - // Set log_start pointer if possible... - - journal->fEntriesLock.Lock(); - - if (logEntry == journal->fEntries.First()) { - LogEntry *next = journal->fEntries.GetNext(logEntry); - if (next != NULL) { - int32 length = next->Start() - logEntry->Start(); - superBlock.log_start = HOST_ENDIAN_TO_BFS_INT64((superBlock.LogStart() + length) % journal->fLogSize); - } else - superBlock.log_start = HOST_ENDIAN_TO_BFS_INT64(journal->fVolume->LogEnd()); - - update = true; - } - - journal->fUsed -= logEntry->Length(); - journal->fEntries.Remove(logEntry); - journal->fEntriesLock.Unlock(); - - free(logEntry); - - // update the superblock, and change the disk's state, if necessary - - if (update) { - journal->fVolume->LogStart() = superBlock.log_start; - - if (superBlock.log_start == superBlock.log_end) - superBlock.flags = SUPER_BLOCK_DISK_CLEAN; - - status_t status = journal->fVolume->WriteSuperBlock(); - if (status != B_OK) - FATAL(("blockNotify: could not write back superblock: %s\n", strerror(status))); - } -} - - -status_t -Journal::WriteLogEntry() -{ - fTransactionsInEntry = 0; - fHasChangedBlocks = false; - - sorted_array *array = fArray.Array(); - if (array == NULL || array->count == 0) - return B_OK; - - // Make sure there is enough space in the log. - // If that fails for whatever reason, panic! - force_cache_flush(fVolume->Device(), false); - int32 tries = fLogSize / 2 + 1; - while (TransactionSize() > FreeLogBlocks() && tries-- > 0) - force_cache_flush(fVolume->Device(), true); - - if (tries <= 0) { - fVolume->Panic(); - return B_BAD_DATA; - } - - int32 blockShift = fVolume->BlockShift(); - off_t logOffset = fVolume->ToBlock(fVolume->Log()) << blockShift; - off_t logStart = fVolume->LogEnd(); - off_t logPosition = logStart % fLogSize; - - // Create log entries for the transaction - - LogEntry *logEntry = NULL, *firstEntry = NULL, *lastAdded = NULL; - - for (int32 i = 0; i < array->CountItems(); i++) { - retry: - if (logEntry == NULL) { - logEntry = new LogEntry(this, logStart); - if (logEntry == NULL) - return B_NO_MEMORY; - if (logEntry->InitCheck() != B_OK) { - delete logEntry; - return B_NO_MEMORY; - } - if (firstEntry == NULL) - firstEntry = logEntry; - - logStart++; - } - - if (!logEntry->InsertBlock(array->ValueAt(i))) { - // log entry is full - start a new one - fEntriesLock.Lock(); - fEntries.Add(logEntry); - fEntriesLock.Unlock(); - lastAdded = logEntry; - - logEntry = NULL; - goto retry; - } - - logStart++; - } - - if (firstEntry == NULL) - return B_OK; - - if (logEntry != lastAdded) { - fEntriesLock.Lock(); - fEntries.Add(logEntry); - fEntriesLock.Unlock(); - } - - // Write log entries to disk - - CachedBlock cached(fVolume); - fEntriesLock.Lock(); - - for (logEntry = firstEntry; logEntry != NULL; logEntry = fEntries.GetNext(logEntry)) { - // first write the log entry array - write_pos(fVolume->Device(), logOffset + (logPosition << blockShift), - logEntry->Array(), fVolume->BlockSize()); - - logPosition = (logPosition + 1) % fLogSize; - - for (int32 i = 0; i < logEntry->CountRuns(); i++) { - uint8 *block = cached.SetTo(logEntry->RunAt(i)); - if (block == NULL) - return B_IO_ERROR; - - // write blocks - write_pos(fVolume->Device(), logOffset + (logPosition << blockShift), - block, fVolume->BlockSize()); - - logPosition = (logPosition + 1) % fLogSize; - } - } - - fEntriesLock.Unlock(); - - fUsed += array->CountItems(); - - // Update the log end pointer in the superblock - fVolume->SuperBlock().flags = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_DISK_DIRTY); - fVolume->SuperBlock().log_end = HOST_ENDIAN_TO_BFS_INT64(logPosition); - fVolume->LogEnd() = logPosition; - - status_t status = fVolume->WriteSuperBlock(); - - // We need to flush the drives own cache here to ensure - // disk consistency. - // If that call fails, we can't do anything about it anyway - ioctl(fVolume->Device(), B_FLUSH_DRIVE_CACHE); - - fEntriesLock.Lock(); - logStart = firstEntry->Start(); - - for (logEntry = firstEntry; logEntry != NULL; logEntry = fEntries.GetNext(logEntry)) { - // Note: this only works this way as we only have block_runs of length 1 - // We're reusing the fArray array, as we don't need it anymore, and - // it's guaranteed to be large enough for us, too - for (int32 i = 0; i < logEntry->CountRuns(); i++) { - array->values[i] = fVolume->ToBlock(logEntry->RunAt(i)); - } - - set_blocks_info(fVolume->Device(), &array->values[0], - logEntry->CountRuns(), blockNotify, logEntry); - } - - fEntriesLock.Unlock(); - - fArray.MakeEmpty(); - - // If the log goes to the next round (the log is written as a - // circular buffer), all blocks will be flushed out which is - // possible because we don't have any locked blocks at this - // point. - if (logPosition < logStart) - fVolume->FlushDevice(); - - return status; -} - - -status_t -Journal::FlushLogAndBlocks() -{ - status_t status = Lock((Transaction *)this); - if (status != B_OK) - return status; - - // write the current log entry to disk - - if (TransactionSize() != 0) { - status = WriteLogEntry(); - if (status < B_OK) - FATAL(("writing current log entry failed: %s\n", strerror(status))); - } - status = fVolume->FlushDevice(); - - Unlock((Transaction *)this, true); - return status; -} - - -status_t -Journal::Lock(Transaction *owner) -{ - if (owner == fOwner) - return B_OK; - - status_t status = fLock.Lock(); - if (status == B_OK) - fOwner = owner; - - // if the last transaction is older than 2 secs, start a new one - if (fTransactionsInEntry != 0 && system_time() - fTimestamp > 2000000L) - WriteLogEntry(); - - return B_OK; -} - - -void -Journal::Unlock(Transaction *owner, bool success) -{ - if (owner != fOwner) - return; - - TransactionDone(success); - - fTimestamp = system_time(); - fOwner = NULL; - fLock.Unlock(); -} - - -/** If there is a current transaction that the current thread has - * started, this function will give you access to it. - */ - -Transaction * -Journal::CurrentTransaction() -{ - if (fLock.LockWithTimeout(0) != B_OK) - return NULL; - - Transaction *owner = fOwner; - fLock.Unlock(); - - return owner; -} - - -status_t -Journal::TransactionDone(bool success) -{ - if (!success && fTransactionsInEntry == 0) { - // we can safely abort the transaction - sorted_array *array = fArray.Array(); - if (array != NULL) { - // release the lock for all blocks in the array (we don't need - // to be notified when they are actually written to disk) - for (int32 i = 0; i < array->CountItems(); i++) - release_block(fVolume->Device(), array->ValueAt(i)); - } - - return B_OK; - } - - // Up to a maximum size, we will just batch several - // transactions together to improve speed - if (TransactionSize() < fMaxTransactionSize) { - fTransactionsInEntry++; - fHasChangedBlocks = false; - - return B_OK; - } - - return WriteLogEntry(); -} - - -status_t -Journal::LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks) -{ - // ToDo: that's for now - we should change the log file size here - if (TransactionSize() + numBlocks + 1 > fLogSize) - return B_DEVICE_FULL; - - fHasChangedBlocks = true; - int32 blockSize = fVolume->BlockSize(); - - for (;numBlocks-- > 0; blockNumber++, buffer += blockSize) { - if (fArray.Find(blockNumber) >= 0) { - // The block is already in the log, so just update its data - // Note, this is only necessary if this method is called with a buffer - // different from the cached block buffer - which is unlikely but - // we'll make sure this way (costs one cache lookup, though). - status_t status = cached_write(fVolume->Device(), blockNumber, buffer, 1, blockSize); - if (status < B_OK) - return status; - - continue; - } - - // Insert the block into the transaction's array, and write the changes - // back into the locked cache buffer - fArray.Insert(blockNumber); - status_t status = cached_write_locked(fVolume->Device(), blockNumber, buffer, 1, blockSize); - if (status < B_OK) - return status; - } - - // If necessary, flush the log, so that we have enough space for this transaction - if (TransactionSize() > FreeLogBlocks()) - force_cache_flush(fVolume->Device(), true); - - return B_OK; -} - - -// #pragma mark - - - -status_t -Transaction::Start(Volume *volume, off_t refBlock) -{ - // has it already been started? - if (fJournal != NULL) - return B_OK; - - fJournal = volume->GetJournal(refBlock); - if (fJournal != NULL && fJournal->Lock(this) == B_OK) - return B_OK; - - fJournal = NULL; - return B_ERROR; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Journal.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Journal.h deleted file mode 100644 index 2edb4dc9ee..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Journal.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de - * This file may be used under the terms of the MIT License. - */ -#ifndef JOURNAL_H -#define JOURNAL_H - - -#include "Debug.h" -#include "Volume.h" -#include "Chain.h" -#include "Utility.h" - -#include "cache.h" - -#include -#include - -#ifdef USER -# include -#endif - - -struct run_array; -class LogEntry; -typedef DoublyLinkedList LogEntryList; - -// Locking policy in BFS: if you need both, the volume lock and the -// journal lock, you must lock the volume first - or else you will -// end up in a deadlock. -// That is, if you start a transaction, and will need to lock the -// volume while the transaction is in progress (for the unsafe -// get_vnode() call, for example), you must lock the volume before -// starting the transaction. - -class Journal { - public: - Journal(Volume *); - ~Journal(); - - status_t InitCheck(); - - status_t Lock(Transaction *owner); - void Unlock(Transaction *owner, bool success); - - status_t ReplayLog(); - - status_t WriteLogEntry(); - status_t LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks); - - Transaction *CurrentTransaction(); - uint32 TransactionSize() const { return fArray.CountItems() + fArray.BlocksUsed(); } - - status_t FlushLogAndBlocks(); - Volume *GetVolume() const { return fVolume; } - - inline uint32 FreeLogBlocks() const; - - private: - status_t _CheckRunArray(const run_array *array); - status_t _ReplayRunArray(int32 *start); - status_t TransactionDone(bool success); - static void blockNotify(off_t blockNumber, size_t numBlocks, void *arg); - - Volume *fVolume; - RecursiveLock fLock; - Transaction *fOwner; - BlockArray fArray; - uint32 fLogSize, fMaxTransactionSize, fUsed; - int32 fTransactionsInEntry; - SimpleLock fEntriesLock; - LogEntryList fEntries; - bool fHasChangedBlocks; - bigtime_t fTimestamp; -}; - - -inline uint32 -Journal::FreeLogBlocks() const -{ - return fVolume->LogStart() <= fVolume->LogEnd() ? - fLogSize - fVolume->LogEnd() + fVolume->LogStart() - : fVolume->LogStart() - fVolume->LogEnd(); -} - - -// For now, that's only a dumb class that does more or less nothing -// else than writing the blocks directly to the real location. -// It doesn't yet use logging. - -class Transaction { - public: - Transaction(Volume *volume, off_t refBlock) - : - fJournal(NULL) - { - Start(volume, refBlock); - } - - Transaction(Volume *volume, block_run refRun) - : - fJournal(NULL) - { - Start(volume, volume->ToBlock(refRun)); - } - - Transaction() - : - fJournal(NULL) - { - } - - ~Transaction() - { - if (fJournal) - fJournal->Unlock(this, false); - } - - status_t Start(Volume *volume, off_t refBlock); - bool IsStarted() const { return fJournal != NULL; } - - void Done() - { - if (fJournal != NULL) - fJournal->Unlock(this, true); - fJournal = NULL; - } - - status_t WriteBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks = 1) - { - if (fJournal == NULL) - return B_NO_INIT; - - return fJournal->LogBlocks(blockNumber, buffer, numBlocks); - } - - Volume *GetVolume() { return fJournal != NULL ? fJournal->GetVolume() : NULL; } - - private: - Transaction(const Transaction &); - Transaction &operator=(const Transaction &); - // no implementation - - Journal *fJournal; -}; - -#endif /* JOURNAL_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Lock.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Lock.h deleted file mode 100644 index 72b726668e..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Lock.h +++ /dev/null @@ -1,529 +0,0 @@ -#ifndef LOCK_H -#define LOCK_H -/* Lock - simple semaphores, read/write lock implementation -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** Roughly based on a Be sample code written by Nathan Schrenk. -** -** This file may be used under the terms of the MIT License. -*/ - - -#include -#include -#include "Utility.h" -#include "Debug.h" - - -// Configure here if and when real benaphores should be used -#define USE_BENAPHORE - // if defined, benaphores are used for the Semaphore/RecursiveLock classes -#ifdef USER -//# define FAST_LOCK - // the ReadWriteLock class uses a second Semaphore to - // speed up locking - only makes sense if USE_BENAPHORE - // is defined, too. -#endif - - -class Semaphore { - public: - Semaphore(const char *name) - : -#ifdef USE_BENAPHORE - fSemaphore(create_sem(0, name)), - fCount(1) -#else - fSemaphore(create_sem(1, name)) -#endif - { -#ifndef USER - set_sem_owner(fSemaphore, B_SYSTEM_TEAM); -#endif - } - - ~Semaphore() - { - delete_sem(fSemaphore); - } - - status_t InitCheck() - { - if (fSemaphore < B_OK) - return fSemaphore; - - return B_OK; - } - - status_t Lock() - { -#ifdef USE_BENAPHORE - if (atomic_add(&fCount, -1) <= 0) -#endif - return acquire_sem(fSemaphore); -#ifdef USE_BENAPHORE - return B_OK; -#endif - } - - status_t Unlock() - { -#ifdef USE_BENAPHORE - if (atomic_add(&fCount, 1) < 0) -#endif - return release_sem(fSemaphore); -#ifdef USE_BENAPHORE - return B_OK; -#endif - } - - private: - sem_id fSemaphore; -#ifdef USE_BENAPHORE - vint32 fCount; -#endif -}; - -// a convenience class to lock a Semaphore object - -class Locker { - public: - Locker(Semaphore &lock) - : fLock(lock) - { - fStatus = lock.Lock(); - ASSERT(fStatus == B_OK); - } - - ~Locker() - { - if (fStatus == B_OK) - fLock.Unlock(); - } - - status_t Status() const - { - return fStatus; - } - - private: - Semaphore &fLock; - status_t fStatus; -}; - - -//**** Recursive Lock - -class RecursiveLock { - public: - RecursiveLock(const char *name) - : -#ifdef USE_BENAPHORE - fSemaphore(create_sem(0, name)), - fCount(1), -#else - fSemaphore(create_sem(1, name)), -#endif - fOwner(-1) - { -#ifndef USER - set_sem_owner(fSemaphore, B_SYSTEM_TEAM); -#endif - } - - status_t LockWithTimeout(bigtime_t timeout) - { - thread_id thread = find_thread(NULL); - if (thread == fOwner) { - fOwnerCount++; - return B_OK; - } - - status_t status; -#ifdef USE_BENAPHORE - if (atomic_add(&fCount, -1) > 0) - status = B_OK; - else -#endif - status = acquire_sem_etc(fSemaphore, 1, B_RELATIVE_TIMEOUT, timeout); - - if (status == B_OK) { - fOwner = thread; - fOwnerCount = 1; - } - - return status; - } - - status_t Lock() - { - return LockWithTimeout(B_INFINITE_TIMEOUT); - } - - status_t Unlock() - { - thread_id thread = find_thread(NULL); - if (thread != fOwner) { - #if __MWERKS__ && !USER //--- The R5 PowerPC kernel doesn't have panic() - char blip[255]; - sprintf(blip,"RecursiveLock unlocked by %ld, owned by %ld\n", thread, fOwner); - kernel_debugger(blip); - #else - panic("RecursiveLock unlocked by %ld, owned by %ld\n", thread, fOwner); - #endif - } - - if (--fOwnerCount == 0) { - fOwner = -1; -#ifdef USE_BENAPHORE - if (atomic_add(&fCount, 1) < 0) -#endif - return release_sem(fSemaphore); - } - - return B_OK; - } - - private: - sem_id fSemaphore; -#ifdef USE_BENAPHORE - vint32 fCount; -#endif - thread_id fOwner; - int32 fOwnerCount; -}; - -// a convenience class to lock an RecursiveLock object - -class RecursiveLocker { - public: - RecursiveLocker(RecursiveLock &lock) - : fLock(lock) - { - fStatus = lock.Lock(); - ASSERT(fStatus == B_OK); - } - - ~RecursiveLocker() - { - if (fStatus == B_OK) - fLock.Unlock(); - } - - status_t Status() const - { - return fStatus; - } - - private: - RecursiveLock &fLock; - status_t fStatus; -}; - - -//**** Many Reader/Single Writer Lock - -// This is a "fast" implementation of a single writer/many reader -// locking scheme. It's fast because it uses the benaphore idea -// to do lazy semaphore locking - in most cases it will only have -// to do some simple integer arithmetic. -// The second semaphore (fWriteLock) is needed to prevent the situation -// that a second writer can acquire the lock when there are still readers -// holding it. - -#define MAX_READERS 100000 - -// Note: this code will break if you actually have 100000 readers -// at once. With the current thread/... limits in BeOS you can't -// touch that value, but it might be possible in the future. -// Also, you can only have about 20000 concurrent writers until -// the semaphore count exceeds the int32 bounds - -// Timeouts: -// It may be a good idea to have timeouts for the WriteLocked class, -// in case something went wrong - we'll see if this is necessary, -// but it would be a somewhat poor work-around for a deadlock... -// But the only real problem with timeouts could be for things like -// "chkbfs" - because such a tool may need to lock for some more time - - -// define if you want to have fast locks as the foundation for the -// ReadWriteLock class - the benefit is that acquire_sem() doesn't -// have to be called when there is no one waiting. -// The disadvantage is the use of 2 real semaphores which is quite -// expensive regarding that BeOS only allows for a total of 64k -// semaphores (since every open BFS inode has such a lock). - -#ifdef FAST_LOCK -class ReadWriteLock { - public: - ReadWriteLock(const char *name) - : - fWriteLock(name) - { - Initialize(name); - } - - ReadWriteLock() - : - fWriteLock("bfs r/w w-lock") - { - } - - ~ReadWriteLock() - { - delete_sem(fSemaphore); - } - - status_t Initialize(const char *name = "bfs r/w lock") - { - fSemaphore = create_sem(0, name); - fCount = MAX_READERS; -#ifndef USER - set_sem_owner(fSemaphore, B_SYSTEM_TEAM); -#endif - return fSemaphore; - } - - status_t InitCheck() - { - if (fSemaphore < B_OK) - return fSemaphore; - - return B_OK; - } - - status_t Lock() - { - if (atomic_add(&fCount, -1) <= 0) - return acquire_sem(fSemaphore); - - return B_OK; - } - - void Unlock() - { - if (atomic_add(&fCount, 1) < 0) - release_sem(fSemaphore); - } - - status_t LockWrite() - { - if (fWriteLock.Lock() < B_OK) - return B_ERROR; - - int32 readers = atomic_add(&fCount, -MAX_READERS); - status_t status = B_OK; - - if (readers < MAX_READERS) { - // Acquire sem for all readers currently not using a semaphore. - // But if we are not the only write lock in the queue, just get - // the one for us - status = acquire_sem_etc(fSemaphore, readers <= 0 ? 1 : MAX_READERS - readers, 0, 0); - } - fWriteLock.Unlock(); - - return status; - } - - void UnlockWrite() - { - int32 readers = atomic_add(&fCount, MAX_READERS); - if (readers < 0) { - // release sem for all readers only when we were the only writer - release_sem_etc(fSemaphore, readers <= -MAX_READERS ? 1 : -readers, 0); - } - } - - private: - friend class ReadLocked; - friend class WriteLocked; - - sem_id fSemaphore; - vint32 fCount; - Semaphore fWriteLock; -}; -#else // FAST_LOCK -class ReadWriteLock { - public: - ReadWriteLock(const char *name) - { - Initialize(name); - } - - ReadWriteLock() - { - } - - ~ReadWriteLock() - { - delete_sem(fSemaphore); - } - - status_t Initialize(const char *name = "bfs r/w lock") - { - fSemaphore = create_sem(MAX_READERS, name); -#ifndef USER - set_sem_owner(fSemaphore, B_SYSTEM_TEAM); -#endif - return fSemaphore; - } - - status_t InitCheck() - { - if (fSemaphore < B_OK) - return fSemaphore; - - return B_OK; - } - - status_t Lock() - { - return acquire_sem(fSemaphore); - } - - void Unlock() - { - release_sem(fSemaphore); - } - - status_t LockWrite() - { - return acquire_sem_etc(fSemaphore, MAX_READERS, 0, 0); - } - - void UnlockWrite() - { - release_sem_etc(fSemaphore, MAX_READERS, 0); - } - - private: - friend class ReadLocked; - friend class WriteLocked; - - sem_id fSemaphore; -}; -#endif // FAST_LOCK - - -class ReadLocked { - public: - ReadLocked(ReadWriteLock &lock) - : - fLock(lock) - { - fStatus = lock.Lock(); - } - - ~ReadLocked() - { - if (fStatus == B_OK) - fLock.Unlock(); - } - - private: - ReadWriteLock &fLock; - status_t fStatus; -}; - - -class WriteLocked { - public: - WriteLocked(ReadWriteLock &lock) - : - fLock(lock) - { - fStatus = lock.LockWrite(); - } - - ~WriteLocked() - { - if (fStatus == B_OK) - fLock.UnlockWrite(); - } - - status_t IsLocked() - { - return fStatus; - } - - private: - ReadWriteLock &fLock; - status_t fStatus; -}; - - -// A simple locking structure that doesn't use a semaphore - it's useful -// if you have to protect critical parts with a short runtime. -// It also allows to nest several locks for the same thread. - -class SimpleLock { - public: - SimpleLock() - : - fHolder(-1), - fCount(0) - { - } - - status_t Lock(bigtime_t time = 500) - { - int32 thisThread = find_thread(NULL); - int32 current; - while (1) { - /*if (fHolder == -1) { - current = fHolder; - fHolder = thisThread; - }*/ - current = _atomic_test_and_set(&fHolder, thisThread, -1); - if (current == -1) - break; - if (current == thisThread) - break; - - snooze(time); - } - - // ToDo: the lock cannot fail currently! We may want - // to change this - atomic_add(&fCount, 1); - return B_OK; - } - - void Unlock() - { - if (atomic_add(&fCount, -1) == 1) - _atomic_set(&fHolder, -1); - } - - bool IsLocked() const - { - return fHolder == find_thread(NULL); - } - - private: - vint32 fHolder; - vint32 fCount; -}; - -// A convenience class to lock the SimpleLock, note the -// different timing compared to the direct call - -class SimpleLocker { - public: - SimpleLocker(SimpleLock &lock,bigtime_t time = 1000) - : fLock(lock) - { - lock.Lock(time); - } - - ~SimpleLocker() - { - fLock.Unlock(); - } - - private: - SimpleLock &fLock; -}; - -#endif /* LOCK_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Query.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/Query.cpp deleted file mode 100644 index 6f3c6b1f1f..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Query.cpp +++ /dev/null @@ -1,1578 +0,0 @@ -/* Query - query parsing and evaluation - * - * The pattern matching is roughly based on code originally written - * by J. Kercheval, and on code written by Kenneth Almquist, though - * it shares no code. - * - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de. - * This file may be used under the terms of the MIT License. - */ - - -#include "fsproto.h" // include first for hacky reasons - -#include "Query.h" -#include "bfs.h" -#include "Debug.h" -#include "Stack.h" -#include "Volume.h" -#include "Inode.h" -#include "BPlusTree.h" -#include "Index.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -// The parser has a very static design, but it will do what is required. -// -// ParseOr(), ParseAnd(), ParseEquation() are guarantying the operator -// precedence, that is =,!=,>,<,>=,<= .. && .. ||. -// Apparently, the "!" (not) can only be used with brackets. -// -// If you think that there are too few NULL pointer checks in some places -// of the code, just read the beginning of the query constructor. -// The API is not fully available, just the Query and the Expression class -// are. - - -enum ops { - OP_NONE, - - OP_AND, - OP_OR, - - OP_EQUATION, - - OP_EQUAL, - OP_UNEQUAL, - OP_GREATER_THAN, - OP_LESS_THAN, - OP_GREATER_THAN_OR_EQUAL, - OP_LESS_THAN_OR_EQUAL, -}; - -enum match { - NO_MATCH = 0, - MATCH_OK = 1, - - MATCH_BAD_PATTERN = -2, - MATCH_INVALID_CHARACTER -}; - -// return values from isValidPattern() -enum { - PATTERN_INVALID_ESCAPE = -3, - PATTERN_INVALID_RANGE, - PATTERN_INVALID_SET -}; - -union value { - int64 Int64; - uint64 Uint64; - int32 Int32; - uint32 Uint32; - float Float; - double Double; - char String[INODE_FILE_NAME_LENGTH]; -}; - -// B_MIME_STRING_TYPE is defined in storage/Mime.h, but we -// don't need the whole file here; the type can't change anyway -#ifndef _MIME_H -# define B_MIME_STRING_TYPE 'MIMS' -#endif - -class Term { - public: - Term(int8 op) : fOp(op), fParent(NULL) {} - virtual ~Term() {} - - int8 Op() const { return fOp; } - - void SetParent(Term *parent) { fParent = parent; } - Term *Parent() const { return fParent; } - - virtual status_t Match(Inode *inode,const char *attribute = NULL,int32 type = 0, - const uint8 *key = NULL,size_t size = 0) = 0; - virtual void Complement() = 0; - - virtual void CalculateScore(Index &index) = 0; - virtual int32 Score() const = 0; - - virtual status_t InitCheck() = 0; - -#ifdef DEBUG - virtual void PrintToStream() = 0; -#endif - - protected: - int8 fOp; - Term *fParent; -}; - -// Although an Equation object is quite independent from the volume on which -// the query is run, there are some dependencies that are produced while -// querying: -// The type/size of the value, the score, and if it has an index or not. -// So you could run more than one query on the same volume, but it might return -// wrong values when it runs concurrently on another volume. -// That's not an issue right now, because we run single-threaded and don't use -// queries more than once. - -class Equation : public Term { - public: - Equation(char **expr); - virtual ~Equation(); - - virtual status_t InitCheck(); - - status_t ParseQuotedString(char **_start, char **_end); - char *CopyString(char *start, char *end); - - virtual status_t Match(Inode *inode, const char *attribute = NULL, int32 type = 0, - const uint8 *key = NULL, size_t size = 0); - virtual void Complement(); - - status_t PrepareQuery(Volume *volume, Index &index, TreeIterator **iterator, - bool queryNonIndexed); - status_t GetNextMatching(Volume *volume, TreeIterator *iterator, - struct dirent *dirent, size_t bufferSize); - - virtual void CalculateScore(Index &index); - virtual int32 Score() const { return fScore; } - -#ifdef DEBUG - virtual void PrintToStream(); -#endif - - private: - Equation(const Equation &); - Equation &operator=(const Equation &); - // no implementation - - status_t ConvertValue(type_code type); - bool CompareTo(const uint8 *value, uint16 size); - uint8 *Value() const { return (uint8 *)&fValue; } - status_t MatchEmptyString(); - - char *fAttribute; - char *fString; - union value fValue; - type_code fType; - size_t fSize; - bool fIsPattern; - bool fIsSpecialTime; - - int32 fScore; - bool fHasIndex; -}; - -class Operator : public Term { - public: - Operator(Term *,int8,Term *); - virtual ~Operator(); - - Term *Left() const { return fLeft; } - Term *Right() const { return fRight; } - - virtual status_t Match(Inode *inode, const char *attribute = NULL, int32 type = 0, - const uint8 *key = NULL, size_t size = 0); - virtual void Complement(); - - virtual void CalculateScore(Index &index); - virtual int32 Score() const; - - virtual status_t InitCheck(); - - //Term *Copy() const; -#ifdef DEBUG - virtual void PrintToStream(); -#endif - - private: - Operator(const Operator &); - Operator &operator=(const Operator &); - // no implementation - - Term *fLeft,*fRight; -}; - - -//--------------------------------- - - -void -skipWhitespace(char **expr, int32 skip = 0) -{ - char *string = (*expr) + skip; - while (*string == ' ' || *string == '\t') string++; - *expr = string; -} - - -void -skipWhitespaceReverse(char **expr,char *stop) -{ - char *string = *expr; - while (string > stop && (*string == ' ' || *string == '\t')) string--; - *expr = string; -} - - -// #pragma mark - - - -uint32 -utf8ToUnicode(char **string) -{ - uint8 *bytes = (uint8 *)*string; - int32 length; - uint8 mask = 0x1f; - - switch (bytes[0] & 0xf0) { - case 0xc0: - case 0xd0: length = 2; break; - case 0xe0: length = 3; break; - case 0xf0: - mask = 0x0f; - length = 4; - break; - default: - // valid 1-byte character - // and invalid characters - (*string)++; - return bytes[0]; - } - uint32 c = bytes[0] & mask; - int32 i = 1; - for (;i < length && (bytes[i] & 0x80) > 0;i++) - c = (c << 6) | (bytes[i] & 0x3f); - - if (i < length) { - // invalid character - (*string)++; - return (uint32)bytes[0]; - } - *string += length; - return c; -} - - -int32 -getFirstPatternSymbol(char *string) -{ - char c; - - for (int32 index = 0;(c = *string++);index++) { - if (c == '*' || c == '?' || c == '[') - return index; - } - return -1; -} - - -bool -isPattern(char *string) -{ - return getFirstPatternSymbol(string) >= 0 ? true : false; -} - - -status_t -isValidPattern(char *pattern) -{ - while (*pattern) { - switch (*pattern++) { - case '\\': - // the escape character must not be at the end of the pattern - if (!*pattern++) - return PATTERN_INVALID_ESCAPE; - break; - - case '[': - if (pattern[0] == ']' || !pattern[0]) - return PATTERN_INVALID_SET; - - while (*pattern != ']') { - if (*pattern == '\\' && !*++pattern) - return PATTERN_INVALID_ESCAPE; - - if (!*pattern) - return PATTERN_INVALID_SET; - - if (pattern[0] == '-' && pattern[1] == '-') - return PATTERN_INVALID_RANGE; - - pattern++; - } - break; - } - } - return B_OK; -} - - -/** Matches the string against the given wildcard pattern. - * Returns either MATCH_OK, or NO_MATCH when everything went fine, - * or values < 0 (see enum at the top of Query.cpp) if an error - * occurs - */ - -status_t -matchString(char *pattern, char *string) -{ - while (*pattern) { - // end of string == valid end of pattern? - if (!string[0]) { - while (pattern[0] == '*') - pattern++; - return !pattern[0] ? MATCH_OK : NO_MATCH; - } - - switch (*pattern++) { - case '?': - { - // match exactly one UTF-8 character; we are - // not interested in the result - utf8ToUnicode(&string); - break; - } - - case '*': - { - // compact pattern - while (true) { - if (pattern[0] == '?') { - if (!*++string) - return NO_MATCH; - } else if (pattern[0] != '*') - break; - - pattern++; - } - - // if the pattern is done, we have matched the string - if (!pattern[0]) - return MATCH_OK; - - while(true) { - // we have removed all occurences of '*' and '?' - if (pattern[0] == string[0] - || pattern[0] == '[' - || pattern[0] == '\\') { - status_t status = matchString(pattern,string); - if (status < B_OK || status == MATCH_OK) - return status; - } - - // we could be nice here and just jump to the next - // UTF-8 character - but we wouldn't gain that much - // and it'd be slower (since we're checking for - // equality before entering the recursion) - if (!*++string) - return NO_MATCH; - } - break; - } - - case '[': - { - bool invert = false; - if (pattern[0] == '^' || pattern[0] == '!') { - invert = true; - pattern++; - } - - if (!pattern[0] || pattern[0] == ']') - return MATCH_BAD_PATTERN; - - uint32 c = utf8ToUnicode(&string); - bool matched = false; - - while (pattern[0] != ']') { - if (!pattern[0]) - return MATCH_BAD_PATTERN; - - if (pattern[0] == '\\') - pattern++; - - uint32 first = utf8ToUnicode(&pattern); - - // Does this character match, or is this a range? - if (first == c) { - matched = true; - break; - } else if (pattern[0] == '-' && pattern[1] != ']' && pattern[1]) { - pattern++; - - if (pattern[0] == '\\') { - pattern++; - if (!pattern[0]) - return MATCH_BAD_PATTERN; - } - uint32 last = utf8ToUnicode(&pattern); - - if (c >= first && c <= last) { - matched = true; - break; - } - } - } - - if (invert) - matched = !matched; - - if (matched) { - while (pattern[0] != ']') { - if (!pattern[0]) - return MATCH_BAD_PATTERN; - pattern++; - } - pattern++; - break; - } - return NO_MATCH; - } - - case '\\': - if (!pattern[0]) - return MATCH_BAD_PATTERN; - // supposed to fall through - default: - if (pattern[-1] != string[0]) - return NO_MATCH; - string++; - break; - } - } - - if (string[0]) - return NO_MATCH; - - return MATCH_OK; -} - - -// #pragma mark - - - -Equation::Equation(char **expr) - : Term(OP_EQUATION), - fAttribute(NULL), - fString(NULL), - fType(0), - fIsPattern(false) -{ - char *string = *expr; - char *start = string; - char *end = NULL; - - // Since the equation is the integral part of any query, we're just parsing - // the whole thing here. - // The whitespace at the start is already removed in Expression::ParseEquation() - - if (*start == '"' || *start == '\'') { - // string is quoted (start has to be on the beginning of a string) - if (ParseQuotedString(&start, &end) < B_OK) - return; - - // set string to a valid start of the equation symbol - string = end + 2; - skipWhitespace(&string); - if (*string != '=' && *string != '<' && *string != '>' && *string != '!') { - *expr = string; - return; - } - } else { - // search the (in)equation for the actual equation symbol (and for other operators - // in case the equation is malformed) - while (*string && *string != '=' && *string != '<' && *string != '>' && *string != '!' - && *string != '&' && *string != '|') - string++; - - // get the attribute string (and trim whitespace), in case - // the string was not quoted - end = string - 1; - skipWhitespaceReverse(&end, start); - } - - // attribute string is empty (which is not allowed) - if (start > end) - return; - - // at this point, "start" points to the beginning of the string, "end" points - // to the last character of the string, and "string" points to the first - // character of the equation symbol - - // test for the right symbol (as this doesn't need any memory) - switch (*string) { - case '=': - fOp = OP_EQUAL; - break; - case '>': - fOp = *(string + 1) == '=' ? OP_GREATER_THAN_OR_EQUAL : OP_GREATER_THAN; - break; - case '<': - fOp = *(string + 1) == '=' ? OP_LESS_THAN_OR_EQUAL : OP_LESS_THAN; - break; - case '!': - if (*(string + 1) != '=') - return; - fOp = OP_UNEQUAL; - break; - - // any invalid characters will be rejected - default: - *expr = string; - return; - } - // lets change "start" to point to the first character after the symbol - if (*(string + 1) == '=') - string++; - string++; - skipWhitespace(&string); - - // allocate & copy the attribute string - - fAttribute = CopyString(start, end); - if (fAttribute == NULL) - return; - - start = string; - if (*start == '"' || *start == '\'') { - // string is quoted (start has to be on the beginning of a string) - if (ParseQuotedString(&start, &end) < B_OK) - return; - - string = end + 2; - skipWhitespace(&string); - } else { - while (*string && *string != '&' && *string != '|' && *string != ')') - string++; - - end = string - 1; - skipWhitespaceReverse(&end, start); - } - - // at this point, "start" will point to the first character of the value, - // "end" will point to its last character, and "start" to the first non- - // whitespace character after the value string - - fString = CopyString(start, end); - if (fString == NULL) - return; - - // patterns are only allowed for these operations (and strings) - if (fOp == OP_EQUAL || fOp == OP_UNEQUAL) { - fIsPattern = isPattern(fString); - if (fIsPattern && isValidPattern(fString) < B_OK) { - // we only want to have valid patterns; setting fString - // to NULL will cause InitCheck() to fail - free(fString); - fString = NULL; - } - } - - // The special time flag is set if the time values are shifted - // 64-bit values to reduce the number of duplicates. - // We have to be able to compare them against unshifted values - // later. The only index which needs this is the last_modified - // index, but we may want to open that feature for other indices, - // too one day. - fIsSpecialTime = !strcmp(fAttribute, "last_modified"); - - *expr = string; -} - - -Equation::~Equation() -{ - if (fAttribute != NULL) - free(fAttribute); - if (fString != NULL) - free(fString); -} - - -status_t -Equation::InitCheck() -{ - if (fAttribute == NULL - || fString == NULL - || fOp == OP_NONE) - return B_BAD_VALUE; - - return B_OK; -} - - -status_t -Equation::ParseQuotedString(char **_start, char **_end) -{ - char *start = *_start; - char quote = *start++; - char *end = start; - - for (;*end && *end != quote;end++) { - if (*end == '\\') - end++; - } - if (*end == '\0') - return B_BAD_VALUE; - - *_start = start; - *_end = end - 1; - - return B_OK; -} - - -char * -Equation::CopyString(char *start, char *end) -{ - // end points to the last character of the string - and the length - // also has to include the null-termination - int32 length = end + 2 - start; - // just to make sure; since that's the max. attribute name length and - // the max. string in an index, it make sense to have it that way - if (length > INODE_FILE_NAME_LENGTH || length <= 0) - return NULL; - - char *copy = (char *)malloc(length); - if (copy == NULL) - return NULL; - - memcpy(copy,start,length - 1); - copy[length - 1] = '\0'; - - return copy; -} - - -status_t -Equation::ConvertValue(type_code type) -{ - // Has the type already been converted? - if (type == fType) - return B_OK; - - char *string = fString; - - switch (type) { - case B_MIME_STRING_TYPE: - type = B_STRING_TYPE; - // supposed to fall through - case B_STRING_TYPE: - strncpy(fValue.String, string, INODE_FILE_NAME_LENGTH); - fValue.String[INODE_FILE_NAME_LENGTH - 1] = '\0'; - fSize = strlen(fValue.String); - break; - case B_INT32_TYPE: - fValue.Int32 = strtol(string, &string, 0); - fSize = sizeof(int32); - break; - case B_UINT32_TYPE: - fValue.Int32 = strtoul(string, &string, 0); - fSize = sizeof(uint32); - break; - case B_INT64_TYPE: - fValue.Int64 = strtoll(string, &string, 0); - fSize = sizeof(int64); - break; - case B_UINT64_TYPE: - fValue.Uint64 = strtoull(string, &string, 0); - fSize = sizeof(uint64); - break; - case B_FLOAT_TYPE: - fValue.Float = strtod(string, &string); - fSize = sizeof(float); - break; - case B_DOUBLE_TYPE: - fValue.Double = strtod(string, &string); - fSize = sizeof(double); - break; - default: - FATAL(("query value conversion to 0x%lx requested!\n", type)); - // should we fail here or just do a safety int32 conversion? - return B_ERROR; - } - - fType = type; - - // patterns are only allowed for string types - if (fType != B_STRING_TYPE && fIsPattern) - fIsPattern = false; - - return B_OK; -} - - -/** Returns true when the key matches the equation. You have to - * call ConvertValue() before this one. - */ - -bool -Equation::CompareTo(const uint8 *value, uint16 size) -{ - int32 compare; - - // fIsPattern is only true if it's a string type, and fOp OP_EQUAL, or OP_UNEQUAL - if (fIsPattern) { - // we have already validated the pattern, so we don't check for failing - // here - if something is broken, and matchString() returns an error, - // we just don't match - compare = matchString(fValue.String, (char *)value) == MATCH_OK ? 0 : 1; - } else if (fIsSpecialTime) { - // the index is a shifted int64 index, but we have to match - // against an unshifted value (i.e. the last_modified index) - int64 timeValue = *(int64 *)value >> INODE_TIME_SHIFT; - compare = compareKeys(fType, &timeValue, sizeof(int64), &fValue.Int64, sizeof(int64)); - } else - compare = compareKeys(fType, value, size, Value(), fSize); - - switch (fOp) { - case OP_EQUAL: - return compare == 0; - case OP_UNEQUAL: - return compare != 0; - case OP_LESS_THAN: - return compare < 0; - case OP_LESS_THAN_OR_EQUAL: - return compare <= 0; - case OP_GREATER_THAN: - return compare > 0; - case OP_GREATER_THAN_OR_EQUAL: - return compare >= 0; - } - FATAL(("Unknown/Unsupported operation: %d\n", fOp)); - return false; -} - - -void -Equation::Complement() -{ - D(if (fOp <= OP_EQUATION || fOp > OP_LESS_THAN_OR_EQUAL) { - FATAL(("op out of range!")); - return; - }); - - int8 complementOp[] = {OP_UNEQUAL, OP_EQUAL, OP_LESS_THAN_OR_EQUAL, - OP_GREATER_THAN_OR_EQUAL, OP_LESS_THAN, OP_GREATER_THAN}; - fOp = complementOp[fOp - OP_EQUAL]; -} - - -status_t -Equation::MatchEmptyString() -{ - // there is no matching attribute, we will just bail out if we - // already know that our value is not of a string type. - // If not, it will be converted to a string - and then be compared with "". - // That's why we have to call ConvertValue() here - but it will be - // a cheap call for the next time - // Should we do this only for OP_UNEQUAL? - if (fType != 0 && fType != B_STRING_TYPE) - return NO_MATCH; - - status_t status = ConvertValue(B_STRING_TYPE); - if (status == B_OK) - status = CompareTo((const uint8 *)"", fSize) ? MATCH_OK : NO_MATCH; - - return status; -} - - -/** Matches the inode's attribute value with the equation. - * Returns MATCH_OK if it matches, NO_MATCH if not, < 0 if something went wrong - */ - -status_t -Equation::Match(Inode *inode, const char *attributeName, int32 type, const uint8 *key, size_t size) -{ - // get a pointer to the attribute in question - union value value; - uint8 *buffer; - bool locked = false; - - // first, check if we are matching for a live query and use that value - if (attributeName != NULL && !strcmp(fAttribute, attributeName)) { - if (key == NULL) { - if (type == B_STRING_TYPE) - return MatchEmptyString(); - - return NO_MATCH; - } - buffer = const_cast(key); - } else if (!strcmp(fAttribute, "name")) { - // we need to lock before accessing Inode::Name() - inode->SmallDataLock().Lock(); - locked = true; - - // if not, check for "fake" attributes, "name", "size", "last_modified", - buffer = (uint8 *)inode->Name(); - if (buffer == NULL) { - inode->SmallDataLock().Unlock(); - return B_ERROR; - } - - type = B_STRING_TYPE; - size = strlen((const char *)buffer); - } else if (!strcmp(fAttribute,"size")) { - buffer = (uint8 *)&inode->Node()->data.size; - type = B_INT64_TYPE; - } else if (!strcmp(fAttribute,"last_modified")) { - buffer = (uint8 *)&inode->Node()->last_modified_time; - type = B_INT64_TYPE; - } else { - // then for attributes in the small_data section, and finally for the - // real attributes - Inode *attribute; - - inode->SmallDataLock().Lock(); - small_data *smallData = inode->FindSmallData(fAttribute); - if (smallData != NULL) { - buffer = smallData->Data(); - type = smallData->type; - size = smallData->data_size; - locked = true; - } else { - // needed to unlock the small_data section as fast as possible - inode->SmallDataLock().Unlock(); - - if (inode->GetAttribute(fAttribute, &attribute) == B_OK) { - buffer = (uint8 *)&value; - type = attribute->Node()->type; - size = attribute->Size(); - - if (size > INODE_FILE_NAME_LENGTH) - size = INODE_FILE_NAME_LENGTH; - - if (attribute->ReadAt(0, buffer, &size) < B_OK) { - inode->ReleaseAttribute(attribute); - return B_IO_ERROR; - } - inode->ReleaseAttribute(attribute); - } else - return MatchEmptyString(); - } - } - // prepare own value for use, if it is possible to convert it - status_t status = ConvertValue(type); - if (status == B_OK) - status = CompareTo(buffer, size) ? MATCH_OK : NO_MATCH; - - if (locked) - inode->SmallDataLock().Unlock(); - - RETURN_ERROR(status); -} - - -void -Equation::CalculateScore(Index &index) -{ - // As always, these values could be tuned and refined. - // And the code could also need some real world testing :-) - - // do we have to operate on a "foreign" index? - if (fOp == OP_UNEQUAL || index.SetTo(fAttribute) < B_OK) { - fScore = 0; - return; - } - - // if we have a pattern, how much does it help our search? - if (fIsPattern) - fScore = getFirstPatternSymbol(fString) << 3; - else { - // Score by operator - if (fOp == OP_EQUAL) - // higher than pattern="255 chars+*" - fScore = 2048; - else - // the pattern search is regarded cheaper when you have at - // least one character to set your index to - fScore = 5; - } - - // take index size into account (1024 is the current node size - // in our B+trees) - // 2048 * 2048 == 4194304 is the maximum score (for an empty - // tree, since the header + 1 node are already 2048 bytes) - fScore = fScore * ((2048 * 1024LL) / index.Node()->Size()); -} - - -status_t -Equation::PrepareQuery(Volume */*volume*/, Index &index, TreeIterator **iterator, bool queryNonIndexed) -{ - status_t status = index.SetTo(fAttribute); - - // if we should query attributes without an index, we can just proceed here - if (status < B_OK && !queryNonIndexed) - return B_ENTRY_NOT_FOUND; - - type_code type; - - // special case for OP_UNEQUAL - it will always operate through the whole index - // but we need the call to the original index to get the correct type - if (status < B_OK || fOp == OP_UNEQUAL) { - // Try to get an index that holds all files (name) - // Also sets the default type for all attributes without index - // to string. - type = status < B_OK ? B_STRING_TYPE : index.Type(); - - if (index.SetTo("name") < B_OK) - return B_ENTRY_NOT_FOUND; - - fHasIndex = false; - } else { - fHasIndex = true; - type = index.Type(); - } - - if (ConvertValue(type) < B_OK) - return B_BAD_VALUE; - - BPlusTree *tree; - if (index.Node()->GetTree(&tree) < B_OK) - return B_ERROR; - - *iterator = new TreeIterator(tree); - if (*iterator == NULL) - return B_NO_MEMORY; - - if ((fOp == OP_EQUAL || fOp == OP_GREATER_THAN || fOp == OP_GREATER_THAN_OR_EQUAL - || fIsPattern) - && fHasIndex) { - // set iterator to the exact position - - int32 keySize = index.KeySize(); - - // at this point, fIsPattern is only true if it's a string type, and fOp - // is either OP_EQUAL or OP_UNEQUAL - if (fIsPattern) { - // let's see if we can use the beginning of the key for positioning - // the iterator and adjust the key size; if not, just leave the - // iterator at the start and return success - keySize = getFirstPatternSymbol(fString); - if (keySize <= 0) - return B_OK; - } - - if (keySize == 0) { - // B_STRING_TYPE doesn't have a fixed length, so it was set - // to 0 before - we compute the correct value here - if (fType == B_STRING_TYPE) { - keySize = strlen(fValue.String); - - // The empty string is a special case - we normally don't check - // for the trailing null byte, in the case for the empty string - // we do it explicitly, because there can't be keys in the B+tree - // with a length of zero - if (keySize == 0) - keySize = 1; - } else - RETURN_ERROR(B_ENTRY_NOT_FOUND); - } - - if (fIsSpecialTime) { - // we have to find the first matching shifted value - off_t value = fValue.Int64 << INODE_TIME_SHIFT; - status = (*iterator)->Find((uint8 *)&value, keySize); - if (status == B_ENTRY_NOT_FOUND) - return B_OK; - } else { - status = (*iterator)->Find(Value(), keySize); - if (fOp == OP_EQUAL && !fIsPattern) - return status; - else if (status == B_ENTRY_NOT_FOUND - && (fIsPattern || fOp == OP_GREATER_THAN || fOp == OP_GREATER_THAN_OR_EQUAL)) - return B_OK; - } - - RETURN_ERROR(status); - } - - return B_OK; -} - - -status_t -Equation::GetNextMatching(Volume *volume, TreeIterator *iterator, - struct dirent *dirent, size_t bufferSize) -{ - while (true) { - union value indexValue; - uint16 keyLength; - uint16 duplicate; - off_t offset; - - status_t status = iterator->GetNextEntry(&indexValue, &keyLength, - (uint16)sizeof(indexValue), &offset, &duplicate); - if (status < B_OK) - return status; - - // only compare against the index entry when this is the correct - // index for the equation - if (fHasIndex && duplicate < 2 && !CompareTo((uint8 *)&indexValue, keyLength)) { - // They aren't equal? let the operation decide what to do - // Since we always start at the beginning of the index (or the correct - // position), only some needs to be stopped if the entry doesn't fit. - if (fOp == OP_LESS_THAN - || fOp == OP_LESS_THAN_OR_EQUAL - || (fOp == OP_EQUAL && !fIsPattern)) - return B_ENTRY_NOT_FOUND; - - if (duplicate > 0) - iterator->SkipDuplicates(); - continue; - } - - Vnode vnode(volume, offset); - Inode *inode; - if ((status = vnode.Get(&inode)) != B_OK) { - REPORT_ERROR(status); - FATAL(("could not get inode %Ld in index \"%s\"!\n", offset, fAttribute)); - // try with next - continue; - } - - // ToDo: check user permissions here - but which one?! - // we could filter out all those where we don't have - // read access... (we should check for every parent - // directory if the X_OK is allowed) - // Although it's quite expensive to open all parents, - // it's likely that the application that runs the - // query will do something similar (and we don't have - // to do it for root, either). - - // go up in the tree until a &&-operator is found, and check if the - // inode matches with the rest of the expression - we don't have to - // check ||-operators for that - Term *term = this; - status = MATCH_OK; - - if (!fHasIndex) - status = Match(inode); - - while (term != NULL && status == MATCH_OK) { - Operator *parent = (Operator *)term->Parent(); - if (parent == NULL) - break; - - if (parent->Op() == OP_AND) { - // choose the other child of the parent - Term *other = parent->Right(); - if (other == term) - other = parent->Left(); - - if (other == NULL) { - FATAL(("&&-operator has only one child... (parent = %p)\n", parent)); - break; - } - status = other->Match(inode); - if (status < 0) { - REPORT_ERROR(status); - status = NO_MATCH; - } - } - term = (Term *)parent; - } - - if (status == MATCH_OK) { - dirent->d_dev = volume->ID(); - dirent->d_ino = offset; - dirent->d_pdev = volume->ID(); - dirent->d_pino = volume->ToVnode(inode->Parent()); - - if (inode->GetName(dirent->d_name) < B_OK) - FATAL(("inode %Ld in query has no name!\n", inode->BlockNumber())); - -#ifdef KEEP_WRONG_DIRENT_RECLEN - // ToDo: The available file systems in BeOS apparently don't set the - // correct d_reclen - we are copying that behaviour if requested, but - // if it doesn't break compatibility, we will remove it. - dirent->d_reclen = strlen(dirent->d_name); -#else - dirent->d_reclen = sizeof(struct dirent) + strlen(dirent->d_name); -#endif - } - - if (status == MATCH_OK) - return B_OK; - } - RETURN_ERROR(B_ERROR); -} - - -// #pragma mark - - - -Operator::Operator(Term *left, int8 op, Term *right) - : Term(op), - fLeft(left), - fRight(right) -{ - if (left) - left->SetParent(this); - if (right) - right->SetParent(this); -} - - -Operator::~Operator() -{ - delete fLeft; - delete fRight; -} - - -status_t -Operator::Match(Inode *inode, const char *attribute, int32 type, const uint8 *key, size_t size) -{ - if (fOp == OP_AND) { - status_t status = fLeft->Match(inode, attribute, type, key, size); - if (status != MATCH_OK) - return status; - - return fRight->Match(inode, attribute, type, key, size); - } else { - // choose the term with the better score for OP_OR - if (fRight->Score() > fLeft->Score()) { - status_t status = fRight->Match(inode, attribute, type, key, size); - if (status != NO_MATCH) - return status; - } - return fLeft->Match(inode, attribute, type, key, size); - } -} - - -void -Operator::Complement() -{ - if (fOp == OP_AND) - fOp = OP_OR; - else - fOp = OP_AND; - - fLeft->Complement(); - fRight->Complement(); -} - - -void -Operator::CalculateScore(Index &index) -{ - fLeft->CalculateScore(index); - fRight->CalculateScore(index); -} - - -int32 -Operator::Score() const -{ - if (fOp == OP_AND) { - // return the one with the better score - if (fRight->Score() > fLeft->Score()) - return fRight->Score(); - - return fLeft->Score(); - } - - // for OP_OR, be honest, and return the one with the worse score - if (fRight->Score() < fLeft->Score()) - return fRight->Score(); - - return fLeft->Score(); -} - - -status_t -Operator::InitCheck() -{ - if (fOp != OP_AND && fOp != OP_OR - || fLeft == NULL || fLeft->InitCheck() < B_OK - || fRight == NULL || fRight->InitCheck() < B_OK) - return B_ERROR; - - return B_OK; -} - - -#if 0 -Term * -Operator::Copy() const -{ - if (fEquation != NULL) { - Equation *equation = new Equation(*fEquation); - if (equation == NULL) - return NULL; - - Term *term = new Term(equation); - if (term == NULL) - delete equation; - - return term; - } - - Term *left = NULL, *right = NULL; - - if (fLeft != NULL && (left = fLeft->Copy()) == NULL) - return NULL; - if (fRight != NULL && (right = fRight->Copy()) == NULL) { - delete left; - return NULL; - } - - Term *term = new Term(left,fOp,right); - if (term == NULL) { - delete left; - delete right; - return NULL; - } - return term; -} -#endif - - -// #pragma mark - - -#ifdef DEBUG -void -Operator::PrintToStream() -{ - D(__out("( ")); - if (fLeft != NULL) - fLeft->PrintToStream(); - - char *op; - switch (fOp) { - case OP_OR: op = "OR"; break; - case OP_AND: op = "AND"; break; - default: op = "?"; break; - } - D(__out(" %s ",op)); - - if (fRight != NULL) - fRight->PrintToStream(); - - D(__out(" )")); -} - - -void -Equation::PrintToStream() -{ - char *symbol = "???"; - switch (fOp) { - case OP_EQUAL: symbol = "=="; break; - case OP_UNEQUAL: symbol = "!="; break; - case OP_GREATER_THAN: symbol = ">"; break; - case OP_GREATER_THAN_OR_EQUAL: symbol = ">="; break; - case OP_LESS_THAN: symbol = "<"; break; - case OP_LESS_THAN_OR_EQUAL: symbol = "<="; break; - } - D(__out("[\"%s\" %s \"%s\"]", fAttribute, symbol, fString)); -} - -#endif /* DEBUG */ - -// #pragma mark - - - -Expression::Expression(char *expr) -{ - if (expr == NULL) - return; - - fTerm = ParseOr(&expr); - if (fTerm != NULL && fTerm->InitCheck() < B_OK) { - FATAL(("Corrupt tree in expression!\n")); - delete fTerm; - fTerm = NULL; - } - D(if (fTerm != NULL) { - fTerm->PrintToStream(); - D(__out("\n")); - if (*expr != '\0') - PRINT(("Unexpected end of string: \"%s\"!\n", expr)); - }); - fPosition = expr; -} - - -Expression::~Expression() -{ - delete fTerm; -} - - -Term * -Expression::ParseEquation(char **expr) -{ - skipWhitespace(expr); - - bool _not = false; - if (**expr == '!') { - skipWhitespace(expr, 1); - if (**expr != '(') - return NULL; - - _not = true; - } - - if (**expr == ')') { - // shouldn't be handled here - return NULL; - } else if (**expr == '(') { - skipWhitespace(expr, 1); - - Term *term = ParseOr(expr); - - skipWhitespace(expr); - - if (**expr != ')') { - delete term; - return NULL; - } - - // If the term is negated, we just complement the tree, to get - // rid of the not, a.k.a. DeMorgan's Law. - if (_not) - term->Complement(); - - skipWhitespace(expr, 1); - - return term; - } - - Equation *equation = new Equation(expr); - if (equation == NULL || equation->InitCheck() < B_OK) { - delete equation; - return NULL; - } - return equation; -} - - -Term * -Expression::ParseAnd(char **expr) -{ - Term *left = ParseEquation(expr); - if (left == NULL) - return NULL; - - while (IsOperator(expr,'&')) { - Term *right = ParseAnd(expr); - Term *newParent = NULL; - - if (right == NULL || (newParent = new Operator(left, OP_AND, right)) == NULL) { - delete left; - delete right; - - return NULL; - } - left = newParent; - } - - return left; -} - - -Term * -Expression::ParseOr(char **expr) -{ - Term *left = ParseAnd(expr); - if (left == NULL) - return NULL; - - while (IsOperator(expr,'|')) { - Term *right = ParseAnd(expr); - Term *newParent = NULL; - - if (right == NULL || (newParent = new Operator(left, OP_OR, right)) == NULL) { - delete left; - delete right; - - return NULL; - } - left = newParent; - } - - return left; -} - - -bool -Expression::IsOperator(char **expr, char op) -{ - char *string = *expr; - - if (*string == op && *(string + 1) == op) { - *expr += 2; - return true; - } - return false; -} - - -status_t -Expression::InitCheck() -{ - if (fTerm == NULL) - return B_BAD_VALUE; - - return B_OK; -} - - -// #pragma mark - - - -Query::Query(Volume *volume, Expression *expression, uint32 flags) - : - fVolume(volume), - fExpression(expression), - fCurrent(NULL), - fIterator(NULL), - fIndex(volume), - fFlags(flags), - fPort(-1) -{ - // if the expression has a valid root pointer, the whole tree has - // already passed the sanity check, so that we don't have to check - // every pointer - if (volume == NULL || expression == NULL || expression->Root() == NULL) - return; - - // create index on the stack and delete it afterwards - fExpression->Root()->CalculateScore(fIndex); - fIndex.Unset(); - - Stack stack; - stack.Push(fExpression->Root()); - - Term *term; - while (stack.Pop(&term)) { - if (term->Op() < OP_EQUATION) { - Operator *op = (Operator *)term; - - if (op->Op() == OP_OR) { - stack.Push(op->Left()); - stack.Push(op->Right()); - } else { - // For OP_AND, we can use the scoring system to decide which path to add - if (op->Right()->Score() > op->Left()->Score()) - stack.Push(op->Right()); - else - stack.Push(op->Left()); - } - } else if (term->Op() == OP_EQUATION || fStack.Push((Equation *)term) < B_OK) - FATAL(("Unknown term on stack or stack error")); - } - - if (fFlags & B_LIVE_QUERY) - volume->AddQuery(this); -} - - -Query::~Query() -{ - if (fFlags & B_LIVE_QUERY) - fVolume->RemoveQuery(this); -} - - -status_t -Query::GetNextEntry(struct dirent *dirent, size_t size) -{ - // If we don't have an equation to use yet/anymore, get a new one - // from the stack - while (true) { - if (fIterator == NULL) { - if (!fStack.Pop(&fCurrent) - || fCurrent == NULL - || fCurrent->PrepareQuery(fVolume, fIndex, &fIterator, - false/*fFlags & B_QUERY_NON_INDEXED*/) < B_OK) - return B_ENTRY_NOT_FOUND; - } - if (fCurrent == NULL) - RETURN_ERROR(B_ERROR); - - status_t status = fCurrent->GetNextMatching(fVolume, fIterator, dirent, size); - if (status < B_OK) { - delete fIterator; - fIterator = NULL; - fCurrent = NULL; - } else { - // only return if we have another entry - return B_OK; - } - } -} - - -void -Query::SetLiveMode(port_id port, int32 token) -{ - fPort = port; - fToken = token; - - if ((fFlags & B_LIVE_QUERY) == 0) { - // you can decide at any point to set the live query mode, - // only live queries have to be updated by attribute changes - fFlags |= B_LIVE_QUERY; - fVolume->AddQuery(this); - } -} - - -void -Query::LiveUpdate(Inode *inode, const char *attribute, int32 type, const uint8 *oldKey, - size_t oldLength, const uint8 *newKey, size_t newLength) -{ - if (fPort < 0 || fExpression == NULL || attribute == NULL) - return; - - // ToDo: check if the attribute is part of the query at all... - - status_t oldStatus = fExpression->Root()->Match(inode, attribute, type, oldKey, oldLength); - status_t newStatus = fExpression->Root()->Match(inode, attribute, type, newKey, newLength); - - int32 op; - if (oldStatus == MATCH_OK && newStatus == MATCH_OK) { - // only send out a notification if the name was changed - if (oldKey == NULL || strcmp(attribute, "name")) - return; - - send_notification(fPort, fToken, B_QUERY_UPDATE, B_ENTRY_REMOVED, fVolume->ID(), 0, - fVolume->ToVnode(inode->Parent()), 0, inode->ID(), (const char *)oldKey); - op = B_ENTRY_CREATED; - } else if (oldStatus != MATCH_OK && newStatus != MATCH_OK) { - // nothing has changed - return; - } else if (oldStatus == MATCH_OK && newStatus != MATCH_OK) - op = B_ENTRY_REMOVED; - else - op = B_ENTRY_CREATED; - - // if "value" is NULL, send_notification() crashes... - const char *value = (const char *)newKey; - if (type != B_STRING_TYPE || value == NULL) - value = ""; - - send_notification(fPort, fToken, B_QUERY_UPDATE, op, fVolume->ID(), 0, - fVolume->ToVnode(inode->Parent()), 0, inode->ID(), value); -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Query.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Query.h deleted file mode 100644 index 94f9fc6df5..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Query.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef QUERY_H -#define QUERY_H -/* Query - query parsing and evaluation -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include - -#include "Index.h" -#include "Stack.h" -#include "Chain.h" - -class Volume; -class Term; -class Equation; -class TreeIterator; -class Query; - - -class Expression { - public: - Expression(char *expr); - ~Expression(); - - status_t InitCheck(); - const char *Position() const { return fPosition; } - Term *Root() const { return fTerm; } - - protected: - Term *ParseOr(char **expr); - Term *ParseAnd(char **expr); - Term *ParseEquation(char **expr); - - bool IsOperator(char **expr,char op); - - private: - Expression(const Expression &); - Expression &operator=(const Expression &); - // no implementation - - char *fPosition; - Term *fTerm; -}; - -class Query { - public: - Query(Volume *volume, Expression *expression, uint32 flags); - ~Query(); - - status_t GetNextEntry(struct dirent *,size_t size); - - void SetLiveMode(port_id port,int32 token); - void LiveUpdate(Inode *inode,const char *attribute,int32 type,const uint8 *oldKey,size_t oldLength,const uint8 *newKey,size_t newLength); - - Expression *GetExpression() const { return fExpression; } - - private: - Volume *fVolume; - Expression *fExpression; - Equation *fCurrent; - TreeIterator *fIterator; - Index fIndex; - Stack fStack; - - uint32 fFlags; - port_id fPort; - int32 fToken; - - private: - friend class Chain; - Query *fNext; -}; - -#endif /* QUERY_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Stack.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Stack.h deleted file mode 100644 index 0d3e4e3862..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Stack.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef STACK_H -#define STACK_H -/* Stack - a template stack class -** -** Copyright 2001 pinc Software. All Rights Reserved. -** This file may be used under the terms of the MIT License. -*/ - - -#include - - -template class Stack { - public: - Stack() - : - fArray(NULL), - fUsed(0), - fMax(0) - { - } - - ~Stack() - { - if (fArray) - free(fArray); - } - - status_t Push(T value) - { - if (fUsed >= fMax) { - fMax += 16; - T *newArray = (T *)realloc(fArray,fMax * sizeof(T)); - if (newArray == NULL) - return B_NO_MEMORY; - - fArray = newArray; - } - fArray[fUsed++] = value; - return B_OK; - } - - bool Pop(T *value) - { - if (fUsed == 0) - return false; - - *value = fArray[--fUsed]; - return true; - } - - private: - T *fArray; - int32 fUsed; - int32 fMax; -}; - -#endif /* STACK_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Stream.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Stream.h deleted file mode 100644 index f0fd32b657..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Stream.h +++ /dev/null @@ -1,636 +0,0 @@ -/* Stream - inode stream access functions -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include "Inode.h" - - -// The classes in the namespace "Access" provide different type of access -// to the inode's data stream. -// Uncached accesses the underlaying device directly, Cached uses the -// standard cache, while Logged directs write accesses through the log. -// -// The classes interface is similar to the one of the CachedBlock class, -// but adds two other (static) functions for reading/writing several -// blocks at once. -// We don't use a real pure virtual interface as the class base, but we -// provide the same mechanism using templates. - -namespace Access { - -class Uncached { - public: - Uncached(Volume *volume); - Uncached(Volume *volume, off_t block, bool empty = false); - Uncached(Volume *volume, block_run run, bool empty = false); - ~Uncached(); - - void Unset(); - uint8 *SetTo(off_t block, bool empty = false); - uint8 *SetTo(block_run run, bool empty = false); - status_t WriteBack(Transaction *transaction); - - uint8 *Block() const { return fBlock; } - off_t BlockNumber() const { return fBlockNumber; } - uint32 BlockSize() const { return fVolume->BlockSize(); } - uint32 BlockShift() const { return fVolume->BlockShift(); } - - static status_t Read(Volume *volume, block_run run, uint8 *buffer); - static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); - - private: - Volume *fVolume; - off_t fBlockNumber; - uint8 *fBlock; -}; - -class Cached : public CachedBlock { - public: - Cached(Volume *volume); - Cached(Volume *volume, off_t block, bool empty = false); - Cached(Volume *volume, block_run run, bool empty = false); - - status_t WriteBack(Transaction *transaction); - static status_t Read(Volume *volume, block_run run, uint8 *buffer); - static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); -}; - -class Logged : public CachedBlock { - public: - Logged(Volume *volume); - Logged(Volume *volume,off_t block, bool empty = false); - Logged(Volume *volume, block_run run, bool empty = false); - - static status_t Read(Volume *volume, block_run run, uint8 *buffer); - static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); -}; - - -Uncached::Uncached(Volume *volume) - : - fVolume(volume), - fBlock(NULL) -{ -} - - -Uncached::Uncached(Volume *volume,off_t block, bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(block,empty); -} - - -Uncached::Uncached(Volume *volume,block_run run,bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(volume->ToBlock(run),empty); -} - - -Uncached::~Uncached() -{ - Unset(); -} - - -void -Uncached::Unset() -{ - if (fBlock != NULL) - fVolume->Pool().PutBuffer((void *)fBlock); -} - - -uint8 * -Uncached::SetTo(off_t block, bool empty) -{ - Unset(); - fBlockNumber = block; - if (fVolume->Pool().GetBuffer((void **)&fBlock) < B_OK) - return NULL; - - if (empty) - memset(fBlock, 0, BlockSize()); - else - read_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize()); - - return fBlock; -} - - -uint8 * -Uncached::SetTo(block_run run, bool empty) -{ - return SetTo(fVolume->ToBlock(run), empty); -} - - -status_t -Uncached::WriteBack(Transaction *transaction) -{ - if (fBlock == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return write_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize()); -} - - -status_t -Uncached::Read(Volume *volume, block_run run, uint8 *buffer) -{ - return read_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift()); -} - - -status_t -Uncached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) -{ - return write_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift()); -} - - -// #pragma mark - - - -Cached::Cached(Volume *volume) - : CachedBlock(volume) -{ -} - - -Cached::Cached(Volume *volume,off_t block,bool empty) - : CachedBlock(volume, block, empty) -{ -} - - -Cached::Cached(Volume *volume,block_run run,bool empty) - : CachedBlock(volume, run, empty) -{ -} - - -status_t -Cached::WriteBack(Transaction *transaction) -{ - if (transaction == NULL || fBlock == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return fVolume->WriteBlocks(fBlockNumber, fBlock, 1); -} - - -status_t -Cached::Read(Volume *volume, block_run run, uint8 *buffer) -{ - return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize()); -} - - -status_t -Cached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) -{ - return volume->WriteBlocks(volume->ToBlock(run), buffer, run.Length()); -} - - -// #pragma mark - - - -Logged::Logged(Volume *volume) - : CachedBlock(volume) -{ -} - - -Logged::Logged(Volume *volume, off_t block, bool empty) - : CachedBlock(volume, block, empty) -{ -} - - -Logged::Logged(Volume *volume, block_run run, bool empty) - : CachedBlock(volume, run, empty) -{ -} - - -status_t -Logged::Read(Volume *volume, block_run run, uint8 *buffer) -{ - return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize()); -} - - -status_t -Logged::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) -{ - return transaction->WriteBlocks(volume->ToBlock(run), buffer, run.Length()); -} - -}; // namespace Access - - -// #pragma mark - - - -// The Stream template class allows to have only one straight-forward -// implementation of the FindBlockRun(), ReadAt(), and WriteAt() methods. -// They will access the disk through the given cache class only, which -// means either uncached, cached, or logged (see above). - -template -class Stream : public Inode { - private: - // The constructor only exists to make the compiler happy - it - // is never called in the code itself - Stream() : Inode(NULL, -1) {} - - public: - status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); - status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); - status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length); -}; - - -/** see Inode::FindBlockRun() for the documentation of this method */ - -template -status_t -Stream::FindBlockRun(off_t pos, block_run &run, off_t &offset) -{ - data_stream *data = &Node()->data; - - // find matching block run - - if (data->MaxDirectRange() > 0 && pos >= data->MaxDirectRange()) { - if (data->MaxDoubleIndirectRange() > 0 && pos >= data->MaxIndirectRange()) { - // access to double indirect blocks - - Cache cached(fVolume); - - off_t start = pos - data->MaxIndirectRange(); - int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + cached.BlockShift())) - * (fVolume->BlockSize() / sizeof(block_run)); - int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift(); - int32 index = start / indirectSize; - int32 runsPerBlock = cached.BlockSize() / sizeof(block_run); - - block_run *indirect = (block_run *)cached.SetTo( - fVolume->ToBlock(data->double_indirect) + index / runsPerBlock); - if (indirect == NULL) - RETURN_ERROR(B_ERROR); - - //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); - //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); - - int32 current = (start % indirectSize) / directSize; - - indirect = (block_run *)cached.SetTo( - fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); - if (indirect == NULL) - RETURN_ERROR(B_ERROR); - - run = indirect[current % runsPerBlock]; - offset = data->MaxIndirectRange() + (index * indirectSize) + (current * directSize); - //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); - } else { - // access to indirect blocks - - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - off_t runBlockEnd = data->MaxDirectRange(); - - Cache cached(fVolume); - off_t block = fVolume->ToBlock(data->indirect); - - for (int32 i = 0; i < data->indirect.Length(); i++) { - block_run *indirect = (block_run *)cached.SetTo(block + i); - if (indirect == NULL) - RETURN_ERROR(B_IO_ERROR); - - int32 current = -1; - while (++current < runsPerBlock) { - if (indirect[current].IsZero()) - break; - - runBlockEnd += indirect[current].Length() << cached.BlockShift(); - if (runBlockEnd > pos) { - run = indirect[current]; - offset = runBlockEnd - (run.Length() << cached.BlockShift()); - //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); - //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); - return fVolume->ValidateBlockRun(run); - } - } - } - RETURN_ERROR(B_ERROR); - } - } else { - // access from direct blocks - - off_t runBlockEnd = 0LL; - int32 current = -1; - - while (++current < NUM_DIRECT_BLOCKS) { - if (data->direct[current].IsZero()) - break; - - runBlockEnd += data->direct[current].Length() << fVolume->BlockShift(); - if (runBlockEnd > pos) { - run = data->direct[current]; - offset = runBlockEnd - (run.Length() << fVolume->BlockShift()); - //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); - return fVolume->ValidateBlockRun(run); - } - } - //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos)); - return B_ENTRY_NOT_FOUND; - } - return fVolume->ValidateBlockRun(run); -} - - -template -status_t -Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length) -{ - size_t length = *_length; - - // set/check boundaries for pos/length - if (pos < 0) - return B_BAD_VALUE; - if (pos >= Node()->data.Size() || length == 0) { - *_length = 0; - return B_NO_ERROR; - } - - if (pos + length > Node()->data.Size()) - length = Node()->data.Size() - pos; - - block_run run; - off_t offset; - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - uint32 bytesRead = 0; - uint32 blockSize = fVolume->BlockSize(); - uint32 blockShift = fVolume->BlockShift(); - uint8 *block; - - // the first block_run we read could not be aligned to the block_size boundary - // (read partial block at the beginning) - - // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 - if (pos % blockSize != 0) { - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - Cache cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bytesRead = blockSize - (pos % blockSize); - if (length < bytesRead) - bytesRead = length; - - memcpy(buffer, block + (pos % blockSize), bytesRead); - pos += bytesRead; - - length -= bytesRead; - if (length == 0) { - *_length = bytesRead; - return B_OK; - } - - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - } - - // the first block_run is already filled in at this point - // read the following complete blocks using cached_read(), - // the last partial block is read using the generic Cache class - - bool partial = false; - - while (length > 0) { - // offset is the offset to the current pos in the block_run - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - if (uint32(run.Length() << blockShift) > length) { - if (length < blockSize) { - Cache cached(fVolume, run); - if ((block = cached.Block()) == NULL) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - memcpy(buffer + bytesRead, block, length); - bytesRead += length; - break; - } - run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); - partial = true; - } - - if (Cache::Read(fVolume, run, buffer + bytesRead) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - - int32 bytes = run.Length() << blockShift; -#ifdef DEBUG - if ((uint32)bytes > length) - DEBUGGER(("bytes greater than length")); -#endif - length -= bytes; - bytesRead += bytes; - if (length == 0) - break; - - pos += bytes; - - if (partial) { - // if the last block was read only partially, point block_run - // to the remaining part - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); - run.length = HOST_ENDIAN_TO_BFS_INT16(1); - offset = pos; - } else if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - } - - *_length = bytesRead; - return B_OK; -} - - -template -status_t -Stream::WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *_length) -{ - size_t length = *_length; - - // set/check boundaries for pos/length - if (pos < 0) - return B_BAD_VALUE; - - if (pos + length > Size()) { - off_t oldSize = Size(); - - // uncached files can't be resized (Inode::SetFileSize() also - // doesn't allow this, but this way we don't have to start a - // transaction to find out). - if (Flags() & INODE_NO_CACHE) - return B_BAD_VALUE; - - // the transaction doesn't have to be started already - // ToDo: what's that INODE_NO_TRANSACTION flag good for again? - if ((Flags() & INODE_NO_TRANSACTION) == 0 - && !transaction->IsStarted()) - transaction->Start(fVolume, BlockNumber()); - - // let's grow the data stream to the size needed - status_t status = SetFileSize(transaction, pos + length); - if (status < B_OK) { - *_length = 0; - RETURN_ERROR(status); - } - // If the position of the write was beyond the file size, we - // have to fill the gap between that position and the old file - // size with zeros. - FillGapWithZeros(oldSize, pos); - } - - // If we don't want to write anything, we can now return (we may - // just have changed the file size using the position parameter) - if (length == 0) - return B_OK; - - block_run run; - off_t offset; - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bool logStream = (Flags() & INODE_LOGGED) == INODE_LOGGED; - if (logStream - && !transaction->IsStarted()) - transaction->Start(fVolume, BlockNumber()); - - uint32 bytesWritten = 0; - uint32 blockSize = fVolume->BlockSize(); - uint32 blockShift = fVolume->BlockShift(); - uint8 *block; - - // the first block_run we write could not be aligned to the block_size boundary - // (write partial block at the beginning) - - // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 - if (pos % blockSize != 0) { - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - Cache cached(fVolume, run); - if ((block = cached.Block()) == NULL) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bytesWritten = blockSize - (pos % blockSize); - if (length < bytesWritten) - bytesWritten = length; - - memcpy(block + (pos % blockSize),buffer,bytesWritten); - - cached.WriteBack(transaction); - - pos += bytesWritten; - - length -= bytesWritten; - if (length == 0) { - *_length = bytesWritten; - return B_OK; - } - - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - } - - // the first block_run is already filled in at this point - // write the following complete blocks using Volume::WriteBlocks(), - // the last partial block is written using the generic Cache class - - bool partial = false; - - while (length > 0) { - // offset is the offset to the current pos in the block_run - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - if (uint32(run.Length() << blockShift) > length) { - if (length < blockSize) { - Cache cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - memcpy(block, buffer + bytesWritten, length); - - cached.WriteBack(transaction); - - bytesWritten += length; - break; - } - run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); - partial = true; - } - - if (Cache::Write(transaction, fVolume, run, buffer + bytesWritten) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - - int32 bytes = run.Length() << blockShift; - length -= bytes; - bytesWritten += bytes; - if (length == 0) - break; - - pos += bytes; - - if (partial) { - // if the last block was written only partially, point block_run - // to the remaining part - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); - run.length = HOST_ENDIAN_TO_BFS_INT16(1); - offset = pos; - } else if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - } - - *_length = bytesWritten; - - return B_OK; -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/ToDo b/src/tests/add-ons/kernel/file_systems/bfs/r5/ToDo deleted file mode 100644 index 0ab920b6a9..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/ToDo +++ /dev/null @@ -1,84 +0,0 @@ -BFS - ToDo, May 26th, 2003 ------ - -BlockAllocator - - - the BlockAllocator is only slightly optimized and probably slow - - the first free and the largest range are currently not correctly maintained (only efficiency suffers - it does work correctly) - - the allocation policies will have to stand against some real world tests - - the access to the block bitmap is currently managed using a global lock (doesn't matter as long as transactions are serialized) - - -DataStream - - - only files are trimmed back (in bfs_close()), but every inode has a preallocated stream... - - Inode::GrowStream(): merging of block_runs doesn't work between range/block boundaries - - check the array block size in BFS for different block sizes - - -Queries - - - There shouldn't be any cases where you can speed up a query with reordering the query expression - test it - - Check permissions of the parent directories - - Add protection against crashing applications which had a query open - at least the original BeOS kernel does not free the cookie (which throws some memory away *and* prevents unmounting the disk), but that won't be needed for OpenBeOS - - the query set for "!=" and last_modified/size is not the same as for "="; last_modified/size don't contain directories - - check if the query has to be checked for a live update - - accept hex values 0x... - - -Journal - - - Check if there are any standard and often-happening cases for a transaction to fail, and if so, start the transaction only when necessary - - if the system crashes between bfs_unlink() and bfs_remove_vnode(), the inode can be removed from the tree, but its memory is still allocated - this can happen if the inode is still in use by someone (and that's what the "chkbfs" utility is for, mainly). - - add delayed index updating (+ delete actions to solve the issue above) - - multiple log files, parallel transactions? (note that parallel transactions would require more locking to be done) - - variable sized log file - - as long as we have a fixed-sized log file, it should be possible to reserve space for a transaction to be able to decide if batching it is possible - - -BPlusTree - - - BPlusTree::Remove() could trigger CachedNode::Free() to go through the free nodes list and free all pages at the end of the data stream - - BPlusTree::Remove() could let the tree shrink (simple kind of reorganization) - - updating the TreeIterators doesn't work yet for duplicates (which may be a problem if a duplicate node will go away after a remove) - - BPlusTree::RemoveDuplicate() could merge the contents of duplicate node with only a few entries to save some space (right now, only empty nodes are freed) - - -Inode - - - exchange Inode::OldLastModified() with Inode::NewLastModified(), and don't change the last_modified field directly in Inode::WriteAt() for consistency in case of a crash - - the size is only updated in bfs_close() - but if the system crashes before, the entry in the size index doesn't match the one in the inode anymore - it would be better to let the data.size not reflect the real file size in this case (since the max_xxx_range entries are always correct) - - Inode::FillGapWithZeros() currently disabled; apart from being slow, it really shouldn't be executed while a transaction is running, because that stops all other threads from doing anything (which can be a long time for a 100 MB file) - - need better locking mechanism in combination with B+trees etc.! - - -Indices - - - consider Index::UpdateLastModified() writing back the updated inode - - clearing up Index::Update() and live query update (seems to be a bit confusing right now) - - investigate adding an index cache to improve performance - -Attributes - - - Inode::WriteAttribute() doesn't check if the attribute data may fit into the small_data region if there already is that attribute as an attribute file - - for indices, we could get the old data from there when doing a query update - - -Volume - - -kernel_interface - - - missing functions, maybe they are not really needed: bfs_rename_attr(), bfs_rename_index(), bfs_initialize(), bfs_link() - - bfs_rename() currently doesn't respect any permissions - - -general stuff - - - There are also some comments with a leading "ToDo:" directly in the code which may not be mentioned here. - - implement mkbfs (try to do it in OpenBeOS style directly - only write the superblock from user space) - - ------ -Axel Dörfler -axeld@pinc-software.de diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Utility.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/Utility.cpp deleted file mode 100644 index 59ae90b9eb..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Utility.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* Utility - some helper classes - * - * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de. - * This file may be used under the terms of the MIT License. - */ - - -#include "Utility.h" -#include "Debug.h" - -#include - -#include -#include - - -bool -sorted_array::_FindInternal(off_t value, int32 &index) const -{ - int32 min = 0, max = CountItems() - 1; - off_t cmp; - while (min <= max) { - index = (min + max) / 2; - - cmp = ValueAt(index) - value; - if (cmp < 0) - min = index + 1; - else if (cmp > 0) - max = index - 1; - else - return true; - } - return false; -} - - -void -sorted_array::Insert(off_t value) -{ - // if there are more than 8 values in this array, use a - // binary search, if not, just iterate linearly to find - // the insertion point - int32 i; - if (CountItems() > 8 ) { - if (!_FindInternal(value, i) && ValueAt(i) <= value) - i++; - } else { - for (i = 0; i < CountItems(); i++) { - if (ValueAt(i) > value) - break; - } - } - - memmove(&values[i + 1], &values[i], (CountItems() - i) * sizeof(off_t)); - values[i] = HOST_ENDIAN_TO_BFS_INT64(value); - count = HOST_ENDIAN_TO_BFS_INT64(CountItems() + 1); -} - - -bool -sorted_array::Remove(off_t value) -{ - int32 index = Find(value); - if (index == -1) - return false; - - memmove(&values[index], &values[index + 1], (CountItems() - index) * sizeof(off_t)); - count = HOST_ENDIAN_TO_BFS_INT64(CountItems() - 1); - - return true; -} - - -// #pragma mark - - - -BlockArray::BlockArray(int32 blockSize) - : - fArray(NULL), - fBlockSize(blockSize), - fSize(0) -{ -} - - -BlockArray::~BlockArray() -{ - if (fArray) - free(fArray); -} - - -int32 -BlockArray::Find(off_t value) -{ - if (fArray == NULL) - return -1; - - return fArray->Find(value); -} - - -status_t -BlockArray::Insert(off_t value) -{ - if (fArray == NULL || fArray->CountItems() + 1 > fMaxBlocks) { - sorted_array *array = (sorted_array *)realloc(fArray, fSize + fBlockSize); - if (array == NULL) - return B_NO_MEMORY; - - if (fArray == NULL) - array->count = 0; - - fArray = array; - fSize += fBlockSize; - fMaxBlocks = fSize / sizeof(off_t) - 1; - } - - fArray->Insert(value); - return B_OK; -} - - -status_t -BlockArray::Remove(off_t value) -{ - if (fArray == NULL) - return B_ENTRY_NOT_FOUND; - - return fArray->Remove(value) ? (status_t)B_OK : (status_t)B_ENTRY_NOT_FOUND; -} - - -void -BlockArray::MakeEmpty() -{ - fArray->count = 0; -} - - -// #pragma mark - - - -extern "C" size_t -strlcpy(char *dest, char const *source, size_t length) -{ - if (length == 0) - return strlen(source); - - size_t i = 0; - for (; i < length - 1 && source[i]; i++) - dest[i] = source[i]; - - dest[i] = '\0'; - - return i + strlen(source + i); -} - diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Utility.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Utility.h deleted file mode 100644 index 21d3614297..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Utility.h +++ /dev/null @@ -1,160 +0,0 @@ -/* Utility - some helper classes - * - * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de - * This file may be used under the terms of the MIT License. - */ -#ifndef UTILITY_H -#define UTILITY_H - - -#include "bfs_endian.h" - - -// Simple array, used for the duplicate handling in the B+Tree, -// and for the log entries. - -struct sorted_array { - public: - off_t count; - -#if __MWERKS__ - off_t values[1]; -#else - off_t values[0]; -#endif - - off_t CountItems() const { return BFS_ENDIAN_TO_HOST_INT64(count); } - off_t ValueAt(int32 index) const { return BFS_ENDIAN_TO_HOST_INT64(values[index]); } - inline int32 Find(off_t value) const; - void Insert(off_t value); - bool Remove(off_t value); - - private: - bool _FindInternal(off_t value, int32 &index) const; -}; - - -inline int32 -sorted_array::Find(off_t value) const -{ - int32 i; - return _FindInternal(value, i) ? i : -1; -} - - -// The BlockArray reserves a multiple of "blockSize" and -// maintain array size for new entries. -// This is used for the in-memory log entries before they -// are written to disk. - -class BlockArray { - public: - BlockArray(int32 blockSize); - ~BlockArray(); - - int32 Find(off_t value); - status_t Insert(off_t value); - status_t Remove(off_t value); - - void MakeEmpty(); - - int32 CountItems() const { return fArray != NULL ? fArray->CountItems() : 0; } - int32 BlocksUsed() const { return fArray != NULL ? ((fArray->CountItems() + 1) * sizeof(off_t) + fBlockSize - 1) / fBlockSize : 0; } - sorted_array *Array() const { return fArray; } - int32 Size() const { return fSize; } - - private: - sorted_array *fArray; - int32 fBlockSize; - int32 fSize; - int32 fMaxBlocks; -}; - - -// Some atomic operations that are somehow missing in BeOS: -// -// _atomic_test_and_set(value, newValue, testAgainst) -// sets "value" to "newValue", if "value" is equal to "testAgainst" -// _atomic_set(value, newValue) -// sets "value" to "newValue" - -#if _NO_INLINE_ASM - // Note that these atomic versions *don't* work as expected! - // They are only used for single processor user space tests - // (and don't even work correctly there) - inline int32 - _atomic_test_and_set(volatile int32 *value, int32 newValue, int32 testAgainst) - { - int32 oldValue = *value; - if (oldValue == testAgainst) - *value = newValue; - - return oldValue; - } - - inline void - _atomic_set(volatile int32 *value, int32 newValue) - { - *value = newValue; - } -#elif __INTEL__ - inline int32 - _atomic_test_and_set(volatile int32 *value, int32 newValue, int32 testAgainst) - { - int32 oldValue; - asm volatile("lock; cmpxchg %%ecx, (%%edx)" - : "=a" (oldValue) : "a" (testAgainst), "c" (newValue), "d" (value)); - return oldValue; - } - - inline void - _atomic_set(volatile int32 *value, int32 newValue) - { - asm volatile("lock; xchg %%eax, (%%edx)" - : : "a" (newValue), "d" (value)); - } -#elif __POWERPC__ && __MWERKS__ /* GCC has different assembler syntax */ -inline asm int32 - _atomic_set(volatile int32 *value, int32) - { - loop: - dcbf r0, r3; - lwarx r0, 0, r3; - stwcx. r4, 0, r3; - bc 5, 2, loop - mr r3,r5; - isync; - blr; - } - -inline asm int32 - _atomic_test_and_set(volatile int32 *value, int32 newValue, int32 testAgainst) - { - loop: - dcbf r0, r3; - lwarx r0, 0, r3; - cmpw r5, r0; - bne no_dice; - stwcx. r4, 0, r3; - bc 5, 2, loop - - mr r3,r0; - isync; - blr; - - no_dice: - stwcx. r0, 0, r3; - mr r3,r0; - isync; - blr; - } - -#else -# error The macros _atomic_set(), and _atomic_test_and_set() are not defined for the target processor -#endif - - -extern "C" size_t strlcpy(char *dest, char const *source, size_t length); - - -#endif /* UTILITY_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Volume.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/Volume.cpp deleted file mode 100644 index a5d6f9e8ff..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Volume.cpp +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved. - * This file may be used under the terms of the MIT License. - */ - -//! superblock, mounting, etc. - - -#include "Debug.h" -#include "Volume.h" -#include "Journal.h" -#include "Inode.h" -#include "Query.h" - -#include -#include -#include - -#include -#include -#include -#include - - -static const int32 kDesiredAllocationGroups = 56; - // This is the number of allocation groups that will be tried - // to be given for newly initialized disks. - // That's only relevant for smaller disks, though, since any - // of today's disk sizes already reach the maximum length - // of an allocation group (65536 blocks). - // It seems to create appropriate numbers for smaller disks - // with this setting, though (i.e. you can create a 400 MB - // file on a 1 GB disk without the need for double indirect - // blocks). - - -class DeviceOpener { - public: - DeviceOpener(const char *device, int mode); - ~DeviceOpener(); - - int Open(const char *device, int mode); - status_t InitCache(off_t numBlocks); - void RemoveCache(int mode); - - void Keep(); - - int Device() const { return fDevice; } - - status_t GetSize(off_t *_size, uint32 *_blockSize = NULL); - - private: - int fDevice; - bool fCached; -}; - - -DeviceOpener::DeviceOpener(const char *device, int mode) - : - fCached(false) -{ - Open(device, mode); -} - - -DeviceOpener::~DeviceOpener() -{ - if (fDevice >= B_OK) { - close(fDevice); - if (fCached) - remove_cached_device_blocks(fDevice, NO_WRITES); - } -} - - -int -DeviceOpener::Open(const char *device, int mode) -{ - fDevice = open(device, mode); - return fDevice; -} - - -status_t -DeviceOpener::InitCache(off_t numBlocks) -{ - if (init_cache_for_device(fDevice, numBlocks) == B_OK) { - fCached = true; - return B_OK; - } - - return B_ERROR; -} - - -void -DeviceOpener::RemoveCache(int mode) -{ - if (!fCached) - return; - - remove_cached_device_blocks(fDevice, mode); - fCached = false; -} - - -void -DeviceOpener::Keep() -{ - fDevice = -1; -} - - -/** Returns the size of the device in bytes. It uses B_GET_GEOMETRY - * to compute the size, or fstat() if that failed. - */ - -status_t -DeviceOpener::GetSize(off_t *_size, uint32 *_blockSize) -{ - device_geometry geometry; - if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) { - // maybe it's just a file - struct stat stat; - if (fstat(fDevice, &stat) < 0) - return B_ERROR; - - if (_size) - *_size = stat.st_size; - if (_blockSize) // that shouldn't cause us any problems - *_blockSize = 512; - - return B_OK; - } - - if (_size) { - *_size = 1LL * geometry.head_count * geometry.cylinder_count - * geometry.sectors_per_track * geometry.bytes_per_sector; - } - if (_blockSize) - *_blockSize = geometry.bytes_per_sector; - - return B_OK; -} - - -// #pragma mark - - - -bool -disk_super_block::IsValid() -{ - if (Magic1() != (int32)SUPER_BLOCK_MAGIC1 - || Magic2() != (int32)SUPER_BLOCK_MAGIC2 - || Magic3() != (int32)SUPER_BLOCK_MAGIC3 - || (int32)block_size != inode_size - || ByteOrder() != SUPER_BLOCK_FS_LENDIAN - || (1UL << BlockShift()) != BlockSize() - || AllocationGroups() < 1 - || AllocationGroupShift() < 1 - || BlocksPerAllocationGroup() < 1 - || NumBlocks() < 10 - || AllocationGroups() != divide_roundup(NumBlocks(), - 1L << AllocationGroupShift())) - return false; - - return true; -} - - -void -disk_super_block::Initialize(const char *diskName, off_t numBlocks, uint32 blockSize) -{ - memset(this, 0, sizeof(disk_super_block)); - - magic1 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC1); - magic2 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC2); - magic3 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC3); - fs_byte_order = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_FS_LENDIAN); - flags = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_DISK_CLEAN); - - strlcpy(name, diskName, sizeof(name)); - - int32 blockShift = 9; - while ((1UL << blockShift) < blockSize) { - blockShift++; - } - - block_size = inode_size = HOST_ENDIAN_TO_BFS_INT32(blockSize); - block_shift = HOST_ENDIAN_TO_BFS_INT32(blockShift); - - num_blocks = HOST_ENDIAN_TO_BFS_INT64(numBlocks); - used_blocks = 0; - - // Get the minimum ag_shift (that's determined by the block size) - - int32 bitsPerBlock = blockSize << 3; - off_t bitmapBlocks = (numBlocks + bitsPerBlock - 1) / bitsPerBlock; - int32 blocksPerGroup = 1; - int32 groupShift = 13; - - for (int32 i = 8192; i < bitsPerBlock; i *= 2) { - groupShift++; - } - - // Many allocation groups help applying allocation policies, but if - // they are too small, we will need to many block_runs to cover large - // files (see above to get an explanation of the kDesiredAllocationGroups - // constant). - - int32 numGroups; - - while (true) { - numGroups = (bitmapBlocks + blocksPerGroup - 1) / blocksPerGroup; - if (numGroups > kDesiredAllocationGroups) { - if (groupShift == 16) - break; - - groupShift++; - blocksPerGroup *= 2; - } else - break; - } - - num_ags = HOST_ENDIAN_TO_BFS_INT32(numGroups); - blocks_per_ag = HOST_ENDIAN_TO_BFS_INT32(blocksPerGroup); - ag_shift = HOST_ENDIAN_TO_BFS_INT32(groupShift); -} - - -// #pragma mark - - - -Volume::Volume(dev_t id) - : - fID(id), - fBlockAllocator(this), - fLock("bfs volume"), - fRootNode(NULL), - fIndicesNode(NULL), - fDirtyCachedBlocks(0), - fUniqueID(0), - fFlags(0) -{ -} - - -Volume::~Volume() -{ -} - - -bool -Volume::IsValidSuperBlock() -{ - return fSuperBlock.IsValid(); -} - - -void -Volume::Panic() -{ - FATAL(("we have to panic... switch to read-only mode!\n")); - fFlags |= VOLUME_READ_ONLY; -#ifdef USER - debugger("BFS panics!"); -#elif defined(DEBUG) - kernel_debugger("BFS panics!"); -#endif -} - - -status_t -Volume::Mount(const char *deviceName, uint32 flags) -{ - if (flags & B_MOUNT_READ_ONLY) - fFlags |= VOLUME_READ_ONLY; - - // ToDo: validate the FS in write mode as well! -#if (B_HOST_IS_LENDIAN && defined(BFS_BIG_ENDIAN_ONLY)) \ - || (B_HOST_IS_BENDIAN && defined(BFS_LITTLE_ENDIAN_ONLY)) - // in big endian mode, we only mount read-only for now - flags |= B_MOUNT_READ_ONLY; -#endif - - DeviceOpener opener(deviceName, flags & B_MOUNT_READ_ONLY ? O_RDONLY : O_RDWR); - - // if we couldn't open the device, try read-only (don't rely on a specific error code) - if (opener.Device() < B_OK && (flags & B_MOUNT_READ_ONLY) == 0) { - opener.Open(deviceName, O_RDONLY); - fFlags |= VOLUME_READ_ONLY; - } - - fDevice = opener.Device(); - if (fDevice < B_OK) - RETURN_ERROR(fDevice); - - // check if it's a regular file, and if so, disable the cache for the - // underlaying file system - struct stat stat; - if (fstat(fDevice, &stat) < 0) - RETURN_ERROR(B_ERROR); - -#ifndef NO_FILE_UNCACHED_IO - if (stat.st_mode & S_FILE && ioctl(fDevice, IOCTL_FILE_UNCACHED_IO, NULL) < 0) { - // mount read-only if the cache couldn't be disabled -# ifdef DEBUG - FATAL(("couldn't disable cache for image file - system may dead-lock!\n")); -# else - FATAL(("couldn't disable cache for image file!\n")); - Panic(); -# endif - } -#endif - - // read the superblock - if (Identify(fDevice, &fSuperBlock) != B_OK) { - FATAL(("invalid superblock!\n")); - return B_BAD_VALUE; - } - - // initialize short hands to the superblock (to save byte swapping) - fBlockSize = fSuperBlock.BlockSize(); - fBlockShift = fSuperBlock.BlockShift(); - fAllocationGroupShift = fSuperBlock.AllocationGroupShift(); - - // check if the device size is large enough to hold the file system - off_t diskSize; - if (opener.GetSize(&diskSize) < B_OK) - RETURN_ERROR(B_ERROR); - if (diskSize < (NumBlocks() << BlockShift())) - RETURN_ERROR(B_BAD_VALUE); - - // set the current log pointers, so that journaling will work correctly - fLogStart = fSuperBlock.LogStart(); - fLogEnd = fSuperBlock.LogEnd(); - - if (opener.InitCache(NumBlocks()) != B_OK) - return B_ERROR; - - fJournal = new Journal(this); - // replaying the log is the first thing we will do on this disk - if (fJournal && fJournal->InitCheck() < B_OK - || fBlockAllocator.Initialize() < B_OK) { - // ToDo: improve error reporting for a bad journal - FATAL(("could not initialize journal/block bitmap allocator!\n")); - return B_NO_MEMORY; - } - - status_t status = B_OK; - - fRootNode = new Inode(this, ToVnode(Root())); - if (fRootNode && fRootNode->InitCheck() == B_OK) { - status = new_vnode(fID, ToVnode(Root()), (void *)fRootNode); - if (status == B_OK) { - // try to get indices root dir - - // question: why doesn't get_vnode() work here?? - // answer: we have not yet backpropagated the pointer to the - // volume in bfs_mount(), so bfs_read_vnode() can't get it. - // But it's not needed to do that anyway. - - if (!Indices().IsZero()) - fIndicesNode = new Inode(this, ToVnode(Indices())); - - if (fIndicesNode == NULL - || fIndicesNode->InitCheck() < B_OK - || !fIndicesNode->IsContainer()) { - INFORM(("bfs: volume doesn't have indices!\n")); - - if (fIndicesNode) { - // if this is the case, the index root node is gone bad, and - // BFS switch to read-only mode - fFlags |= VOLUME_READ_ONLY; - delete fIndicesNode; - fIndicesNode = NULL; - } - } - - // all went fine - opener.Keep(); - return B_OK; - } else - FATAL(("could not create root node: new_vnode() failed!\n")); - - delete fRootNode; - } else { - status = B_BAD_VALUE; - FATAL(("could not create root node!\n")); - } - - return status; -} - - -status_t -Volume::Unmount() -{ - // This will also flush the log & all blocks to disk - delete fJournal; - fJournal = NULL; - - delete fIndicesNode; - - remove_cached_device_blocks(fDevice, IsReadOnly() ? NO_WRITES : ALLOW_WRITES); - close(fDevice); - - return B_OK; -} - - -status_t -Volume::Sync() -{ - return fJournal->FlushLogAndBlocks(); -} - - -status_t -Volume::ValidateBlockRun(block_run run) -{ - if (run.AllocationGroup() < 0 || run.AllocationGroup() > (int32)AllocationGroups() - || run.Start() > (1UL << AllocationGroupShift()) - || run.length == 0 - || uint32(run.Length() + run.Start()) > (1UL << AllocationGroupShift())) { - Panic(); - FATAL(("*** invalid run(%ld,%d,%d)\n", run.AllocationGroup(), run.Start(), run.Length())); - return B_BAD_DATA; - } - return B_OK; -} - - -block_run -Volume::ToBlockRun(off_t block) const -{ - block_run run; - run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(block >> AllocationGroupShift()); - run.start = HOST_ENDIAN_TO_BFS_INT16(block & ((1LL << AllocationGroupShift()) - 1)); - run.length = HOST_ENDIAN_TO_BFS_INT16(1); - return run; -} - - -status_t -Volume::CreateIndicesRoot(Transaction *transaction) -{ - off_t id; - status_t status = Inode::Create(transaction, NULL, NULL, - S_INDEX_DIR | S_STR_INDEX | S_DIRECTORY | 0700, 0, 0, &id, &fIndicesNode); - if (status < B_OK) - RETURN_ERROR(status); - - fSuperBlock.indices = ToBlockRun(id); - return WriteSuperBlock(); -} - - -status_t -Volume::AllocateForInode(Transaction *transaction, const Inode *parent, mode_t type, block_run &run) -{ - return fBlockAllocator.AllocateForInode(transaction, &parent->BlockRun(), type, run); -} - - -status_t -Volume::WriteSuperBlock() -{ - if (write_pos(fDevice, 512, &fSuperBlock, sizeof(disk_super_block)) != sizeof(disk_super_block)) - return B_IO_ERROR; - - return B_OK; -} - - -void -Volume::UpdateLiveQueries(Inode *inode, const char *attribute, int32 type, const uint8 *oldKey, - size_t oldLength, const uint8 *newKey, size_t newLength) -{ - if (fQueryLock.Lock() < B_OK) - return; - - Query *query = NULL; - while ((query = fQueries.Next(query)) != NULL) - query->LiveUpdate(inode, attribute, type, oldKey, oldLength, newKey, newLength); - - fQueryLock.Unlock(); -} - - -/** Checks if there is a live query whose results depend on the presence - * or value of the specified attribute. - * Don't use it if you already have all the data together to evaluate - * the queries - it wouldn't safe you anything in this case. - */ - -bool -Volume::CheckForLiveQuery(const char *attribute) -{ - // ToDo: check for a live query that depends on the specified attribute - return true; -} - - -void -Volume::AddQuery(Query *query) -{ - if (fQueryLock.Lock() < B_OK) - return; - - fQueries.Add(query); - - fQueryLock.Unlock(); -} - - -void -Volume::RemoveQuery(Query *query) -{ - if (fQueryLock.Lock() < B_OK) - return; - - fQueries.Remove(query); - - fQueryLock.Unlock(); -} - - -// #pragma mark - -// Disk scanning and initialization - - -status_t -Volume::Identify(int fd, disk_super_block *superBlock) -{ - char buffer[1024]; - if (read_pos(fd, 0, buffer, sizeof(buffer)) != sizeof(buffer)) - return B_IO_ERROR; - - // Note: that does work only for x86, for PowerPC, the superblock - // may be located at offset 0! - memcpy(superBlock, buffer + 512, sizeof(disk_super_block)); - if (!superBlock->IsValid()) { -#ifndef BFS_LITTLE_ENDIAN_ONLY - memcpy(superBlock, buffer, sizeof(disk_super_block)); - if (!superBlock->IsValid()) - return B_BAD_VALUE; -#else - return B_BAD_VALUE; -#endif - } - - return B_OK; -} - - -#ifdef USER -extern "C" void kill_device_vnodes(dev_t id); - // This call is only available in the userland fs_shell - -status_t -Volume::Initialize(const char *device, const char *name, uint32 blockSize, uint32 flags) -{ - // although there is no really good reason for it, we won't - // accept '/' in disk names (mkbfs does this, too - and since - // Tracker names mounted volumes like their name) - if (strchr(name, '/') != NULL) - return B_BAD_VALUE; - - if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096 && blockSize != 8192) - return B_BAD_VALUE; - - DeviceOpener opener(device, O_RDWR); - if (opener.Device() < B_OK) - return B_BAD_VALUE; - - fDevice = opener.Device(); - - uint32 deviceBlockSize; - off_t deviceSize; - if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK) - return B_ERROR; - - off_t numBlocks = deviceSize / blockSize; - - // create valid superblock - - fSuperBlock.Initialize(name, numBlocks, blockSize); - - // initialize short hands to the superblock (to save byte swapping) - fBlockSize = fSuperBlock.BlockSize(); - fBlockShift = fSuperBlock.BlockShift(); - fAllocationGroupShift = fSuperBlock.AllocationGroupShift(); - - // since the allocator has not been initialized yet, we - // cannot use BlockAllocator::BitmapSize() here - fSuperBlock.log_blocks = ToBlockRun(AllocationGroups() - * fSuperBlock.BlocksPerAllocationGroup() + 1); - fSuperBlock.log_blocks.length = HOST_ENDIAN_TO_BFS_INT16(2048); - // ToDo: set the log size depending on the disk size - fSuperBlock.log_start = fSuperBlock.log_end = HOST_ENDIAN_TO_BFS_INT64(ToBlock(Log())); - - // set the current log pointers, so that journaling will work correctly - fLogStart = fSuperBlock.LogStart(); - fLogEnd = fSuperBlock.LogEnd(); - - if (!IsValidSuperBlock()) - RETURN_ERROR(B_ERROR); - - if (opener.InitCache(numBlocks) != B_OK) - return B_ERROR; - - fJournal = new Journal(this); - if (fJournal == NULL || fJournal->InitCheck() < B_OK) - RETURN_ERROR(B_ERROR); - - // ready to write data to disk - - Transaction transaction(this, 0); - - if (fBlockAllocator.InitializeAndClearBitmap(transaction) < B_OK) - RETURN_ERROR(B_ERROR); - - off_t id; - status_t status = Inode::Create(&transaction, NULL, NULL, - S_DIRECTORY | 0755, 0, 0, &id, &fRootNode); - if (status < B_OK) - RETURN_ERROR(status); - - fSuperBlock.root_dir = ToBlockRun(id); - - if ((flags & VOLUME_NO_INDICES) == 0) { - // The indices root directory will be created automatically - // when the standard indices are created (or any other). - Index index(this); - status = index.Create(&transaction, "name", B_STRING_TYPE); - if (status < B_OK) - return status; - - status = index.Create(&transaction, "last_modified", B_INT64_TYPE); - if (status < B_OK) - return status; - - status = index.Create(&transaction, "size", B_INT64_TYPE); - if (status < B_OK) - return status; - } - - WriteSuperBlock(); - transaction.Done(); - - put_vnode(ID(), fRootNode->ID()); - if (fIndicesNode != NULL) - put_vnode(ID(), fIndicesNode->ID()); - - kill_device_vnodes(ID()); - // This call is only available in the userland fs_shell - - Sync(); - opener.RemoveCache(ALLOW_WRITES); - return B_OK; -} -#endif diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/Volume.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/Volume.h deleted file mode 100644 index e98f986af4..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/Volume.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved. - * This file may be used under the terms of the MIT License. - */ -#ifndef VOLUME_H -#define VOLUME_H - - -#include -#include "fsproto.h" - -#include "cache.h" - -#include "bfs.h" -#include "BlockAllocator.h" -#include "BufferPool.h" -#include "Chain.h" - -class Journal; -class Inode; -class Query; - -enum volume_flags { - VOLUME_READ_ONLY = 0x0001 -}; - -enum volume_initialize_flags { - VOLUME_NO_INDICES = 0x0001, -}; - -class Volume { - public: - Volume(dev_t id); - ~Volume(); - - status_t Mount(const char *device, uint32 flags); - status_t Unmount(); - status_t Initialize(const char *device, const char *name, - uint32 blockSize, uint32 flags); - - bool IsValidSuperBlock(); - bool IsReadOnly() const; - void Panic(); - RecursiveLock &Lock(); - - block_run Root() const { return fSuperBlock.root_dir; } - Inode *RootNode() const { return fRootNode; } - block_run Indices() const { return fSuperBlock.indices; } - Inode *IndicesNode() const { return fIndicesNode; } - block_run Log() const { return fSuperBlock.log_blocks; } - vint32 &LogStart() { return fLogStart; } - vint32 &LogEnd() { return fLogEnd; } - int Device() const { return fDevice; } - - dev_t ID() const { return fID; } - const char *Name() const { return fSuperBlock.name; } - - off_t NumBlocks() const { return fSuperBlock.NumBlocks(); } - off_t UsedBlocks() const { return fSuperBlock.UsedBlocks(); } - off_t FreeBlocks() const { return NumBlocks() - UsedBlocks(); } - - uint32 BlockSize() const { return fBlockSize; } - uint32 BlockShift() const { return fBlockShift; } - uint32 InodeSize() const { return fSuperBlock.InodeSize(); } - uint32 AllocationGroups() const { return fSuperBlock.AllocationGroups(); } - uint32 AllocationGroupShift() const { return fAllocationGroupShift; } - disk_super_block &SuperBlock() { return fSuperBlock; } - - off_t ToOffset(block_run run) const { return ToBlock(run) << BlockShift(); } - off_t ToBlock(block_run run) const { return ((((off_t)run.AllocationGroup()) << AllocationGroupShift()) | (off_t)run.Start()); } - block_run ToBlockRun(off_t block) const; - status_t ValidateBlockRun(block_run run); - - off_t ToVnode(block_run run) const { return ToBlock(run); } - off_t ToVnode(off_t block) const { return block; } - off_t VnodeToBlock(vnode_id id) const { return (off_t)id; } - - status_t CreateIndicesRoot(Transaction *transaction); - - // block bitmap - BlockAllocator &Allocator(); - status_t AllocateForInode(Transaction *transaction, const Inode *parent, - mode_t type, block_run &run); - status_t AllocateForInode(Transaction *transaction, const block_run *parent, - mode_t type, block_run &run); - status_t Allocate(Transaction *transaction,const Inode *inode, - off_t numBlocks, block_run &run, uint16 minimum = 1); - status_t Free(Transaction *transaction, block_run run); - - // cache access - status_t WriteSuperBlock(); - status_t WriteBlocks(off_t blockNumber, const uint8 *block, uint32 numBlocks); - void WriteCachedBlocksIfNecessary(); - status_t FlushDevice(); - - // queries - void UpdateLiveQueries(Inode *inode, const char *attribute, int32 type, - const uint8 *oldKey, size_t oldLength, - const uint8 *newKey, size_t newLength); - bool CheckForLiveQuery(const char *attribute); - void AddQuery(Query *query); - void RemoveQuery(Query *query); - - status_t Sync(); - Journal *GetJournal(off_t refBlock) const; - - BufferPool &Pool(); - - uint32 GetUniqueID(); - - static status_t Identify(int fd, disk_super_block *superBlock); - - protected: - dev_t fID; - int fDevice; - disk_super_block fSuperBlock; - - uint32 fBlockSize; - uint32 fBlockShift; - uint32 fAllocationGroupShift; - - BlockAllocator fBlockAllocator; - RecursiveLock fLock; - Journal *fJournal; - vint32 fLogStart, fLogEnd; - - Inode *fRootNode; - Inode *fIndicesNode; - - vint32 fDirtyCachedBlocks; - - SimpleLock fQueryLock; - Chain fQueries; - - int32 fUniqueID; - uint32 fFlags; - - BufferPool fBufferPool; -}; - - -// inline functions - -inline bool -Volume::IsReadOnly() const -{ - return fFlags & VOLUME_READ_ONLY; -} - - -inline RecursiveLock & -Volume::Lock() -{ - return fLock; -} - - -inline BlockAllocator & -Volume::Allocator() -{ - return fBlockAllocator; -} - - -inline status_t -Volume::AllocateForInode(Transaction *transaction, const block_run *parent, mode_t type, block_run &run) -{ - return fBlockAllocator.AllocateForInode(transaction, parent, type, run); -} - - -inline status_t -Volume::Allocate(Transaction *transaction, const Inode *inode, off_t numBlocks, block_run &run, uint16 minimum) -{ - return fBlockAllocator.Allocate(transaction, inode, numBlocks, run, minimum); -} - - -inline status_t -Volume::Free(Transaction *transaction, block_run run) -{ - return fBlockAllocator.Free(transaction, run); -} - - -inline status_t -Volume::WriteBlocks(off_t blockNumber, const uint8 *block, uint32 numBlocks) -{ - atomic_add(&fDirtyCachedBlocks, numBlocks); - return cached_write(fDevice, blockNumber, block, numBlocks, fSuperBlock.block_size); -} - - -inline void -Volume::WriteCachedBlocksIfNecessary() -{ - // the specific values are only valid for the current BeOS cache - if (fDirtyCachedBlocks > 128) { - force_cache_flush(fDevice, false); - atomic_add(&fDirtyCachedBlocks, -64); - } -} - - -inline status_t -Volume::FlushDevice() -{ - fDirtyCachedBlocks = 0; - return flush_device(fDevice, 0); -} - - -inline Journal * -Volume::GetJournal(off_t /*refBlock*/) const -{ - return fJournal; -} - - -inline BufferPool & -Volume::Pool() -{ - return fBufferPool; -} - - -inline uint32 -Volume::GetUniqueID() -{ - return atomic_add(&fUniqueID, 1); -} - -#endif /* VOLUME_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs.h deleted file mode 100644 index 0ccc9a643e..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs.h +++ /dev/null @@ -1,400 +0,0 @@ -#ifndef BFS_H -#define BFS_H -/* bfs - BFS definitions and helper functions -** -** Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de -** Parts of this code is based on work previously done by Marcus Overhagen -** -** Copyright 2001, pinc Software. All Rights Reserved. -** This file may be used under the terms of the MIT License. -*/ - - -#include - -#include "bfs_endian.h" - - -#ifndef B_BEOS_VERSION_DANO -# define B_BAD_DATA B_ERROR -#endif - -// ToDo: temporary fix! (missing but public ioctls) -#define IOCTL_FILE_UNCACHED_IO 10000 - -struct block_run { - int32 allocation_group; - uint16 start; - uint16 length; - - int32 AllocationGroup() const { return BFS_ENDIAN_TO_HOST_INT32(allocation_group); } - uint16 Start() const { return BFS_ENDIAN_TO_HOST_INT16(start); } - uint16 Length() const { return BFS_ENDIAN_TO_HOST_INT16(length); } - - inline bool operator==(const block_run &run) const; - inline bool operator!=(const block_run &run) const; - inline bool IsZero(); - inline bool MergeableWith(block_run run) const; - inline void SetTo(int32 group, uint16 start, uint16 length = 1); - - inline static block_run Run(int32 group, uint16 start, uint16 length = 1); - // can't have a constructor because it's used in a union -} _PACKED; - -typedef block_run inode_addr; - -// Since the block_run::length field spans 16 bits, the largest number of -// blocks covered by a block_run is 65535 (as long as we don't want to -// break compatibility and take a zero length for 65536). -#define MAX_BLOCK_RUN_LENGTH 65535 - -//************************************** - - -#define BFS_DISK_NAME_LENGTH 32 - -struct disk_super_block { - char name[BFS_DISK_NAME_LENGTH]; - int32 magic1; - int32 fs_byte_order; - uint32 block_size; - uint32 block_shift; - off_t num_blocks; - off_t used_blocks; - int32 inode_size; - int32 magic2; - int32 blocks_per_ag; - int32 ag_shift; - int32 num_ags; - int32 flags; - block_run log_blocks; - off_t log_start; - off_t log_end; - int32 magic3; - inode_addr root_dir; - inode_addr indices; - int32 pad[8]; - - int32 Magic1() const { return BFS_ENDIAN_TO_HOST_INT32(magic1); } - int32 Magic2() const { return BFS_ENDIAN_TO_HOST_INT32(magic2); } - int32 Magic3() const { return BFS_ENDIAN_TO_HOST_INT32(magic3); } - int32 ByteOrder() const { return BFS_ENDIAN_TO_HOST_INT32(fs_byte_order); } - uint32 BlockSize() const { return BFS_ENDIAN_TO_HOST_INT32(block_size); } - uint32 BlockShift() const { return BFS_ENDIAN_TO_HOST_INT32(block_shift); } - off_t NumBlocks() const { return BFS_ENDIAN_TO_HOST_INT64(num_blocks); } - off_t UsedBlocks() const { return BFS_ENDIAN_TO_HOST_INT64(used_blocks); } - int32 InodeSize() const { return BFS_ENDIAN_TO_HOST_INT32(inode_size); } - int32 BlocksPerAllocationGroup() const { return BFS_ENDIAN_TO_HOST_INT32(blocks_per_ag); } - int32 AllocationGroups() const { return BFS_ENDIAN_TO_HOST_INT32(num_ags); } - int32 AllocationGroupShift() const { return BFS_ENDIAN_TO_HOST_INT32(ag_shift); } - int32 Flags() const { return BFS_ENDIAN_TO_HOST_INT32(flags); } - off_t LogStart() const { return BFS_ENDIAN_TO_HOST_INT64(log_start); } - off_t LogEnd() const { return BFS_ENDIAN_TO_HOST_INT64(log_end); } - - // implemented in Volume.cpp: - bool IsValid(); - void Initialize(const char *name, off_t numBlocks, uint32 blockSize); -} _PACKED; - -#define SUPER_BLOCK_FS_LENDIAN 'BIGE' /* BIGE */ - -#define SUPER_BLOCK_MAGIC1 'BFS1' /* BFS1 */ -#define SUPER_BLOCK_MAGIC2 0xdd121031 -#define SUPER_BLOCK_MAGIC3 0x15b6830e - -#define SUPER_BLOCK_DISK_CLEAN 'CLEN' /* CLEN */ -#define SUPER_BLOCK_DISK_DIRTY 'DIRT' /* DIRT */ - -//************************************** - -#define NUM_DIRECT_BLOCKS 12 - -struct data_stream { - block_run direct[NUM_DIRECT_BLOCKS]; - off_t max_direct_range; - block_run indirect; - off_t max_indirect_range; - block_run double_indirect; - off_t max_double_indirect_range; - off_t size; - - off_t MaxDirectRange() const { return BFS_ENDIAN_TO_HOST_INT64(max_direct_range); } - off_t MaxIndirectRange() const { return BFS_ENDIAN_TO_HOST_INT64(max_indirect_range); } - off_t MaxDoubleIndirectRange() const { return BFS_ENDIAN_TO_HOST_INT64(max_double_indirect_range); } - off_t Size() const { return BFS_ENDIAN_TO_HOST_INT64(size); } -} _PACKED; - -// This defines the size of the indirect and double indirect -// blocks. Note: the code may not work correctly at some places -// if this value is changed (it's not tested). -#define NUM_ARRAY_BLOCKS 4 -#define ARRAY_BLOCKS_SHIFT 2 -#define INDIRECT_BLOCKS_SHIFT (ARRAY_BLOCKS_SHIFT + ARRAY_BLOCKS_SHIFT) - -//************************************** - -struct bfs_inode; - -struct small_data { - uint32 type; - uint16 name_size; - uint16 data_size; - -#if !__MWERKS__ //-- mwcc doesn't support thingy[0], so we patch Name() instead - char name[0]; // name_size long, followed by data -#endif - - uint32 Type() const { return BFS_ENDIAN_TO_HOST_INT32(type); } - uint16 NameSize() const { return BFS_ENDIAN_TO_HOST_INT16(name_size); } - uint16 DataSize() const { return BFS_ENDIAN_TO_HOST_INT16(data_size); } - - inline char *Name() const; - inline uint8 *Data() const; - inline uint32 Size() const; - inline small_data *Next() const; - inline bool IsLast(const bfs_inode *inode) const; -} _PACKED; - -// the file name is part of the small_data structure -#define FILE_NAME_TYPE 'CSTR' -#define FILE_NAME_NAME 0x13 -#define FILE_NAME_NAME_LENGTH 1 - - -//************************************** - -class Volume; - -#define SHORT_SYMLINK_NAME_LENGTH 144 // length incl. terminating '\0' - -struct bfs_inode { - int32 magic1; - inode_addr inode_num; - int32 uid; - int32 gid; - int32 mode; // see sys/stat.h - int32 flags; - bigtime_t create_time; - bigtime_t last_modified_time; - inode_addr parent; - inode_addr attributes; - uint32 type; // attribute type - - int32 inode_size; - uint32 etc; // a pointer to the Inode object during construction - - union { - data_stream data; - char short_symlink[SHORT_SYMLINK_NAME_LENGTH]; - }; - int32 pad[4]; - -#if !__MWERKS__ - small_data small_data_start[0]; -#endif - - int32 Magic1() const { return BFS_ENDIAN_TO_HOST_INT32(magic1); } - int32 UserID() const { return BFS_ENDIAN_TO_HOST_INT32(uid); } - int32 GroupID() const { return BFS_ENDIAN_TO_HOST_INT32(gid); } - int32 Mode() const { return BFS_ENDIAN_TO_HOST_INT32(mode); } - int32 Flags() const { return BFS_ENDIAN_TO_HOST_INT32(flags); } - int32 Type() const { return BFS_ENDIAN_TO_HOST_INT32(type); } - int32 InodeSize() const { return BFS_ENDIAN_TO_HOST_INT32(inode_size); } - bigtime_t LastModifiedTime() const { return BFS_ENDIAN_TO_HOST_INT64(last_modified_time); } - bigtime_t CreateTime() const { return BFS_ENDIAN_TO_HOST_INT64(create_time); } - - inline small_data *SmallDataStart(); - - status_t InitCheck(Volume *volume); - // defined in Inode.cpp -} _PACKED; - -#define INODE_MAGIC1 0x3bbe0ad9 -#define INODE_TIME_SHIFT 16 -#define INODE_TIME_MASK 0xffff -#define INODE_FILE_NAME_LENGTH 256 - -enum inode_flags { - INODE_IN_USE = 0x00000001, // always set - INODE_ATTR_INODE = 0x00000004, - INODE_LOGGED = 0x00000008, // log changes to the data stream - INODE_DELETED = 0x00000010, - INODE_NOT_READY = 0x00000020, // used during Inode construction - INODE_LONG_SYMLINK = 0x00000040, // symlink in data stream - - INODE_PERMANENT_FLAGS = 0x0000ffff, - - INODE_NO_CACHE = 0x00010000, - INODE_WAS_WRITTEN = 0x00020000, - INODE_NO_TRANSACTION = 0x00040000, - INODE_DONT_FREE_SPACE = 0x00080000, // only used by the "chkbfs" functionality - INODE_CHKBFS_RUNNING = 0x00200000, -}; - -//************************************** - -struct file_cookie { - bigtime_t last_notification; - off_t last_size; - int open_mode; -}; - -// notify every second if the file size has changed -#define INODE_NOTIFICATION_INTERVAL 1000000LL - -//************************************** - - -inline int32 -divide_roundup(int32 num,int32 divisor) -{ - return (num + divisor - 1) / divisor; -} - -inline int64 -divide_roundup(int64 num,int32 divisor) -{ - return (num + divisor - 1) / divisor; -} - -inline int -get_shift(uint64 i) -{ - int c; - c = 0; - while (i > 1) { - i >>= 1; - c++; - } - return c; -} - -inline int32 -round_up(uint32 data) -{ - // rounds up to the next off_t boundary - return (data + sizeof(off_t) - 1) & ~(sizeof(off_t) - 1); -} - - -/************************ block_run inline functions ************************/ -// #pragma mark - - - -inline bool -block_run::operator==(const block_run &run) const -{ - return allocation_group == run.allocation_group - && start == run.start - && length == run.length; -} - - -inline bool -block_run::operator!=(const block_run &run) const -{ - return allocation_group != run.allocation_group - || start != run.start - || length != run.length; -} - - -inline bool -block_run::IsZero() -{ - return allocation_group == 0 && start == 0 && length == 0; -} - - -inline bool -block_run::MergeableWith(block_run run) const -{ - // 65535 is the maximum allowed run size for BFS - return allocation_group == run.allocation_group - && Start() + Length() == run.Start() - && (uint32)Length() + run.Length() <= MAX_BLOCK_RUN_LENGTH; -} - - -inline void -block_run::SetTo(int32 _group,uint16 _start,uint16 _length) -{ - allocation_group = HOST_ENDIAN_TO_BFS_INT32(_group); - start = HOST_ENDIAN_TO_BFS_INT16(_start); - length = HOST_ENDIAN_TO_BFS_INT16(_length); -} - - -inline block_run -block_run::Run(int32 group, uint16 start, uint16 length) -{ - block_run run; - run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(group); - run.start = HOST_ENDIAN_TO_BFS_INT16(start); - run.length = HOST_ENDIAN_TO_BFS_INT16(length); - return run; -} - - -/************************ small_data inline functions ************************/ -// #pragma mark - - - -inline char * -small_data::Name() const -{ -#if __MWERKS__ - return (char *)(uint32(&data_size)+uint32(sizeof(data_size))); -#else - return const_cast(name); -#endif -} - - -inline uint8 * -small_data::Data() const -{ - return (uint8 *)Name() + NameSize() + 3; -} - - -inline uint32 -small_data::Size() const -{ - return sizeof(small_data) + NameSize() + 3 + DataSize() + 1; -} - - -inline small_data * -small_data::Next() const -{ - return (small_data *)((uint8 *)this + Size()); -} - - -inline bool -small_data::IsLast(const bfs_inode *inode) const -{ - // we need to check the location first, because if name_size is already beyond - // the block, we would touch invalid memory (although that can't cause wrong - // results) - return (uint32)this > (uint32)inode + inode->InodeSize() - sizeof(small_data) || name_size == 0; -} - - -/************************ bfs_inode inline functions ************************/ -// #pragma mark - - - -inline small_data * -bfs_inode::SmallDataStart() -{ -#if __MWERKS__ - return (small_data *)(&pad[4] /* last item in pad + sizeof(int32) */); -#else - return small_data_start; -#endif -} - - -#endif /* BFS_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs_control.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs_control.h deleted file mode 100644 index aa95e04a25..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs_control.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef BFS_CONTROL_H -#define BFS_CONTROL_H -/* bfs_control - additional functionality exported via ioctl() -** -** Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the MIT License. -*/ - - -#include "fsproto.h" - - -/* ioctl to check the version of BFS used - parameter is a uint32 * - * where the number is stored - */ -#define BFS_IOCTL_VERSION 14200 - -/* ioctls to use the "chkbfs" feature from the outside - * all calls use a struct check_result as single parameter - */ -#define BFS_IOCTL_START_CHECKING 14201 -#define BFS_IOCTL_STOP_CHECKING 14202 -#define BFS_IOCTL_CHECK_NEXT_NODE 14203 - -/* all fields except "flags", and "name" must be set to zero before - * BFS_IOCTL_START_CHECKING is called - */ -struct check_control { - uint32 magic; - uint32 flags; - char name[B_FILE_NAME_LENGTH]; - vnode_id inode; - uint32 mode; - uint32 errors; - struct { - uint64 missing; - uint64 already_set; - uint64 freed; - } stats; - status_t status; - void *cookie; -}; - -/* values for the flags field */ -#define BFS_FIX_BITMAP_ERRORS 1 -#define BFS_REMOVE_WRONG_TYPES 2 - /* files that shouldn't be part of its parent will be removed - * (i.e. a directory contains an attribute, ...) - * Works only if B_FIX_BITMAP_ERRORS is set, too - */ -#define BFS_REMOVE_INVALID 4 - /* removes nodes that couldn't be opened at all from its parent - * directory. - * Also requires the B_FIX_BITMAP_ERRORS to be set. - */ - -/* values for the errors field */ -#define BFS_MISSING_BLOCKS 1 -#define BFS_BLOCKS_ALREADY_SET 2 -#define BFS_INVALID_BLOCK_RUN 4 -#define BFS_COULD_NOT_OPEN 8 -#define BFS_WRONG_TYPE 16 -#define BFS_NAMES_DONT_MATCH 32 - -/* check control magic value */ -#define BFS_IOCTL_CHECK_MAGIC 'BChk' - -#endif /* BFS_CONTROL_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs_endian.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs_endian.h deleted file mode 100644 index f9f1dd59d6..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/bfs_endian.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the MIT License. -*/ -#ifndef BFS_ENDIAN_H -#define BFS_ENDIAN_H - - -#include - - -#if !defined(BFS_LITTLE_ENDIAN_ONLY) && !defined(BFS_BIG_ENDIAN_ONLY) -// default setting; BFS is now primarily a little endian file system -# define BFS_LITTLE_ENDIAN_ONLY -#endif - - -#if defined(BFS_LITTLE_ENDIAN_ONLY) && B_HOST_IS_LENDIAN \ - || defined(BFS_BIG_ENDIAN_ONLY) && B_HOST_IS_BENDIAN - /* host is BFS endian */ -# define BFS_ENDIAN_TO_HOST_INT16(value) value -# define BFS_ENDIAN_TO_HOST_INT32(value) value -# define BFS_ENDIAN_TO_HOST_INT64(value) value -# define HOST_ENDIAN_TO_BFS_INT16(value) value -# define HOST_ENDIAN_TO_BFS_INT32(value) value -# define HOST_ENDIAN_TO_BFS_INT64(value) value -#elif defined(BFS_LITTLE_ENDIAN_ONLY) && B_HOST_IS_BENDIAN \ - || defined(BFS_BIG_ENDIAN_ONLY) && B_HOST_IS_LENDIAN - /* host is big endian, BFS is little endian or vice versa */ -# define BFS_ENDIAN_TO_HOST_INT16(value) __swap_int16(value) -# define BFS_ENDIAN_TO_HOST_INT32(value) __swap_int32(value) -# define BFS_ENDIAN_TO_HOST_INT64(value) __swap_int64(value) -# define HOST_ENDIAN_TO_BFS_INT16(value) __swap_int16(value) -# define HOST_ENDIAN_TO_BFS_INT32(value) __swap_int32(value) -# define HOST_ENDIAN_TO_BFS_INT64(value) __swap_int64(value) -#else - // ToDo: maybe build a version that supports both, big & little endian? - // But since that will need some kind of global data (to - // know of what type this file system is), it's probably - // something for the boot loader; anything else would be - // a major pain. -#endif - -#endif /* BFS_ENDIAN_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/boot_block.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/boot_block.h deleted file mode 100644 index 39b7b1de45..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/boot_block.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef BOOT_BLOCK_DATA_H -#define BOOT_BLOCK_DATA_H - -// Note: the boot block here is taken directly from my main BFS disk. -// There is no Haiku replacement for that code yet! - -static uint8 sBootBlockData1[512] = { - 0xb8, 0x00, 0x90, 0x8e, 0xd0, 0x89, 0xc4, 0xfc, 0x68, 0xc0, 0x07, 0x1f, 0x1e, 0x07, 0x80, 0xfa, - 0x02, 0x72, 0x0c, 0x80, 0xfa, 0x80, 0x72, 0x05, 0x80, 0xfa, 0x90, 0x72, 0x02, 0xb2, 0x80, 0x66, - 0x31, 0xc0, 0x40, 0xbb, 0x00, 0x02, 0xe8, 0x7e, 0x00, 0x81, 0x7f, 0x20, 0x31, 0x53, 0x75, 0x75, - 0x80, 0xbf, 0xff, 0x01, 0x20, 0x75, 0x6e, 0xbe, 0x74, 0x02, 0xe8, 0x82, 0x01, 0xe8, 0xa3, 0x02, - 0xbf, 0x07, 0x03, 0xbb, 0x00, 0x06, 0x57, 0xe8, 0xf5, 0x00, 0x72, 0x59, 0x5f, 0x53, 0xbb, 0x00, - 0x02, 0x59, 0x51, 0x81, 0xc3, 0x00, 0x04, 0x39, 0xcb, 0x73, 0xe8, 0x66, 0xff, 0x47, 0x10, 0x75, - 0xf0, 0x66, 0xff, 0x47, 0x14, 0x75, 0xea, 0xb0, 0x2f, 0x89, 0xd9, 0x57, 0xf2, 0xae, 0x75, 0x35, - 0x29, 0xd9, 0xf7, 0xd1, 0x5f, 0x60, 0xe8, 0x2b, 0x02, 0x61, 0x72, 0xd5, 0x8c, 0xd5, 0x01, 0xcf, - 0x47, 0x38, 0x05, 0x75, 0xbe, 0xb8, 0x00, 0x10, 0x8e, 0xc0, 0x31, 0xdb, 0xc1, 0xeb, 0x04, 0x8c, - 0xc0, 0x01, 0xd8, 0x8e, 0xc0, 0x31, 0xdb, 0xe8, 0xa5, 0x00, 0x73, 0xf0, 0x66, 0xa1, 0xab, 0x00, - 0xea, 0x00, 0x02, 0x00, 0x10, 0xeb, 0x7f, 0x66, 0x60, 0x66, 0x05, 0x5b, 0x63, 0x40, 0x01, 0x06, - 0x53, 0x52, 0x66, 0x50, 0x66, 0x31, 0xf6, 0x66, 0xc7, 0x04, 0x10, 0x00, 0x01, 0x00, 0x89, 0x5c, - 0x04, 0x8c, 0x44, 0x06, 0x66, 0x89, 0x44, 0x08, 0x66, 0x89, 0x74, 0x0c, 0xb4, 0x08, 0xcd, 0x13, - 0x88, 0xc8, 0x86, 0xe9, 0xc0, 0xc5, 0x02, 0x80, 0xe5, 0x03, 0x89, 0xcd, 0x25, 0x3f, 0x00, 0x89, - 0xc3, 0xb2, 0x00, 0x86, 0xd6, 0x42, 0xf7, 0xe2, 0x91, 0x58, 0x5a, 0xe3, 0x39, 0x39, 0xca, 0x73, - 0x2a, 0xf7, 0xf1, 0x39, 0xc5, 0x76, 0x24, 0x91, 0x92, 0xf6, 0xf3, 0x86, 0xcd, 0xc0, 0xc9, 0x02, - 0x08, 0xe1, 0x41, 0x5a, 0x88, 0xc6, 0x5b, 0x07, 0xbf, 0x05, 0x00, 0xb8, 0x01, 0x02, 0xcd, 0x13, - 0x73, 0x27, 0x31, 0xc0, 0xcd, 0x13, 0x4f, 0x75, 0xf2, 0xeb, 0x03, 0x5a, 0x5b, 0x07, 0x31, 0xf6, - 0xb4, 0x42, 0xcd, 0x13, 0x73, 0x13, 0xbe, 0xd4, 0x01, 0x31, 0xdb, 0xb4, 0x0e, 0xac, 0xcd, 0x10, - 0x3c, 0x2e, 0x75, 0xf9, 0x93, 0xcd, 0x16, 0xcd, 0x19, 0x66, 0x61, 0xc3, 0x01, 0x46, 0x00, 0x8b, - 0x76, 0x00, 0xe8, 0x7a, 0x00, 0x83, 0x3c, 0x00, 0x75, 0x27, 0xb8, 0x08, 0x00, 0x01, 0x46, 0x00, - 0xff, 0x4e, 0x02, 0x74, 0x08, 0x83, 0x4c, 0x08, 0x00, 0x75, 0xe4, 0xf9, 0xc3, 0xff, 0x46, 0x02, - 0xfe, 0x0e, 0x10, 0x00, 0x7c, 0xf5, 0x81, 0xfd, 0x00, 0x90, 0x74, 0xd0, 0x83, 0xed, 0x04, 0xeb, - 0xce, 0xff, 0x44, 0xfe, 0xff, 0x0c, 0x80, 0x3e, 0x10, 0x00, 0x02, 0x74, 0x24, 0x06, 0x53, 0x1e, - 0x07, 0x8d, 0x5e, 0x08, 0xc1, 0xe3, 0x0b, 0xe8, 0x17, 0x00, 0x93, 0x5b, 0x07, 0x83, 0xc5, 0x04, - 0x89, 0x7e, 0x00, 0x29, 0xf8, 0xc1, 0xe8, 0x03, 0x89, 0x46, 0x02, 0xfe, 0x06, 0x10, 0x00, 0xeb, - 0x9e, 0xbe, 0x00, 0x02, 0x89, 0xdf, 0x8a, 0x4c, 0x2c, 0x80, 0xe9, 0x09, 0x66, 0xd3, 0xe0, 0x8b, - 0x4c, 0x28, 0xc1, 0xe9, 0x09, 0xe8, 0xef, 0xfe, 0x01, 0xf3, 0x66, 0x40, 0xe2, 0xf7, 0xc3, 0x51, - 0x66, 0xad, 0x8a, 0x0e, 0x4c, 0x02, 0x66, 0xd3, 0xe0, 0x66, 0x91, 0x66, 0x31, 0xc0, 0xad, 0x66, - 0x01, 0xc8, 0x59, 0xc3, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x20, 0x4f, 0x53, 0x3b, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, - 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x2e, 0x55, 0xaa, -}; - -static const size_t kBootBlockData2Offset = 676; -static uint8 sBootBlockData2[348] = { - 0x52, 0x8d, 0x77, 0x18, 0xad, 0x92, 0x52, 0xad, 0x89, 0xf3, 0x89, 0xf5, 0x01, 0xc6, 0x83, 0xc6, - 0x07, 0x81, 0xe6, 0xf8, 0xff, 0xad, 0x01, 0xe8, 0x29, 0xd8, 0x39, 0xc8, 0x75, 0x08, 0x60, 0x89, - 0xde, 0xf3, 0xa6, 0x61, 0x74, 0x09, 0x01, 0xc3, 0x4a, 0x75, 0xea, 0x58, 0x5a, 0xf9, 0xc3, 0x58, - 0x29, 0xd0, 0xc1, 0xe0, 0x03, 0x01, 0xc6, 0x4a, 0x01, 0xd6, 0x01, 0xd6, 0x5a, 0x66, 0xad, 0x8c, - 0xd5, 0xbb, 0x00, 0x04, 0xe8, 0xb6, 0xfe, 0x66, 0x81, 0x3d, 0xd9, 0x0a, 0xbe, 0x3b, 0x75, 0x10, - 0xc6, 0x06, 0x10, 0x00, 0x02, 0xc7, 0x46, 0x00, 0x48, 0x04, 0xc7, 0x46, 0x02, 0x0c, 0x00, 0xc3, - 0xe9, 0x1f, 0xfe, 0x62, 0x65, 0x6f, 0x73, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x7a, - 0x62, 0x65, 0x6f, 0x73, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, -}; - -static const size_t kBootBlockData1Size = sizeof(sBootBlockData1); -static const size_t kBootBlockData2Size = sizeof(sBootBlockData2); - -static const size_t kBIOSDriveOffset = 0x1e; -static const size_t kPartitionDataOffset = 0xab; - -#endif /* BOOT_BLOCK_DATA_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/cache.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/cache.h deleted file mode 100644 index d57d1917d4..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/cache.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright 1999-2001, Be Incorporated. All Rights Reserved. - This file may be used under the terms of the Be Sample Code License. -*/ - -#ifndef _CACHE_H_ -#define _CACHE_H_ - -#include - -#define ALLOW_WRITES 1 -#define NO_WRITES 0 - -#ifdef __cplusplus -extern "C" { -#endif - -extern int init_block_cache(int max_blocks, int flags); -extern void shutdown_block_cache(void); - -extern void force_cache_flush(int dev, int prefer_log_blocks); -extern int flush_blocks(int dev, off_t bnum, int nblocks); -extern int flush_device(int dev, int warn_locked); - -extern int init_cache_for_device(int fd, off_t max_blocks); -extern int remove_cached_device_blocks(int dev, int allow_write); - -extern void *get_block(int dev, off_t bnum, int bsize); -extern void *get_empty_block(int dev, off_t bnum, int bsize); -extern int release_block(int dev, off_t bnum); -extern int mark_blocks_dirty(int dev, off_t bnum, int nblocks); - - -extern int cached_read(int dev, off_t bnum, void *data, off_t num_blocks, int bsize); -extern int cached_write(int dev, off_t bnum, const void *data, - off_t num_blocks, int bsize); -extern int cached_write_locked(int dev, off_t bnum, const void *data, - off_t num_blocks, int bsize); -extern int set_blocks_info(int dev, off_t *blocks, int nblocks, - void (*func)(off_t bnum, size_t nblocks, void *arg), - void *arg); - -extern size_t read_phys_blocks (int fd, off_t bnum, void *data, uint num_blocks, int bsize); -extern size_t write_phys_blocks(int fd, off_t bnum, void *data, uint num_blocks, int bsize); - -#ifdef __cplusplus -} -#endif - -#endif /* _CACHE_H_ */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/fsproto.h b/src/tests/add-ons/kernel/file_systems/bfs/r5/fsproto.h deleted file mode 100644 index 1b1e3e03fc..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/fsproto.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - Copyright 1999-2001, Be Incorporated. All Rights Reserved. - This file may be used under the terms of the Be Sample Code License. -*/ - -#ifndef _FSPROTO_H -#define _FSPROTO_H - -// TODO: FIXME! XXX! DANGER! BIOHAZARD! -// OMG, we're defining COMPILE_FOR_R5 so that defines the -// obsolete notify_select_event() prototype. BTW, this happens only on -// non-BeOS-compatible host platforms. Outright scary! -#ifdef MEAN_BFS_SHELL_SELECT_HACK -# define COMPILE_FOR_R5 -# include -# undef COMPILE_FOR_R5 -#endif - -#include -#include -#include -#include -//#include - -#include -#include -#include -#include -#include -#include - -typedef dev_t nspace_id; -typedef ino_t vnode_id; - -/* - * PUBLIC PART OF THE FILE SYSTEM PROTOCOL - */ - -#define WSTAT_MODE 0x0001 -#define WSTAT_UID 0x0002 -#define WSTAT_GID 0x0004 -#define WSTAT_SIZE 0x0008 -#define WSTAT_ATIME 0x0010 -#define WSTAT_MTIME 0x0020 -#define WSTAT_CRTIME 0x0040 - -#define WFSSTAT_NAME 0x0001 - -#define SELECT_READ 1 -#define SELECT_WRITE 2 -#define SELECT_EXCEPTION 3 - -// missing ioctl() call added -#define IOCTL_FILE_UNCACHED_IO 10000 -#define IOCTL_CREATE_TIME 10002 -#define IOCTL_MODIFIED_TIME 10003 - - -// B_CUR_FS_API_VERSION is 2 for R5, but 3 on Zeta and Dano, because -// of the added calls for power management - so it's set to 3 here -// because that's a requirement to let Dano boot from our fs... -#ifdef COMPILE_FOR_ZETA -# define B_CUR_FS_API_VERSION 3 -#else -# define B_CUR_FS_API_VERSION 2 -#endif - -struct attr_info; -struct index_info; - -typedef int op_read_vnode(void *ns, vnode_id vnid, char r, void **node); -typedef int op_write_vnode(void *ns, void *node, char r); -typedef int op_remove_vnode(void *ns, void *node, char r); -typedef int op_secure_vnode(void *ns, void *node); -typedef int op_wake_vnode(void *ns, void *node); -typedef int op_suspend_vnode(void *ns, void *node); - -typedef int op_walk(void *ns, void *base, const char *file, char **newpath, - vnode_id *vnid); - -typedef int op_access(void *ns, void *node, int mode); - -typedef int op_create(void *ns, void *dir, const char *name, - int omode, int perms, vnode_id *vnid, void **cookie); -typedef int op_mkdir(void *ns, void *dir, const char *name, int perms); -typedef int op_symlink(void *ns, void *dir, const char *name, - const char *path); -typedef int op_link(void *ns, void *dir, const char *name, void *node); - -typedef int op_rename(void *ns, void *olddir, const char *oldname, - void *newdir, const char *newname); -typedef int op_unlink(void *ns, void *dir, const char *name); -typedef int op_rmdir(void *ns, void *dir, const char *name); - -typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize); - -typedef int op_opendir(void *ns, void *node, void **cookie); -typedef int op_closedir(void *ns, void *node, void *cookie); -typedef int op_rewinddir(void *ns, void *node, void *cookie); -typedef int op_readdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -typedef int op_open(void *ns, void *node, int omode, void **cookie); -typedef int op_close(void *ns, void *node, void *cookie); -typedef int op_free_cookie(void *ns, void *node, void *cookie); -typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf, - size_t *len); -typedef int op_write(void *ns, void *node, void *cookie, off_t pos, - const void *buf, size_t *len); -typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, - size_t count, size_t *len); -typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, - size_t count, size_t *len); -typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, - size_t len); -typedef int op_setflags(void *ns, void *node, void *cookie, int flags); - -typedef int op_rstat(void *ns, void *node, struct stat *); -typedef int op_wstat(void *ns, void *node, struct stat *, long mask); -typedef int op_fsync(void *ns, void *node); - -typedef int op_select(void *ns, void *node, void *cookie, uint8 event, - uint32 ref, selectsync *sync); -typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event, - selectsync *sync); - -typedef int op_initialize(const char *devname, void *parms, size_t len); -typedef int op_mount(nspace_id nsid, const char *devname, ulong flags, - void *parms, size_t len, void **data, vnode_id *vnid); -typedef int op_unmount(void *ns); -typedef int op_sync(void *ns); -typedef int op_rfsstat(void *ns, struct fs_info *); -typedef int op_wfsstat(void *ns, struct fs_info *, long mask); - - -typedef int op_open_attrdir(void *ns, void *node, void **cookie); -typedef int op_close_attrdir(void *ns, void *node, void *cookie); -typedef int op_rewind_attrdir(void *ns, void *node, void *cookie); -typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufsize); -typedef int op_remove_attr(void *ns, void *node, const char *name); -typedef int op_rename_attr(void *ns, void *node, const char *oldname, - const char *newname); -typedef int op_stat_attr(void *ns, void *node, const char *name, - struct attr_info *buf); - -typedef int op_write_attr(void *ns, void *node, const char *name, int type, - const void *buf, size_t *len, off_t pos); -typedef int op_read_attr(void *ns, void *node, const char *name, int type, - void *buf, size_t *len, off_t pos); - -typedef int op_open_indexdir(void *ns, void **cookie); -typedef int op_close_indexdir(void *ns, void *cookie); -typedef int op_rewind_indexdir(void *ns, void *cookie); -typedef int op_read_indexdir(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); -typedef int op_create_index(void *ns, const char *name, int type, int flags); -typedef int op_remove_index(void *ns, const char *name); -typedef int op_rename_index(void *ns, const char *oldname, - const char *newname); -typedef int op_stat_index(void *ns, const char *name, struct index_info *buf); - -typedef int op_open_query(void *ns, const char *query, ulong flags, - port_id port, long token, void **cookie); -typedef int op_close_query(void *ns, void *cookie); -typedef int op_read_query(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -typedef struct vnode_ops { - op_read_vnode (*read_vnode); - op_write_vnode (*write_vnode); - op_remove_vnode (*remove_vnode); - op_secure_vnode (*secure_vnode); - op_walk (*walk); - op_access (*access); - op_create (*create); - op_mkdir (*mkdir); - op_symlink (*symlink); - op_link (*link); - op_rename (*rename); - op_unlink (*unlink); - op_rmdir (*rmdir); - op_readlink (*readlink); - op_opendir (*opendir); - op_closedir (*closedir); - op_free_cookie (*free_dircookie); - op_rewinddir (*rewinddir); - op_readdir (*readdir); - op_open (*open); - op_close (*close); - op_free_cookie (*free_cookie); - op_read (*read); - op_write (*write); - op_readv (*readv); - op_writev (*writev); - op_ioctl (*ioctl); - op_setflags (*setflags); - op_rstat (*rstat); - op_wstat (*wstat); - op_fsync (*fsync); - op_initialize (*initialize); - op_mount (*mount); - op_unmount (*unmount); - op_sync (*sync); - op_rfsstat (*rfsstat); - op_wfsstat (*wfsstat); - op_select (*select); - op_deselect (*deselect); - op_open_indexdir (*open_indexdir); - op_close_indexdir (*close_indexdir); - op_free_cookie (*free_indexdircookie); - op_rewind_indexdir (*rewind_indexdir); - op_read_indexdir (*read_indexdir); - op_create_index (*create_index); - op_remove_index (*remove_index); - op_rename_index (*rename_index); - op_stat_index (*stat_index); - op_open_attrdir (*open_attrdir); - op_close_attrdir (*close_attrdir); - op_free_cookie (*free_attrdircookie); - op_rewind_attrdir (*rewind_attrdir); - op_read_attrdir (*read_attrdir); - op_write_attr (*write_attr); - op_read_attr (*read_attr); - op_remove_attr (*remove_attr); - op_rename_attr (*rename_attr); - op_stat_attr (*stat_attr); - op_open_query (*open_query); - op_close_query (*close_query); - op_free_cookie (*free_querycookie); - op_read_query (*read_query); - // for Dano compatibility only - op_wake_vnode (*wake_vnode); - op_suspend_vnode (*suspend_vnode); -} vnode_ops; - -#ifdef __cplusplus -extern "C" { -#endif - -extern _IMPEXP_KERNEL int new_path(const char *path, char **copy); -extern _IMPEXP_KERNEL void free_path(char *p); - -extern _IMPEXP_KERNEL void notify_select_event(selectsync *sync, uint32 ref); - -extern _IMPEXP_KERNEL int notify_listener(int op, nspace_id nsid, - vnode_id vnida, vnode_id vnidb, - vnode_id vnidc, const char *name); -extern _IMPEXP_KERNEL status_t send_notification(port_id port, long token, - ulong what, long op, nspace_id device, nspace_id toDevice, - vnode_id parentNode, vnode_id toParentNode, vnode_id node, - const char *name); - -extern _IMPEXP_KERNEL int get_vnode(nspace_id nsid, vnode_id vnid, void **data); -extern _IMPEXP_KERNEL int put_vnode(nspace_id nsid, vnode_id vnid); -extern _IMPEXP_KERNEL int new_vnode(nspace_id nsid, vnode_id vnid, void *data); -extern _IMPEXP_KERNEL int remove_vnode(nspace_id nsid, vnode_id vnid); -extern _IMPEXP_KERNEL int unremove_vnode(nspace_id nsid, vnode_id vnid); -extern _IMPEXP_KERNEL int is_vnode_removed(nspace_id nsid, vnode_id vnid); - -#ifdef __cplusplus -} -#endif - -extern _EXPORT vnode_ops fs_entry; -extern _EXPORT int32 api_version; - -#endif /* _FSPROTO_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/r5/kernel_interface_r5.cpp b/src/tests/add-ons/kernel/file_systems/bfs/r5/kernel_interface_r5.cpp deleted file mode 100644 index fd882cdf4a..0000000000 --- a/src/tests/add-ons/kernel/file_systems/bfs/r5/kernel_interface_r5.cpp +++ /dev/null @@ -1,2281 +0,0 @@ -/* kernel_interface - file system interface to BeOS' vnode layer - * - * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de - * This file may be used under the terms of the MIT License. - */ - - -#ifndef COMPILE_FOR_R5 -# define COMPILE_FOR_R5 -#endif - -#include "Debug.h" -#include "Volume.h" -#include "Inode.h" -#include "Index.h" -#include "BPlusTree.h" -#include "Query.h" -#include "bfs_control.h" - -#include - -#include -#include - -// BeOS vnode layer stuff -#include -#ifndef _IMPEXP_KERNEL -# define _IMPEXP_KERNEL -#endif - -#include "fsproto.h" -#include "cache.h" - -#include -#include - -// needed for bfs_initialize() in the fs_shell only -#ifdef USER -# include "boot_block.h" -#endif - - -#ifdef USER -# define dprintf printf -#endif - -// file system API -static int bfs_mount(nspace_id nsid, const char *device, ulong flags, - void *parms, size_t len, void **data, vnode_id *vnid); -static int bfs_unmount(void *_ns); -static int bfs_read_fs_stat(void *_ns, struct fs_info *); -static int bfs_write_fs_stat(void *ns, struct fs_info *, long mode); -static int bfs_initialize(const char *devname, void *parms, size_t len); - -static int bfs_sync(void *ns); - -static int bfs_read_vnode(void *_ns, vnode_id vnid, char r, void **node); -static int bfs_release_vnode(void *_ns, void *_node, char r); -static int bfs_remove_vnode(void *ns, void *node, char r); - -static int bfs_walk(void *_ns, void *_base, const char *file, - char **newpath, vnode_id *vnid); - -static int bfs_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, size_t len); -static int bfs_setflags(void *ns, void *node, void *cookie, int flags); - -static int bfs_select(void *ns, void *node, void *cookie, uint8 event, - uint32 ref, selectsync *sync); -static int bfs_deselect(void *ns, void *node, void *cookie, uint8 event, - selectsync *sync); -static int bfs_fsync(void *ns, void *node); - -static int bfs_create(void *ns, void *dir, const char *name, - int perms, int omode, vnode_id *vnid, void **cookie); -static int bfs_symlink(void *ns, void *dir, const char *name, - const char *path); -static int bfs_link(void *ns, void *dir, const char *name, void *node); -static int bfs_unlink(void *ns, void *dir, const char *name); -static int bfs_rename(void *ns, void *oldDir, const char *oldName, void *newDir, const char *newName); - -static int bfs_read_stat(void *_ns, void *_node, struct stat *st); -static int bfs_write_stat(void *ns, void *node, struct stat *st, long mask); - -static int bfs_open(void *_ns, void *_node, int omode, void **cookie); -static int bfs_read(void *_ns, void *_node, void *cookie, off_t pos, - void *buf, size_t *len); -static int bfs_write(void *ns, void *node, void *cookie, off_t pos, - const void *buf, size_t *len); -static int bfs_free_cookie(void *ns, void *node, void *cookie); -static int bfs_close(void *ns, void *node, void *cookie); - -static int bfs_access(void *_ns, void *_node, int mode); -static int bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize); - -// directory functions -static int bfs_mkdir(void *ns, void *dir, const char *name, int perms); -static int bfs_rmdir(void *ns, void *dir, const char *name); -static int bfs_open_dir(void *_ns, void *_node, void **cookie); -static int bfs_read_dir(void *_ns, void *_node, void *cookie, - long *num, struct dirent *dirent, size_t bufferSize); -static int bfs_rewind_dir(void *_ns, void *_node, void *cookie); -static int bfs_close_dir(void *_ns, void *_node, void *cookie); -static int bfs_free_dir_cookie(void *_ns, void *_node, void *cookie); - -// attribute support -static int bfs_open_attrdir(void *ns, void *node, void **cookie); -static int bfs_close_attrdir(void *ns, void *node, void *cookie); -static int bfs_free_attrdir_cookie(void *ns, void *node, void *cookie); -static int bfs_rewind_attrdir(void *ns, void *node, void *cookie); -static int bfs_read_attrdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufferSize); -static int bfs_remove_attr(void *ns, void *node, const char *name); -static int bfs_rename_attr(void *ns, void *node, const char *oldname, - const char *newname); -static int bfs_stat_attr(void *ns, void *node, const char *name, - struct attr_info *buf); -static int bfs_write_attr(void *ns, void *node, const char *name, int type, - const void *buf, size_t *len, off_t pos); -static int bfs_read_attr(void *ns, void *node, const char *name, int type, - void *buf, size_t *len, off_t pos); - -// index support -static int bfs_open_indexdir(void *ns, void **cookie); -static int bfs_close_indexdir(void *ns, void *cookie); -static int bfs_free_indexdir_cookie(void *ns, void *node, void *cookie); -static int bfs_rewind_indexdir(void *ns, void *cookie); -static int bfs_read_indexdir(void *ns, void *cookie, long *num, struct dirent *dirent, - size_t bufferSize); -static int bfs_create_index(void *ns, const char *name, int type, int flags); -static int bfs_remove_index(void *ns, const char *name); -static int bfs_rename_index(void *ns, const char *oldname, const char *newname); -static int bfs_stat_index(void *ns, const char *name, struct index_info *indexInfo); - -// query support -static int bfs_open_query(void *ns, const char *query, ulong flags, - port_id port, long token, void **cookie); -static int bfs_close_query(void *ns, void *cookie); -static int bfs_free_query_cookie(void *ns, void *node, void *cookie); -static int bfs_read_query(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -// Dano compatibility (required for booting) -static int bfs_wake_vnode(void *ns, void *node); -static int bfs_suspend_vnode(void *ns, void *node); - - -/* vnode_ops struct. Fill this in to tell the kernel how to call - functions in your driver. -*/ - -vnode_ops fs_entry = { - &bfs_read_vnode, // read_vnode - &bfs_release_vnode, // write_vnode - &bfs_remove_vnode, // remove_vnode - NULL, // secure_vnode (not needed) - &bfs_walk, // walk - &bfs_access, // access - &bfs_create, // create - &bfs_mkdir, // mkdir - &bfs_symlink, // symlink - &bfs_link, // link - &bfs_rename, // rename - &bfs_unlink, // unlink - &bfs_rmdir, // rmdir - &bfs_read_link, // readlink - &bfs_open_dir, // opendir - &bfs_close_dir, // closedir - &bfs_free_dir_cookie, // free_dircookie - &bfs_rewind_dir, // rewinddir - &bfs_read_dir, // readdir - &bfs_open, // open file - &bfs_close, // close file - &bfs_free_cookie, // free cookie - &bfs_read, // read file - &bfs_write, // write file - NULL, // readv - NULL, // writev - &bfs_ioctl, // ioctl - &bfs_setflags, // setflags file - &bfs_read_stat, // read stat - &bfs_write_stat, // write stat - &bfs_fsync, // fsync - &bfs_initialize, // initialize - &bfs_mount, // mount - &bfs_unmount, // unmount - &bfs_sync, // sync - &bfs_read_fs_stat, // read fs stat - &bfs_write_fs_stat, // write fs stat - &bfs_select, // select - &bfs_deselect, // deselect - - &bfs_open_indexdir, // open index dir - &bfs_close_indexdir, // close index dir - &bfs_free_indexdir_cookie, // free index dir cookie - &bfs_rewind_indexdir, // rewind index dir - &bfs_read_indexdir, // read index dir - &bfs_create_index, // create index - &bfs_remove_index, // remove index - &bfs_rename_index, // rename index - &bfs_stat_index, // stat index - - &bfs_open_attrdir, // open attr dir - &bfs_close_attrdir, // close attr dir - &bfs_free_attrdir_cookie, // free attr dir cookie - &bfs_rewind_attrdir, // rewind attr dir - &bfs_read_attrdir, // read attr dir - &bfs_write_attr, // write attr - &bfs_read_attr, // read attr - &bfs_remove_attr, // remove attr - &bfs_rename_attr, // rename attr - &bfs_stat_attr, // stat attr - - &bfs_open_query, // open query - &bfs_close_query, // close query - &bfs_free_query_cookie, // free query cookie - &bfs_read_query, // read query - - &bfs_wake_vnode, // these two are added for Dano compatibility; - &bfs_suspend_vnode // they do nothing. -}; - -#define BFS_IO_SIZE 65536 - -// ToDo: has to change to "bfs" later -#ifdef BFS_REPLACEMENT -# define BFS_NAME "bfs" -#else -# define BFS_NAME "obfs" -#endif - -int32 api_version = B_CUR_FS_API_VERSION; - - -static int -bfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms, - size_t len, void **data, vnode_id *rootID) -{ - FUNCTION(); - - Volume *volume = new Volume(nsid); - if (volume == NULL) - return B_NO_MEMORY; - - status_t status; - if ((status = volume->Mount(device, flags)) == B_OK) { - *data = volume; - *rootID = volume->ToVnode(volume->Root()); - INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n", - volume->Name(), *rootID, device)); - -#ifdef DEBUG - add_debugger_commands(); -#endif - } - else - delete volume; - - RETURN_ERROR(status); -} - - -static int -bfs_unmount(void *ns) -{ - FUNCTION(); - Volume* volume = (Volume *)ns; - - status_t status = volume->Unmount(); - delete volume; - -#ifdef DEBUG - remove_debugger_commands(); -#endif - RETURN_ERROR(status); -} - - -/** Fill in bfs_info struct for device. - */ - -static int -bfs_read_fs_stat(void *_ns, struct fs_info *info) -{ - FUNCTION(); - if (_ns == NULL || info == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - RecursiveLocker locker(volume->Lock()); - - // File system flags. - info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME | B_FS_HAS_QUERY | - (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); - - info->io_size = BFS_IO_SIZE; - // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now - - info->block_size = volume->BlockSize(); - info->total_blocks = volume->NumBlocks(); - info->free_blocks = volume->FreeBlocks(); - - // Volume name - strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); - - // File system name - strlcpy(info->fsh_name, BFS_NAME, sizeof(info->fsh_name)); - - return B_OK; -} - - -static int -bfs_write_fs_stat(void *_ns, struct fs_info *info, long mask) -{ - FUNCTION_START(("mask = %ld\n", mask)); - - Volume *volume = (Volume *)_ns; - disk_super_block &superBlock = volume->SuperBlock(); - - RecursiveLocker locker(volume->Lock()); - - status_t status = B_BAD_VALUE; - - if (mask & WFSSTAT_NAME) { - strncpy(superBlock.name, info->volume_name, sizeof(superBlock.name) - 1); - superBlock.name[sizeof(superBlock.name) - 1] = '\0'; - - status = volume->WriteSuperBlock(); - } - return status; -} - - -static int -bfs_initialize(const char *deviceName, void *parms, size_t len) -{ - FUNCTION_START(("deviceName = %s, parameter len = %ld\n", deviceName, len)); - - // This function is not available from the outside in BeOS - // It will be similarly implemented in OpenBeOS, though - the - // backend (to create the file system) is already done; just - // call Volume::Initialize(). - - // When compiling the userland bfs_shell, we provide a useful - // implementation for this function anyway, so that the bfs_shell can - // initialize images. - -#ifndef USER - - return B_ERROR; - -#else // USER - - if (deviceName == NULL) - return B_BAD_VALUE; - - char **argv = (char**)parms; - - uint32 blockSize = 1024; - uint32 flags = 0; - bool verbose = false; - - while (argv && *++argv) { - char *arg = *argv; - if (*arg == '-') { - if (arg[1] == '-') { - if (!strcmp(arg, "--noindex")) - flags |= VOLUME_NO_INDICES; - else - return B_BAD_VALUE; - } - - while (*++arg && *arg >= 'a' && *arg <= 'z') { - switch (*arg) { - case 'v': - verbose = true; - break; - case 'b': - if (*++argv == NULL || !(**argv >= '0' && **argv <= '9')) - return B_BAD_VALUE; - - blockSize = atol(*argv); - break; - case 'n': - flags |= VOLUME_NO_INDICES; - break; - } - } - } - else - break; - } - - char *volumeName = "Unnamed"; - if (argv[0] != NULL) - volumeName = argv[0]; - - if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096 && blockSize != 8192) { - FATAL(("valid block sizes are: 1024, 2048, 4096, and 8192\n")); - return -1; - } - - Volume volume(2); - // we just happen to know it's 2... - status_t status = volume.Initialize(deviceName, volumeName, blockSize, flags); - if (status < B_OK) { - FATAL(("Initializing volume failed: %s\n", strerror(status))); - return -1; - } - - if (verbose) { - disk_super_block super = volume.SuperBlock(); - - INFORM(("Disk was initialized successfully.\n")); - INFORM(("\tname: \"%s\"\n", super.name)); - INFORM(("\tnum blocks: %Ld\n", super.NumBlocks())); - INFORM(("\tused blocks: %Ld\n", super.UsedBlocks())); - INFORM(("\tblock size: %lu bytes\n", super.BlockSize())); - INFORM(("\tnum allocation groups: %ld\n", super.AllocationGroups())); - INFORM(("\tallocation group size: %ld blocks\n", 1L << super.AllocationGroupShift())); - INFORM(("\tlog size: %u blocks\n", super.log_blocks.Length())); - } - - // make the disk image bootable - - int device = open(deviceName, O_RDWR); - if (device < 0) { - FATAL(("Could not make image bootable: Failed to open \"%s\"\n", - deviceName)); - return B_FILE_ERROR; - } - - // change BIOS drive and partition offset - // ToDo: for now, this will only work for images only - - sBootBlockData1[kBIOSDriveOffset] = 0x80; - // for now, this should be replaced by the real thing - uint32 *offset = (uint32 *)&sBootBlockData1[kPartitionDataOffset]; - *offset = 0; - - write_pos(device, 0, sBootBlockData1, kBootBlockData1Size); - write_pos(device, kBootBlockData2Offset, sBootBlockData2, kBootBlockData2Size); - - close(device); - - return B_OK; - -#endif // USER -} - - -static int -bfs_sync(void *_ns) -{ - FUNCTION(); - if (_ns == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - return volume->Sync(); -} - - -// #pragma mark - - - -/** Reads in the node from disk and creates an inode object from it. - * Has to be cautious if the node in question is currently under - * construction, in which case it waits for that action to be completed, - * and uses the inode object from the construction instead of creating - * a new one. - * ToDo: Must not be called without the volume lock being held. Actually, - * this might even happen with the BDirectory(node_ref *) constructor - * (at least I think so, I haven't tested it yet), so we should better - * test this. Fortunately, we can easily solve the issue with our kernel. - */ - -static int -bfs_read_vnode(void *_ns, vnode_id id, char reenter, void **_node) -{ - FUNCTION_START(("vnode_id = %Ld\n", id)); - Volume *volume = (Volume *)_ns; - - if (id < 0 || id > volume->NumBlocks()) { - FATAL(("inode at %Ld requested!\n", id)); - return B_ERROR; - } - - CachedBlock cached(volume, id); - - bfs_inode *node = (bfs_inode *)cached.Block(); - Inode *inode = NULL; - int32 tries = 0; - -restartIfBusy: - status_t status = node->InitCheck(volume); - - if (status == B_BUSY) { - inode = (Inode *)node->etc; - // We have to use the "etc" field to get the inode object - // (the inode is currently being constructed) - // We would need to call something like get_vnode() again here - // to get rid of the "etc" field - which would be nice, especially - // for other file systems which don't have this messy field. - - // let us wait a bit and try again later - if (tries++ < 200) { - // wait for one second at maximum - snooze(5000); - goto restartIfBusy; - } - FATAL(("inode is not becoming unbusy (id = %Ld)\n", id)); - return status; - } else if (status < B_OK) { - FATAL(("inode at %Ld is corrupt!\n", id)); - return status; - } - - // If the inode is currently being constructed, we already have an inode - // pointer (taken from the node's etc field). - // If not, we create a new one here - - if (inode == NULL) { - inode = new Inode(&cached); - if (inode == NULL) - return B_NO_MEMORY; - - status = inode->InitCheck(false); - if (status < B_OK) - delete inode; - } else - status = inode->InitCheck(false); - - if (status == B_OK) - *_node = inode; - - return status; -} - - -static int -bfs_release_vnode(void *ns, void *_node, char reenter) -{ - //FUNCTION_START(("node = %p\n", _node)); - Inode *inode = (Inode *)_node; - - delete inode; - - return B_NO_ERROR; -} - - -static int -bfs_remove_vnode(void *_ns, void *_node, char reenter) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - // The "chkbfs" functionality uses this flag to prevent the space used - // up by the inode from being freed - this flag is set only in situations - // where this is a good idea... (the block bitmap will get fixed anyway - // in this case). - if (inode->Flags() & INODE_DONT_FREE_SPACE) { - delete inode; - return B_OK; - } - - // If the inode isn't in use anymore, we were called before - // bfs_unlink() returns - in this case, we can just use the - // transaction which has already deleted the inode. - Transaction localTransaction, *transaction = NULL; - - Journal *journal = volume->GetJournal(volume->ToBlock(inode->Parent())); - if (journal != NULL) - transaction = journal->CurrentTransaction(); - - if (transaction == NULL) { - transaction = &localTransaction; - localTransaction.Start(volume, inode->BlockNumber()); - } - - status_t status = inode->Free(transaction); - if (status == B_OK) { - if (transaction == &localTransaction) - localTransaction.Done(); - - delete inode; - } - - return status; -} - - -static int -bfs_wake_vnode(void *_ns, void *_node) -{ - return B_OK; -} - - -static int -bfs_suspend_vnode(void *_ns, void *_node) -{ - return B_OK; -} - - -// #pragma mark - - - -/** the walk function just "walks" through a directory looking for the - * specified file. It calls get_vnode() on its vnode-id to init it - * for the kernel. - */ - -static int -bfs_walk(void *_ns, void *_directory, const char *file, char **_resolvedPath, vnode_id *_vnodeID) -{ - //FUNCTION_START(("file = %s\n", file)); - if (_ns == NULL || _directory == NULL || file == NULL || _vnodeID == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - // check access permissions - status_t status = directory->CheckPermissions(R_OK); - if (status < B_OK) - RETURN_ERROR(status); - - BPlusTree *tree; - if (directory->GetTree(&tree) != B_OK) - RETURN_ERROR(B_BAD_VALUE); - - if ((status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID)) < B_OK) { - PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status))); - return status; - } - - RecursiveLocker locker(volume->Lock()); - // we have to hold the volume lock in order to not - // interfere with new_vnode() here - - Inode *inode; - if ((status = get_vnode(volume->ID(), *_vnodeID, (void **)&inode)) != B_OK) { - REPORT_ERROR(status); - return B_ENTRY_NOT_FOUND; - } - - // Is inode a symlink? Then resolve it, if we should - - if (inode->IsSymLink() && _resolvedPath != NULL) { - status_t status = B_OK; - char *newPath = NULL; - - // Symbolic links can store their target in the data stream (for links - // that take more than 144 bytes of storage [the size of the data_stream - // structure]), or directly instead of the data_stream class - // So we have to deal with both cases here. - - // Note: we would naturally call bfs_read_link() here, but the API of the - // vnode layer would require us to always reserve a large chunk of memory - // for the path, so we're not going to do that - - if (inode->Flags() & INODE_LONG_SYMLINK) { - size_t readBytes = inode->Size(); - char *data = (char *)malloc(readBytes); - if (data != NULL) { - status = inode->ReadAt(0, (uint8 *)data, &readBytes); - if (status == B_OK && readBytes == inode->Size()) - status = new_path(data, &newPath); - - free(data); - } else - status = B_NO_MEMORY; - } else - status = new_path((char *)&inode->Node()->short_symlink, &newPath); - - put_vnode(volume->ID(), inode->ID()); - if (status == B_OK) - *_resolvedPath = newPath; - - RETURN_ERROR(status); - } - - return B_OK; -} - - -static int -bfs_ioctl(void *_ns, void *_node, void *_cookie, int cmd, void *buffer, size_t bufferLength) -{ - FUNCTION_START(("node = %p, cmd = %d, buf = %p, len = %ld\n", _node, cmd, buffer, bufferLength)); - - if (_ns == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - switch (cmd) { - case IOCTL_FILE_UNCACHED_IO: - { - if (inode == NULL) - return B_BAD_VALUE; - - // if the inode is already set up for uncached access, bail out - if (inode->Flags() & INODE_NO_CACHE) { - FATAL(("File %Ld is already uncached\n", inode->ID())); - return B_ERROR; - } - - PRINT(("uncached access to inode %Ld\n", inode->ID())); - - // ToDo: sync the cache for this file! - // Unfortunately, we can't remove any blocks from the cache in BeOS, - // that means we can't guarantee consistency for the file contents - // spanning over both access modes. - - // request buffers for being able to access the file without - // using the cache or allocating memory - status_t status = volume->Pool().RequestBuffers(volume->BlockSize()); - if (status == B_OK) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_NO_CACHE); - return status; - } - case IOCTL_CREATE_TIME: - { - if (inode == NULL || buffer == NULL) - return B_BAD_VALUE; - - off_t *creationTime = (off_t *)buffer; - *creationTime = inode->Node()->CreateTime(); - return B_OK; - } - case IOCTL_MODIFIED_TIME: - { - if (inode == NULL || buffer == NULL) - return B_BAD_VALUE; - - off_t *modifiedTime = (off_t *)buffer; - *modifiedTime = inode->LastModified(); - return B_OK; - } - case BFS_IOCTL_VERSION: - { - uint32 *version = (uint32 *)buffer; - - *version = 0x10000; - return B_OK; - } - case BFS_IOCTL_START_CHECKING: - { - // start checking - BlockAllocator &allocator = volume->Allocator(); - check_control *control = (check_control *)buffer; - - status_t status = allocator.StartChecking(control); - if (status == B_OK && inode != NULL) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING); - - return status; - } - case BFS_IOCTL_STOP_CHECKING: - { - // stop checking - BlockAllocator &allocator = volume->Allocator(); - check_control *control = (check_control *)buffer; - - status_t status = allocator.StopChecking(control); - if (status == B_OK && inode != NULL) - inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING); - - return status; - } - case BFS_IOCTL_CHECK_NEXT_NODE: - { - // check next - BlockAllocator &allocator = volume->Allocator(); - check_control *control = (check_control *)buffer; - - return allocator.CheckNextNode(control); - } -#ifdef DEBUG - case 56742: - { - // allocate all free blocks and zero them out (a test for the BlockAllocator)! - BlockAllocator &allocator = volume->Allocator(); - Transaction transaction(volume, 0); - CachedBlock cached(volume); - block_run run; - while (allocator.AllocateBlocks(&transaction, 8, 0, 64, 1, run) == B_OK) { - PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, run.start, run.length)); - for (int32 i = 0;i < run.length;i++) { - uint8 *block = cached.SetTo(run); - if (block != NULL) { - memset(block, 0, volume->BlockSize()); - cached.WriteBack(&transaction); - } - } - } - return B_OK; - } - case 56743: - dump_super_block(&volume->SuperBlock()); - return B_OK; - case 56744: - if (inode != NULL) - dump_inode(inode->Node()); - return B_OK; - case 56745: - if (inode != NULL) - dump_block((const char *)inode->Node(), volume->BlockSize()); - return B_OK; -#endif - } - return B_BAD_VALUE; -} - - -/** Sets the open-mode flags for the open file cookie - only - * supports O_APPEND currently, but that should be sufficient - * for a file system. - */ - -static int -bfs_setflags(void *_ns, void *_node, void *_cookie, int flags) -{ - FUNCTION_START(("node = %p, flags = %d", _node, flags)); - - file_cookie *cookie = (file_cookie *)_cookie; - cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND); - - return B_OK; -} - - -int -bfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync) -{ - FUNCTION_START(("event = %d, ref = %lu, sync = %p\n", event, ref, sync)); - notify_select_event(sync, ref); - - return B_OK; -} - - -static int -bfs_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync) -{ - FUNCTION(); - return B_OK; -} - - -static int -bfs_fsync(void *_ns, void *_node) -{ - FUNCTION(); - if (_node == NULL) - return B_BAD_VALUE; - - Inode *inode = (Inode *)_node; - return inode->Sync(); -} - - -/** Fills in the stat struct for a node - */ - -static int -bfs_read_stat(void *_ns, void *_node, struct stat *st) -{ - FUNCTION(); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - bfs_inode *node = inode->Node(); - - st->st_dev = volume->ID(); - st->st_ino = inode->ID(); - st->st_nlink = 1; - st->st_blksize = BFS_IO_SIZE; - - st->st_uid = node->UserID(); - st->st_gid = node->GroupID(); - st->st_mode = node->Mode(); - st->st_size = node->data.Size(); - - st->st_atime = time(NULL); - st->st_mtime = st->st_ctime = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); - st->st_crtime = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); - - return B_NO_ERROR; -} - - -static int -bfs_write_stat(void *_ns, void *_node, struct stat *stat, long mask) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL || stat == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - // that may be incorrect here - I don't think we need write access to - // change most of the stat... - // we should definitely check a bit more if the new stats are correct and valid... - - status_t status = inode->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - - WriteLocked locked(inode->Lock()); - if (locked.IsLocked() < B_OK) - RETURN_ERROR(B_ERROR); - - Transaction transaction(volume, inode->BlockNumber()); - - bfs_inode *node = inode->Node(); - - if (mask & WSTAT_SIZE) { - // Since WSTAT_SIZE is the only thing that can fail directly, we - // do it first, so that the inode state will still be consistent - // with the on-disk version - if (inode->IsDirectory()) - return B_IS_A_DIRECTORY; - - if (inode->Size() != stat->st_size) { - status = inode->SetFileSize(&transaction, stat->st_size); - if (status < B_OK) - return status; - - // fill the new blocks (if any) with zeros - inode->FillGapWithZeros(inode->OldSize(), inode->Size()); - - Index index(volume); - index.UpdateSize(&transaction, inode); - - if ((mask & WSTAT_MTIME) == 0) - index.UpdateLastModified(&transaction, inode); - } - } - - if (mask & WSTAT_MODE) { - PRINT(("original mode = %ld, stat->st_mode = %d\n", node->mode, stat->st_mode)); - node->mode = node->mode & ~S_IUMSK | stat->st_mode & S_IUMSK; - } - - if (mask & WSTAT_UID) - node->uid = stat->st_uid; - if (mask & WSTAT_GID) - node->gid = stat->st_gid; - - if (mask & WSTAT_MTIME) { - if (inode->IsDirectory()) { - // directory modification times are not part of the index - node->last_modified_time = HOST_ENDIAN_TO_BFS_INT64( - (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); - } else if (!inode->IsDeleted()) { - // Index::UpdateLastModified() will set the new time in the inode - Index index(volume); - index.UpdateLastModified(&transaction, inode, - (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); - } - } - if (mask & WSTAT_CRTIME) { - node->create_time = HOST_ENDIAN_TO_BFS_INT64( - (bigtime_t)stat->st_crtime << INODE_TIME_SHIFT); - } - - if ((status = inode->WriteBack(&transaction)) == B_OK) - transaction.Done(); - - notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); - - return status; -} - - -int -bfs_create(void *_ns, void *_directory, const char *name, int omode, int mode, - vnode_id *vnodeID, void **_cookie) -{ - FUNCTION_START(("name = \"%s\", perms = %d, omode = %d\n", name, mode, omode)); - - if (_ns == NULL || _directory == NULL || _cookie == NULL - || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - if (!directory->IsDirectory()) - RETURN_ERROR(B_BAD_TYPE); - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - - // We are creating the cookie at this point, so that we don't have - // to remove the inode if we don't have enough free memory later... - file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); - if (cookie == NULL) - RETURN_ERROR(B_NO_MEMORY); - - // initialize the cookie - cookie->open_mode = omode; - cookie->last_size = 0; - cookie->last_notification = system_time(); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - status = Inode::Create(&transaction, directory, name, S_FILE | (mode & S_IUMSK), - omode, 0, vnodeID); - - if (status >= B_OK) { - transaction.Done(); - - // register the cookie - *_cookie = cookie; - - notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, *vnodeID, name); - } else - free(cookie); - - return status; -} - - -int -bfs_symlink(void *_ns, void *_directory, const char *name, const char *path) -{ - FUNCTION(); - - if (_ns == NULL || _directory == NULL || path == NULL - || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - if (!directory->IsDirectory()) - RETURN_ERROR(B_BAD_TYPE); - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - Inode *link; - off_t id; - status = Inode::Create(&transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link); - if (status < B_OK) - RETURN_ERROR(status); - - size_t length = strlen(path); - if (length < SHORT_SYMLINK_NAME_LENGTH) { - strcpy(link->Node()->short_symlink, path); - status = link->WriteBack(&transaction); - } else { - link->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK | INODE_LOGGED); - // The following call will have to write the inode back, so - // we don't have to do that here... - status = link->WriteAt(&transaction, 0, (const uint8 *)path, &length); - } - // ToDo: would be nice if Inode::Create() would let the INODE_NOT_READY - // flag set until here, so that it can be accessed directly - - // Inode::Create() left the inode locked in memory - put_vnode(volume->ID(), id); - - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); - } - - return status; -} - - -int -bfs_link(void *ns, void *dir, const char *name, void *node) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - // This one won't be implemented in a binary compatible BFS - - return B_ERROR; -} - - -int -bfs_unlink(void *_ns, void *_directory, const char *name) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - if (!strcmp(name, "..") || !strcmp(name, ".")) - return B_NOT_ALLOWED; - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - return status; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - off_t id; - if ((status = directory->Remove(&transaction, name, &id)) == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); - } - return status; -} - - -int -bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const char *newName) -{ - FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = \"%s\"\n", _oldDir, oldName, _newDir, newName)); - - // there might be some more tests needed?! - if (_ns == NULL || _oldDir == NULL || _newDir == NULL - || oldName == NULL || *oldName == '\0' - || newName == NULL || *newName == '\0' - || !strcmp(oldName, ".") || !strcmp(oldName, "..") - || !strcmp(newName, ".") || !strcmp(newName, "..") - || strchr(newName, '/') != NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *oldDirectory = (Inode *)_oldDir; - Inode *newDirectory = (Inode *)_newDir; - - // are we already done? - if (oldDirectory == newDirectory && !strcmp(oldName, newName)) - return B_OK; - - // are we allowed to do what we've been told? - status_t status = oldDirectory->CheckPermissions(W_OK); - if (status == B_OK) - status = newDirectory->CheckPermissions(W_OK); - if (status < B_OK) - return status; - - RecursiveLocker locker(volume->Lock()); - - // get the directory's tree, and a pointer to the inode which should be changed - BPlusTree *tree; - status = oldDirectory->GetTree(&tree); - if (status < B_OK) - RETURN_ERROR(status); - - off_t id; - status = tree->Find((const uint8 *)oldName, strlen(oldName), &id); - if (status < B_OK) - RETURN_ERROR(status); - - Vnode vnode(volume, id); - Inode *inode; - if (vnode.Get(&inode) < B_OK) - return B_IO_ERROR; - - // Don't move a directory into one of its children - we soar up - // from the newDirectory to either the root node or the old - // directory, whichever comes first. - // If we meet our inode on that way, we have to bail out. - - if (oldDirectory != newDirectory) { - vnode_id parent = volume->ToVnode(newDirectory->Parent()); - vnode_id root = volume->RootNode()->ID(); - - while (true) { - if (parent == id) - return B_BAD_VALUE; - else if (parent == root || parent == oldDirectory->ID()) - break; - - Vnode vnode(volume, parent); - Inode *parentNode; - if (vnode.Get(&parentNode) < B_OK) - return B_ERROR; - - parent = volume->ToVnode(parentNode->Parent()); - } - } - - // Everything okay? Then lets get to work... - - Transaction transaction(volume, oldDirectory->BlockNumber()); - - // First, try to make sure there is nothing that will stop us in - // the target directory - since this is the only non-critical - // failure, we will test this case first - BPlusTree *newTree = tree; - if (newDirectory != oldDirectory) { - status = newDirectory->GetTree(&newTree); - if (status < B_OK) - RETURN_ERROR(status); - } - - status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); - if (status == B_NAME_IN_USE) { - // If there is already a file with that name, we have to remove - // it, as long it's not a directory with files in it - off_t clobber; - if (newTree->Find((const uint8 *)newName, strlen(newName), &clobber) < B_OK) - return B_NAME_IN_USE; - if (clobber == id) - return B_BAD_VALUE; - - Vnode vnode(volume, clobber); - Inode *other; - if (vnode.Get(&other) < B_OK) - return B_NAME_IN_USE; - - status = newDirectory->Remove(&transaction, newName, NULL, other->IsDirectory()); - if (status < B_OK) - return status; - - notify_listener(B_ENTRY_REMOVED, volume->ID(), newDirectory->ID(), 0, clobber, NULL); - - status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); - } - if (status < B_OK) - return status; - - // If anything fails now, we have to remove the inode from the - // new directory in any case to restore the previous state - status_t bailStatus = B_OK; - - // update the name only when they differ - bool nameUpdated = false; - if (strcmp(oldName, newName)) { - status = inode->SetName(&transaction, newName); - if (status == B_OK) { - Index index(volume); - index.UpdateName(&transaction, oldName, newName, inode); - nameUpdated = true; - } - } - - if (status == B_OK) { - status = tree->Remove(&transaction, (const uint8 *)oldName, strlen(oldName), id); - if (status == B_OK) { - inode->Node()->parent = newDirectory->BlockRun(); - - // if it's a directory, update the parent directory pointer - // in its tree if necessary - BPlusTree *movedTree = NULL; - if (oldDirectory != newDirectory - && inode->IsDirectory() - && (status = inode->GetTree(&movedTree)) == B_OK) - status = movedTree->Replace(&transaction, (const uint8 *)"..", 2, newDirectory->ID()); - - if (status == B_OK) { - status = inode->WriteBack(&transaction); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_MOVED, volume->ID(), oldDirectory->ID(), - newDirectory->ID(), id, newName); - return B_OK; - } - } - // If we get here, something has gone wrong already! - - // Those better don't fail, or we switch to a read-only - // device for safety reasons (Volume::Panic() does this - // for us) - // Anyway, if we overwrote a file in the target directory - // this is lost now (only in-memory, not on-disk)... - bailStatus = tree->Insert(&transaction, (const uint8 *)oldName, strlen(oldName), id); - if (movedTree != NULL) - movedTree->Replace(&transaction, (const uint8 *)"..", 2, oldDirectory->ID()); - } - } - - if (bailStatus == B_OK && nameUpdated) { - bailStatus = inode->SetName(&transaction, oldName); - if (status == B_OK) { - // update inode and index - inode->WriteBack(&transaction); - - Index index(volume); - index.UpdateName(&transaction, newName, oldName, inode); - } - } - - if (bailStatus == B_OK) - bailStatus = newTree->Remove(&transaction, (const uint8 *)newName, strlen(newName), id); - - if (bailStatus < B_OK) - volume->Panic(); - - return status; -} - - -/** Opens the file with the specified mode. - */ - -static int -bfs_open(void *_ns, void *_node, int omode, void **_cookie) -{ - FUNCTION(); - if (_ns == NULL || _node == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - // we can't open a file which uses uncached access twice - if (inode->Flags() & INODE_NO_CACHE) - return B_BUSY; - - // opening a directory read-only is allowed, although you can't read - // any data from it. - if (inode->IsDirectory() && omode & O_RWMASK) { - omode = omode & ~O_RWMASK; - // ToDo: for compatibility reasons, we don't return an error here... - // e.g. "copyattr" tries to do that - //return B_IS_A_DIRECTORY; - } - - status_t status = inode->CheckPermissions(oModeToAccess(omode)); - if (status < B_OK) - RETURN_ERROR(status); - - // we could actually use the cookie to keep track of: - // - the last block_run - // - the location in the data_stream (indirect, double indirect, - // position in block_run array) - // - // This could greatly speed up continuous reads of big files, especially - // in the indirect block section. - - file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); - if (cookie == NULL) - RETURN_ERROR(B_NO_MEMORY); - - // initialize the cookie - cookie->open_mode = omode; - // needed by e.g. bfs_write() for O_APPEND - cookie->last_size = inode->Size(); - cookie->last_notification = system_time(); - - // Should we truncate the file? - if (omode & O_TRUNC) { - WriteLocked locked(inode->Lock()); - Transaction transaction(volume, inode->BlockNumber()); - - status_t status = inode->SetFileSize(&transaction, 0); - if (status < B_OK) { - // bfs_free_cookie() is only called if this function is successful - free(cookie); - return status; - } - - transaction.Done(); - } - - *_cookie = cookie; - return B_OK; -} - - -/** Read a file specified by node, using information in cookie - * and at offset specified by pos. read len bytes into buffer buf. - */ - -static int -bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length) -{ - //FUNCTION(); - Inode *inode = (Inode *)_node; - - if (!inode->HasUserAccessableStream()) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - ReadLocked locked(inode->Lock()); - return inode->ReadAt(pos, (uint8 *)buffer, _length); -} - - -int -bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) -{ - //FUNCTION(); - // uncomment to be more robust against a buggy vnode layer ;-) - //if (_ns == NULL || _node == NULL || _cookie == NULL) - // return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - if (!inode->HasUserAccessableStream()) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - file_cookie *cookie = (file_cookie *)_cookie; - - if (cookie->open_mode & O_APPEND) - pos = inode->Size(); - - WriteLocked locked(inode->Lock()); - if (locked.IsLocked() < B_OK) - RETURN_ERROR(B_ERROR); - - Transaction transaction; - // We are not starting the transaction here, since - // it might not be needed at all (the contents of - // regular files aren't logged) - - status_t status = inode->WriteAt(&transaction, pos, (const uint8 *)buffer, _length); - - if (status == B_OK) - transaction.Done(); - - if (status == B_OK && (inode->Flags() & INODE_NO_CACHE) == 0) { - // uncached files don't cause notifications during access, and - // never want to write back any cached blocks - - // periodically notify if the file size has changed - // ToDo: should we better test for a change in the last_modified time only? - if (cookie->last_size != inode->Size() - && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { - notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); - cookie->last_size = inode->Size(); - cookie->last_notification = system_time(); - } - - // This will flush the dirty blocks to disk from time to time. - // It's done here and not in Inode::WriteAt() so that it won't - // add to the duration of a transaction - it might even be a - // good idea to offload those calls to another thread - volume->WriteCachedBlocksIfNecessary(); - } - - return status; -} - - -/** Do whatever is necessary to close a file, EXCEPT for freeing - * the cookie! - */ - -static int -bfs_close(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _node == NULL || _cookie == NULL) - return B_BAD_VALUE; - - return B_OK; -} - - -static int -bfs_free_cookie(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL || _cookie == NULL) - return B_BAD_VALUE; - - file_cookie *cookie = (file_cookie *)_cookie; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - if (cookie->open_mode & O_RWMASK) { -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - ReadLocked locked(inode->Lock()); - - // trim the preallocated blocks and update the size, - // and last_modified indices if needed - - Transaction transaction(volume, inode->BlockNumber()); - status_t status = B_OK; - bool changed = false; - Index index(volume); - - if (inode->OldSize() != inode->Size()) { - status = inode->Trim(&transaction); - if (status < B_OK) - FATAL(("Could not trim preallocated blocks!")); - - index.UpdateSize(&transaction, inode); - changed = true; - } - if (inode->OldLastModified() != inode->LastModified()) { - index.UpdateLastModified(&transaction, inode, inode->LastModified()); - changed = true; - - // updating the index doesn't write back the inode - inode->WriteBack(&transaction); - } - - if (status == B_OK) - transaction.Done(); - - if (changed) - notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); - } - - if (inode->Flags() & INODE_NO_CACHE) { - volume->Pool().ReleaseBuffers(); - inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_NO_CACHE); - // We don't need to save the inode, because INODE_NO_CACHE is a - // non-permanent flag which will be removed when the inode is loaded - // into memory. - } - - if (inode->Flags() & INODE_CHKBFS_RUNNING) { - // "chkbfs" exited abnormally, so we have to stop it here... - FATAL(("check process was aborted!\n")); - volume->Allocator().StopChecking(NULL); - } - - return B_OK; -} - - -/** Checks access permissions, return B_NOT_ALLOWED if the action - * is not allowed. - */ - -static int -bfs_access(void *_ns, void *_node, int accessMode) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL) - return B_BAD_VALUE; - - Inode *inode = (Inode *)_node; - status_t status = inode->CheckPermissions(accessMode); - if (status < B_OK) - RETURN_ERROR(status); - - return B_OK; -} - - -static int -bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize) -{ - FUNCTION(); - - Inode *inode = (Inode *)_node; - - if (!inode->IsSymLink()) - RETURN_ERROR(B_BAD_VALUE); - - if (inode->Flags() & INODE_LONG_SYMLINK) { - status_t status = inode->ReadAt(0, (uint8 *)buffer, bufferSize); - if (status < B_OK) - RETURN_ERROR(status); - - *bufferSize = inode->Size(); - return B_OK; - } - - size_t numBytes = strlen((char *)&inode->Node()->short_symlink); - uint32 bytes = numBytes; - if (bytes > *bufferSize) - bytes = *bufferSize; - - memcpy(buffer, inode->Node()->short_symlink, bytes); - *bufferSize = numBytes; - - return B_OK; -} - - -// #pragma mark - -// Directory functions - - -static int -bfs_mkdir(void *_ns, void *_directory, const char *name, int mode) -{ - FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode)); - - if (_ns == NULL || _directory == NULL - || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - if (!directory->IsDirectory()) - RETURN_ERROR(B_BAD_TYPE); - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - // Inode::Create() locks the inode if we pass the "id" parameter, but we - // need it anyway - off_t id; - status = Inode::Create(&transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id); - if (status == B_OK) { - put_vnode(volume->ID(), id); - transaction.Done(); - - notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); - } - - return status; -} - - -static int -bfs_rmdir(void *_ns, void *_directory, const char *name) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - off_t id; - status_t status = directory->Remove(&transaction, name, &id, true); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); - } - - return status; -} - - -/** creates fs-specific "cookie" struct that keeps track of where - * you are at in reading through directory entries in bfs_readdir. - */ - -static int -bfs_open_dir(void *_ns, void *_node, void **_cookie) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Inode *inode = (Inode *)_node; - - // we don't ask here for directories only, because the bfs_open_index_dir() - // function utilizes us (so we must be able to open indices as well) - if (!inode->IsContainer()) - RETURN_ERROR(B_BAD_VALUE); - - BPlusTree *tree; - if (inode->GetTree(&tree) != B_OK) - RETURN_ERROR(B_BAD_VALUE); - - TreeIterator *iterator = new TreeIterator(tree); - if (iterator == NULL) - RETURN_ERROR(B_NO_MEMORY); - - *_cookie = iterator; - return B_OK; -} - - -static int -bfs_read_dir(void *_ns, void *_node, void *_cookie, long *num, - struct dirent *dirent, size_t bufferSize) -{ - FUNCTION(); - - TreeIterator *iterator = (TreeIterator *)_cookie; - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - uint16 length; - vnode_id id; - status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id); - if (status == B_ENTRY_NOT_FOUND) { - *num = 0; - return B_OK; - } else if (status != B_OK) - RETURN_ERROR(status); - - Volume *volume = (Volume *)_ns; - - dirent->d_dev = volume->ID(); - dirent->d_ino = id; - -#ifdef KEEP_WRONG_DIRENT_RECLEN - dirent->d_reclen = length; -#else - dirent->d_reclen = sizeof(struct dirent) + length; -#endif - - *num = 1; - return B_OK; -} - - -/** Sets the TreeIterator back to the beginning of the directory - */ - -static int -bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie) -{ - FUNCTION(); - TreeIterator *iterator = (TreeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return iterator->Rewind(); -} - - -static int -bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/) -{ - FUNCTION(); - // Do whatever you need to to close a directory, but DON'T free the cookie! - return B_OK; -} - - -static int -bfs_free_dir_cookie(void *ns, void *node, void *_cookie) -{ - TreeIterator *iterator = (TreeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - delete iterator; - return B_OK; -} - - -// #pragma mark - -// Attribute functions - - -static int -bfs_open_attrdir(void *_ns, void *_node, void **cookie) -{ - FUNCTION(); - - Inode *inode = (Inode *)_node; - if (inode == NULL || inode->Node() == NULL) - RETURN_ERROR(B_ERROR); - - AttributeIterator *iterator = new AttributeIterator(inode); - if (iterator == NULL) - RETURN_ERROR(B_NO_MEMORY); - - *cookie = iterator; - return B_OK; -} - - -static int -bfs_close_attrdir(void *ns, void *node, void *cookie) -{ - FUNCTION(); - return B_OK; -} - - -static int -bfs_free_attrdir_cookie(void *ns, void *node, void *_cookie) -{ - FUNCTION(); - AttributeIterator *iterator = (AttributeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - delete iterator; - return B_OK; -} - - -static int -bfs_rewind_attrdir(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - - AttributeIterator *iterator = (AttributeIterator *)_cookie; - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - RETURN_ERROR(iterator->Rewind()); -} - - -static int -bfs_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent *dirent, size_t bufsize) -{ - FUNCTION(); - AttributeIterator *iterator = (AttributeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - uint32 type; - size_t length; - status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino); - if (status == B_ENTRY_NOT_FOUND) { - *num = 0; - return B_OK; - } else if (status != B_OK) - RETURN_ERROR(status); - - Volume *volume = (Volume *)_ns; - - dirent->d_dev = volume->ID(); -#ifdef KEEP_WRONG_DIRENT_RECLEN - dirent->d_reclen = length; -#else - dirent->d_reclen = sizeof(struct dirent) + length; -#endif - - *num = 1; - return B_OK; -} - - -static int -bfs_remove_attr(void *_ns, void *_node, const char *name) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - if (_ns == NULL || _node == NULL || name == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - status_t status = inode->CheckPermissions(W_OK); - if (status < B_OK) - return status; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, inode->BlockNumber()); - - status = inode->RemoveAttribute(&transaction, name); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); - } - - RETURN_ERROR(status); -} - - -static int -bfs_rename_attr(void *ns, void *node, const char *oldname, const char *newname) -{ - FUNCTION_START(("name = \"%s\", to = \"%s\"\n", oldname, newname)); - - // ToDo: implement bfs_rename_attr()! - // I'll skip the implementation here, and will do it for OpenBeOS - at least - // there will be an API to move one attribute to another file, making that - // function much more complicated - oh joy ;-) - - RETURN_ERROR(B_ENTRY_NOT_FOUND); -} - - -static int -bfs_stat_attr(void *ns, void *_node, const char *name, struct attr_info *attrInfo) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - Inode *inode = (Inode *)_node; - if (inode == NULL || inode->Node() == NULL) - RETURN_ERROR(B_ERROR); - - // first, try to find it in the small data region - small_data *smallData = NULL; - if (inode->SmallDataLock().Lock() == B_OK) { - if ((smallData = inode->FindSmallData((const char *)name)) != NULL) { - attrInfo->type = smallData->Type(); - attrInfo->size = smallData->DataSize(); - } - inode->SmallDataLock().Unlock(); - } - if (smallData != NULL) - return B_OK; - - // then, search in the attribute directory - Inode *attribute; - status_t status = inode->GetAttribute(name, &attribute); - if (status == B_OK) { - attrInfo->type = attribute->Type(); - attrInfo->size = attribute->Size(); - - inode->ReleaseAttribute(attribute); - return B_OK; - } - - RETURN_ERROR(status); -} - - -static int -bfs_write_attr(void *_ns, void *_node, const char *name, int type, const void *buffer, - size_t *_length, off_t pos) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - if (_ns == NULL || _node == NULL || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - // Writing the name attribute using this function is not allowed, - // also using the reserved indices name, last_modified, and size - // shouldn't be allowed. - // ToDo: we might think about allowing to update those values, but - // really change their corresponding values in the bfs_inode structure - if (name[0] == FILE_NAME_NAME && name[1] == '\0' - || !strcmp(name, "name") - || !strcmp(name, "last_modified") - || !strcmp(name, "size")) - RETURN_ERROR(B_NOT_ALLOWED); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - status_t status = inode->CheckPermissions(W_OK); - if (status < B_OK) - return status; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, inode->BlockNumber()); - - status = inode->WriteAttribute(&transaction, name, type, pos, (const uint8 *)buffer, _length); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); - } - - return status; -} - - -static int -bfs_read_attr(void *_ns, void *_node, const char *name, int type, void *buffer, - size_t *_length, off_t pos) -{ - FUNCTION(); - Inode *inode = (Inode *)_node; - - if (inode == NULL || name == NULL || *name == '\0' || buffer == NULL) - RETURN_ERROR(B_BAD_VALUE); - - status_t status = inode->CheckPermissions(R_OK); - if (status < B_OK) - return status; - - return inode->ReadAttribute(name, type, pos, (uint8 *)buffer, _length); -} - - -// #pragma mark - -// Index functions - - -static int -bfs_open_indexdir(void *_ns, void **_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - - if (volume->IndicesNode() == NULL) - RETURN_ERROR(B_ENTRY_NOT_FOUND); - - // Since the indices root node is just a directory, and we are storing - // a pointer to it in our Volume object, we can just use the directory - // traversal functions. - // In fact we're storing it in the Volume object for that reason. - - RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_close_indexdir(void *_ns, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_close_dir(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_free_indexdir_cookie(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_free_dir_cookie(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_rewind_indexdir(void *_ns, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_rewind_dir(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_read_indexdir(void *_ns, void *_cookie, long *num, struct dirent *dirent, size_t bufferSize) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_read_dir(_ns, volume->IndicesNode(), _cookie, num, dirent, bufferSize)); -} - - -static int -bfs_create_index(void *_ns, const char *name, int type, int flags) -{ - FUNCTION_START(("name = \"%s\", type = %d, flags = %d\n", name, type, flags)); - if (_ns == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - if (volume->IsReadOnly()) - return B_READ_ONLY_DEVICE; - - // only root users are allowed to create indices - if (geteuid() != 0) - return B_NOT_ALLOWED; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, volume->Indices()); - - Index index(volume); - status_t status = index.Create(&transaction, name, type); - - if (status == B_OK) - transaction.Done(); - - RETURN_ERROR(status); -} - - -static int -bfs_remove_index(void *_ns, const char *name) -{ - FUNCTION(); - if (_ns == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - if (volume->IsReadOnly()) - return B_READ_ONLY_DEVICE; - - // only root users are allowed to remove indices - if (geteuid() != 0) - return B_NOT_ALLOWED; - - Inode *indices; - if ((indices = volume->IndicesNode()) == NULL) - return B_ENTRY_NOT_FOUND; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, volume->Indices()); - - status_t status = indices->Remove(&transaction, name); - if (status == B_OK) - transaction.Done(); - - RETURN_ERROR(status); -} - - -static int -bfs_rename_index(void *ns, const char *oldname, const char *newname) -{ - FUNCTION_START(("from = %s, to = %s\n", oldname, newname)); - - // Well, renaming an index doesn't make that much sense, as you - // would also need to remove every file in it (or the index - // would contain wrong data) - // But in that case, you can better remove the old one, and - // create a new one... - // There is also no way to call this function from a userland - // application. - - RETURN_ERROR(B_ENTRY_NOT_FOUND); -} - - -static int -bfs_stat_index(void *_ns, const char *name, struct index_info *indexInfo) -{ - FUNCTION_START(("name = %s\n", name)); - if (_ns == NULL || name == NULL || indexInfo == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Index index(volume); - status_t status = index.SetTo(name); - if (status < B_OK) - RETURN_ERROR(status); - - bfs_inode *node = index.Node()->Node(); - - indexInfo->type = index.Type(); - indexInfo->size = node->data.Size(); - indexInfo->modification_time = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); - indexInfo->creation_time = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); - indexInfo->uid = node->UserID(); - indexInfo->gid = node->GroupID(); - - return B_OK; -} - - -// #pragma mark - -// Query functions - - -static int -bfs_open_query(void *_ns, const char *queryString, ulong flags, port_id port, - long token, void **cookie) -{ - FUNCTION(); - if (_ns == NULL || queryString == NULL || cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token)); - - Volume *volume = (Volume *)_ns; - - Expression *expression = new Expression((char *)queryString); - if (expression == NULL) - RETURN_ERROR(B_NO_MEMORY); - - if (expression->InitCheck() < B_OK) { - FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position())); - delete expression; - RETURN_ERROR(B_BAD_VALUE); - } - - Query *query = new Query(volume, expression, flags); - if (query == NULL) { - delete expression; - RETURN_ERROR(B_NO_MEMORY); - } - - if (flags & B_LIVE_QUERY) - query->SetLiveMode(port, token); - - *cookie = (void *)query; - - return B_OK; -} - - -static int -bfs_close_query(void *ns, void *cookie) -{ - FUNCTION(); - return B_OK; -} - - -static int -bfs_free_query_cookie(void *ns, void *node, void *cookie) -{ - FUNCTION(); - if (cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Query *query = (Query *)cookie; - Expression *expression = query->GetExpression(); - delete query; - delete expression; - - return B_OK; -} - - -static int -bfs_read_query(void */*ns*/, void *cookie, long *num, struct dirent *dirent, size_t bufferSize) -{ - FUNCTION(); - Query *query = (Query *)cookie; - if (query == NULL) - RETURN_ERROR(B_BAD_VALUE); - - status_t status = query->GetNextEntry(dirent, bufferSize); - if (status == B_OK) - *num = 1; - else if (status == B_ENTRY_NOT_FOUND) - *num = 0; - else - return status; - - return B_OK; -} -