From a38a92c955f46ea3dbd3929ad1398c2b418e80cb Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 27 Apr 2007 12:17:22 +0000 Subject: [PATCH] Beginnings of a new, better portable FS shell with Haiku FS interface. Doesn't do anything ATM, but already provides the required system interface (VFS, caches, POSIX functions). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20859 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/fs_shell/DoublyLinkedList.h | 584 ++ headers/private/fs_shell/KPath.h | 62 + headers/private/fs_shell/Stack.h | 87 + headers/private/fs_shell/fssh_api_wrapper.h | 1477 +++++ headers/private/fs_shell/fssh_atomic.h | 32 + headers/private/fs_shell/fssh_auto_locker.h | 162 + headers/private/fs_shell/fssh_byte_order.h | 131 + headers/private/fs_shell/fssh_defs.h | 51 + headers/private/fs_shell/fssh_dirent.h | 42 + .../private/fs_shell/fssh_disk_device_defs.h | 156 + .../fs_shell/fssh_disk_device_manager.h | 134 + headers/private/fs_shell/fssh_drivers.h | 217 + headers/private/fs_shell/fssh_errno.h | 25 + headers/private/fs_shell/fssh_errors.h | 283 + headers/private/fs_shell/fssh_fcntl.h | 83 + headers/private/fs_shell/fssh_fs_attr.h | 50 + headers/private/fs_shell/fssh_fs_cache.h | 96 + headers/private/fs_shell/fssh_fs_index.h | 42 + headers/private/fs_shell/fssh_fs_info.h | 53 + headers/private/fs_shell/fssh_fs_interface.h | 284 + headers/private/fs_shell/fssh_fs_query.h | 43 + headers/private/fs_shell/fssh_fs_volume.h | 34 + headers/private/fs_shell/fssh_kernel_export.h | 51 + headers/private/fs_shell/fssh_kernel_priv.h | 56 + headers/private/fs_shell/fssh_module.h | 62 + headers/private/fs_shell/fssh_node_monitor.h | 67 + headers/private/fs_shell/fssh_os.h | 211 + headers/private/fs_shell/fssh_stat.h | 114 + headers/private/fs_shell/fssh_stdio.h | 40 + headers/private/fs_shell/fssh_string.h | 91 + headers/private/fs_shell/fssh_time.h | 86 + .../private/fs_shell/fssh_type_constants.h | 78 + headers/private/fs_shell/fssh_types.h | 33 + headers/private/fs_shell/fssh_uio.h | 35 + headers/private/fs_shell/fssh_unistd.h | 87 + src/tools/Jamfile | 1 + src/tools/fs_shell/Jamfile | 38 + src/tools/fs_shell/KPath.cpp | 298 + src/tools/fs_shell/atomic.cpp | 52 + src/tools/fs_shell/block_cache.cpp | 1219 ++++ src/tools/fs_shell/block_cache_priv.h | 91 + src/tools/fs_shell/errno.cpp | 31 + src/tools/fs_shell/fcntl.cpp | 34 + src/tools/fs_shell/fd.cpp | 736 +++ src/tools/fs_shell/fd.h | 117 + src/tools/fs_shell/file_cache.cpp | 1872 ++++++ src/tools/fs_shell/fssh.cpp | 11 + src/tools/fs_shell/hash.cpp | 310 + src/tools/fs_shell/hash.h | 51 + src/tools/fs_shell/kernel_export.cpp | 91 + src/tools/fs_shell/list.cpp | 253 + src/tools/fs_shell/list.h | 74 + src/tools/fs_shell/lock.cpp | 243 + src/tools/fs_shell/lock.h | 160 + src/tools/fs_shell/node_monitor.cpp | 66 + src/tools/fs_shell/sem.cpp | 113 + src/tools/fs_shell/sem_unix.cpp | 162 + src/tools/fs_shell/stat.cpp | 54 + src/tools/fs_shell/stat_util.cpp | 227 + src/tools/fs_shell/stat_util.h | 26 + src/tools/fs_shell/stdio.cpp | 51 + src/tools/fs_shell/string.cpp | 281 + src/tools/fs_shell/thread.cpp | 54 + src/tools/fs_shell/time.cpp | 62 + src/tools/fs_shell/uio.cpp | 80 + src/tools/fs_shell/unistd.cpp | 226 + src/tools/fs_shell/vfs.cpp | 5170 +++++++++++++++++ src/tools/fs_shell/vfs.h | 111 + 68 files changed, 17504 insertions(+) create mode 100644 headers/private/fs_shell/DoublyLinkedList.h create mode 100644 headers/private/fs_shell/KPath.h create mode 100644 headers/private/fs_shell/Stack.h create mode 100644 headers/private/fs_shell/fssh_api_wrapper.h create mode 100644 headers/private/fs_shell/fssh_atomic.h create mode 100644 headers/private/fs_shell/fssh_auto_locker.h create mode 100644 headers/private/fs_shell/fssh_byte_order.h create mode 100644 headers/private/fs_shell/fssh_defs.h create mode 100644 headers/private/fs_shell/fssh_dirent.h create mode 100644 headers/private/fs_shell/fssh_disk_device_defs.h create mode 100644 headers/private/fs_shell/fssh_disk_device_manager.h create mode 100644 headers/private/fs_shell/fssh_drivers.h create mode 100644 headers/private/fs_shell/fssh_errno.h create mode 100644 headers/private/fs_shell/fssh_errors.h create mode 100644 headers/private/fs_shell/fssh_fcntl.h create mode 100644 headers/private/fs_shell/fssh_fs_attr.h create mode 100644 headers/private/fs_shell/fssh_fs_cache.h create mode 100644 headers/private/fs_shell/fssh_fs_index.h create mode 100644 headers/private/fs_shell/fssh_fs_info.h create mode 100644 headers/private/fs_shell/fssh_fs_interface.h create mode 100644 headers/private/fs_shell/fssh_fs_query.h create mode 100644 headers/private/fs_shell/fssh_fs_volume.h create mode 100644 headers/private/fs_shell/fssh_kernel_export.h create mode 100644 headers/private/fs_shell/fssh_kernel_priv.h create mode 100644 headers/private/fs_shell/fssh_module.h create mode 100644 headers/private/fs_shell/fssh_node_monitor.h create mode 100644 headers/private/fs_shell/fssh_os.h create mode 100644 headers/private/fs_shell/fssh_stat.h create mode 100644 headers/private/fs_shell/fssh_stdio.h create mode 100644 headers/private/fs_shell/fssh_string.h create mode 100644 headers/private/fs_shell/fssh_time.h create mode 100644 headers/private/fs_shell/fssh_type_constants.h create mode 100644 headers/private/fs_shell/fssh_types.h create mode 100644 headers/private/fs_shell/fssh_uio.h create mode 100644 headers/private/fs_shell/fssh_unistd.h create mode 100644 src/tools/fs_shell/Jamfile create mode 100644 src/tools/fs_shell/KPath.cpp create mode 100644 src/tools/fs_shell/atomic.cpp create mode 100644 src/tools/fs_shell/block_cache.cpp create mode 100644 src/tools/fs_shell/block_cache_priv.h create mode 100644 src/tools/fs_shell/errno.cpp create mode 100644 src/tools/fs_shell/fcntl.cpp create mode 100644 src/tools/fs_shell/fd.cpp create mode 100644 src/tools/fs_shell/fd.h create mode 100644 src/tools/fs_shell/file_cache.cpp create mode 100644 src/tools/fs_shell/fssh.cpp create mode 100644 src/tools/fs_shell/hash.cpp create mode 100644 src/tools/fs_shell/hash.h create mode 100644 src/tools/fs_shell/kernel_export.cpp create mode 100644 src/tools/fs_shell/list.cpp create mode 100644 src/tools/fs_shell/list.h create mode 100644 src/tools/fs_shell/lock.cpp create mode 100644 src/tools/fs_shell/lock.h create mode 100644 src/tools/fs_shell/node_monitor.cpp create mode 100644 src/tools/fs_shell/sem.cpp create mode 100644 src/tools/fs_shell/sem_unix.cpp create mode 100644 src/tools/fs_shell/stat.cpp create mode 100644 src/tools/fs_shell/stat_util.cpp create mode 100644 src/tools/fs_shell/stat_util.h create mode 100644 src/tools/fs_shell/stdio.cpp create mode 100644 src/tools/fs_shell/string.cpp create mode 100644 src/tools/fs_shell/thread.cpp create mode 100644 src/tools/fs_shell/time.cpp create mode 100644 src/tools/fs_shell/uio.cpp create mode 100644 src/tools/fs_shell/unistd.cpp create mode 100644 src/tools/fs_shell/vfs.cpp create mode 100644 src/tools/fs_shell/vfs.h diff --git a/headers/private/fs_shell/DoublyLinkedList.h b/headers/private/fs_shell/DoublyLinkedList.h new file mode 100644 index 0000000000..ee6ebdea18 --- /dev/null +++ b/headers/private/fs_shell/DoublyLinkedList.h @@ -0,0 +1,584 @@ +/* + * Copyright 2005-2006, Ingo Weinhold, bonefish@users.sf.net. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef KERNEL_UTIL_DOUBLY_LINKED_LIST_H +#define KERNEL_UTIL_DOUBLY_LINKED_LIST_H + + +#include "fssh_types.h" + + +#ifdef __cplusplus + + +namespace FSShell { + + +// DoublyLinkedListLink +template +class DoublyLinkedListLink { +public: + DoublyLinkedListLink() : previous(NULL), next(NULL) {} + ~DoublyLinkedListLink() {} + + Element *previous; + Element *next; +}; + +// DoublyLinkedListLinkImpl +template +class DoublyLinkedListLinkImpl { +private: + typedef DoublyLinkedListLink DLL_Link; + +public: + DoublyLinkedListLinkImpl() : fDoublyLinkedListLink() {} + ~DoublyLinkedListLinkImpl() {} + + DLL_Link *GetDoublyLinkedListLink() + { return &fDoublyLinkedListLink; } + const DLL_Link *GetDoublyLinkedListLink() const + { return &fDoublyLinkedListLink; } + +private: + DLL_Link fDoublyLinkedListLink; +}; + +// DoublyLinkedListStandardGetLink +template +class DoublyLinkedListStandardGetLink { +private: + typedef DoublyLinkedListLink Link; + +public: + inline Link *operator()(Element *element) const + { + return element->GetDoublyLinkedListLink(); + } + + inline const Link *operator()(const Element *element) const + { + return element->GetDoublyLinkedListLink(); + } +}; + +// DoublyLinkedListMemberGetLink +template Element::* LinkMember = &Element::fLink> +class DoublyLinkedListMemberGetLink { +private: + typedef DoublyLinkedListLink Link; + +public: + inline Link *operator()(Element *element) const + { + return &(element->*LinkMember); + } + + inline const Link *operator()(const Element *element) const + { + return &(element->*LinkMember); + } +}; + +// DoublyLinkedListCLink - interface to struct list +template +class DoublyLinkedListCLink { + private: + typedef DoublyLinkedListLink Link; + + public: + inline Link *operator()(Element *element) const + { + return (Link *)&element->link; + } + + inline const Link *operator()(const Element *element) const + { + return (const Link *)&element->link; + } +}; + +// for convenience +#define DOUBLY_LINKED_LIST_TEMPLATE_LIST \ + template +#define DOUBLY_LINKED_LIST_CLASS_NAME DoublyLinkedList + +// DoublyLinkedList +template > +class DoublyLinkedList { +private: + typedef DoublyLinkedList List; + typedef DoublyLinkedListLink Link; + +public: + class Iterator { + public: + Iterator(List *list) + : + fList(list) + { + Rewind(); + } + + Iterator(const Iterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + fCurrent = fNext; + if (fNext) + fNext = fList->GetNext(fNext); + return fCurrent; + } + + Element *Remove() + { + Element *element = fCurrent; + if (fCurrent) { + fList->Remove(fCurrent); + fCurrent = NULL; + } + return element; + } + + Iterator &operator=(const Iterator &other) + { + fList = other.fList; + fCurrent = other.fCurrent; + fNext = other.fNext; + return *this; + } + + void Rewind() + { + fCurrent = NULL; + fNext = fList->First(); + } + + private: + List *fList; + Element *fCurrent; + Element *fNext; + }; + + class ConstIterator { + public: + ConstIterator(const List *list) + : + fList(list) + { + Rewind(); + } + + ConstIterator(const ConstIterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + Element *element = fNext; + if (fNext) + fNext = fList->GetNext(fNext); + return element; + } + + ConstIterator &operator=(const ConstIterator &other) + { + fList = other.fList; + fNext = other.fNext; + return *this; + } + + void Rewind() + { + fNext = fList->First(); + } + + private: + const List *fList; + Element *fNext; + }; + + class ReverseIterator { + public: + ReverseIterator(List *list) + : + fList(list) + { + Rewind(); + } + + ReverseIterator(const ReverseIterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + fCurrent = fNext; + if (fNext) + fNext = fList->GetPrevious(fNext); + return fCurrent; + } + + Element *Remove() + { + Element *element = fCurrent; + if (fCurrent) { + fList->Remove(fCurrent); + fCurrent = NULL; + } + return element; + } + + ReverseIterator &operator=(const ReverseIterator &other) + { + fList = other.fList; + fCurrent = other.fCurrent; + fNext = other.fNext; + return *this; + } + + void Rewind() + { + fCurrent = NULL; + fNext = fList->Last(); + } + + private: + List *fList; + Element *fCurrent; + Element *fNext; + }; + + class ConstReverseIterator { + public: + ConstReverseIterator(const List *list) + : + fList(list) + { + Rewind(); + } + + ConstReverseIterator(const ConstReverseIterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + Element *element = fNext; + if (fNext) + fNext = fList->GetPrevious(fNext); + return element; + } + + ConstReverseIterator &operator=(const ConstReverseIterator &other) + { + fList = other.fList; + fNext = other.fNext; + return *this; + } + + void Rewind() + { + fNext = fList->Last(); + } + + private: + const List *fList; + Element *fNext; + }; + +public: + DoublyLinkedList() : fFirst(NULL), fLast(NULL) {} + ~DoublyLinkedList() {} + + inline bool IsEmpty() const { return (fFirst == NULL); } + + inline void Insert(Element *element, bool back = true); + inline void Insert(Element *before, Element *element); + inline void Add(Element *element, bool back = true); + inline void Remove(Element *element); + + inline void Swap(Element *a, Element *b); + + inline void MoveFrom(DOUBLY_LINKED_LIST_CLASS_NAME *fromList); + + inline void RemoveAll(); + inline void MakeEmpty() { RemoveAll(); } + + inline Element *First() const { return fFirst; } + inline Element *Last() const { return fLast; } + + inline Element *Head() const { return fFirst; } + inline Element *Tail() const { return fLast; } + + inline Element *RemoveHead(); + + inline Element *GetPrevious(Element *element) const; + inline Element *GetNext(Element *element) const; + + inline int32_t Size() const; + // O(n)! + + inline Iterator GetIterator() { return Iterator(this); } + inline ConstIterator GetIterator() const { return ConstIterator(this); } + + inline ReverseIterator GetReverseIterator() + { return ReverseIterator(this); } + inline ConstReverseIterator GetReverseIterator() const + { return ConstReverseIterator(this); } + +private: + Element *fFirst; + Element *fLast; + + static GetLink sGetLink; +}; + + +// inline methods + +// Insert +DOUBLY_LINKED_LIST_TEMPLATE_LIST +void +DOUBLY_LINKED_LIST_CLASS_NAME::Insert(Element *element, bool back) +{ + if (element) { + if (back) { + // append + Link *elLink = sGetLink(element); + elLink->previous = fLast; + elLink->next = NULL; + if (fLast) + sGetLink(fLast)->next = element; + else + fFirst = element; + fLast = element; + } else { + // prepend + Link *elLink = sGetLink(element); + elLink->previous = NULL; + elLink->next = fFirst; + if (fFirst) + sGetLink(fFirst)->previous = element; + else + fLast = element; + fFirst = element; + } + } +} + +// Insert +DOUBLY_LINKED_LIST_TEMPLATE_LIST +void +DOUBLY_LINKED_LIST_CLASS_NAME::Insert(Element *before, Element *element) +{ + if (before == NULL) { + Insert(element); + return; + } + if (element == NULL) + return; + + Link *beforeLink = sGetLink(before); + Link *link = sGetLink(element); + + link->next = before; + link->previous = beforeLink->previous; + if (link->previous != NULL) + sGetLink(link->previous)->next = element; + beforeLink->previous = element; + + if (fFirst == before) + fFirst = element; +} + +// Add +DOUBLY_LINKED_LIST_TEMPLATE_LIST +void +DOUBLY_LINKED_LIST_CLASS_NAME::Add(Element *element, bool back) +{ + Insert(element, back); +} + +// Remove +DOUBLY_LINKED_LIST_TEMPLATE_LIST +void +DOUBLY_LINKED_LIST_CLASS_NAME::Remove(Element *element) +{ + if (element) { + Link *elLink = sGetLink(element); + if (elLink->previous) + sGetLink(elLink->previous)->next = elLink->next; + else + fFirst = elLink->next; + if (elLink->next) + sGetLink(elLink->next)->previous = elLink->previous; + else + fLast = elLink->previous; + elLink->previous = NULL; + elLink->next = NULL; + } +} + +// Swap +DOUBLY_LINKED_LIST_TEMPLATE_LIST +void +DOUBLY_LINKED_LIST_CLASS_NAME::Swap(Element *a, Element *b) +{ + if (a && b && a != b) { + Link *aLink = sGetLink(a); + Link *bLink = sGetLink(b); + Element *aPrev = aLink->previous; + Element *bPrev = bLink->previous; + Element *aNext = aLink->next; + Element *bNext = bLink->next; + // place a + if (bPrev) + sGetLink(bPrev)->next = a; + else + fFirst = a; + if (bNext) + sGetLink(bNext)->previous = a; + else + fLast = a; + aLink->previous = bPrev; + aLink->next = bNext; + // place b + if (aPrev) + sGetLink(aPrev)->next = b; + else + fFirst = b; + if (aNext) + sGetLink(aNext)->previous = b; + else + fLast = b; + bLink->previous = aPrev; + bLink->next = aNext; + } +} + +// MoveFrom +DOUBLY_LINKED_LIST_TEMPLATE_LIST +void +DOUBLY_LINKED_LIST_CLASS_NAME::MoveFrom(DOUBLY_LINKED_LIST_CLASS_NAME *fromList) +{ + if (fromList && fromList->fFirst) { + if (fFirst) { + sGetLink(fLast)->next = fromList->fFirst; + sGetLink(fFirst)->previous = fLast; + fLast = fromList->fLast; + } else { + fFirst = fromList->fFirst; + fLast = fromList->fLast; + } + fromList->fFirst = NULL; + fromList->fLast = NULL; + } +} + +// RemoveAll +DOUBLY_LINKED_LIST_TEMPLATE_LIST +void +DOUBLY_LINKED_LIST_CLASS_NAME::RemoveAll() +{ + Element *element = fFirst; + while (element) { + Link *elLink = sGetLink(element); + element = elLink->next; + elLink->previous = NULL; + elLink->next = NULL; + } + fFirst = NULL; + fLast = NULL; +} + +// RemoveHead +DOUBLY_LINKED_LIST_TEMPLATE_LIST +Element * +DOUBLY_LINKED_LIST_CLASS_NAME::RemoveHead() +{ + Element *element = Head(); + Remove(element); + return element; +} + +// GetPrevious +DOUBLY_LINKED_LIST_TEMPLATE_LIST +Element * +DOUBLY_LINKED_LIST_CLASS_NAME::GetPrevious(Element *element) const +{ + Element *result = NULL; + if (element) + result = sGetLink(element)->previous; + return result; +} + +// GetNext +DOUBLY_LINKED_LIST_TEMPLATE_LIST +Element * +DOUBLY_LINKED_LIST_CLASS_NAME::GetNext(Element *element) const +{ + Element *result = NULL; + if (element) + result = sGetLink(element)->next; + return result; +} + +// Size +DOUBLY_LINKED_LIST_TEMPLATE_LIST +int32_t +DOUBLY_LINKED_LIST_CLASS_NAME::Size() const +{ + int32_t count = 0; + for (Element* element = First(); element; element = GetNext(element)) + count++; + return count; +} + +// sGetLink +DOUBLY_LINKED_LIST_TEMPLATE_LIST +GetLink DOUBLY_LINKED_LIST_CLASS_NAME::sGetLink; + + +} // namespace FSShell + +using FSShell::DoublyLinkedListLink; +using FSShell::DoublyLinkedListLinkImpl; +using FSShell::DoublyLinkedListStandardGetLink; +using FSShell::DoublyLinkedListMemberGetLink; +using FSShell::DoublyLinkedListCLink; +using FSShell::DoublyLinkedList; + + +#endif /* __cplusplus */ + +#endif // _KERNEL_UTIL_DOUBLY_LINKED_LIST_H diff --git a/headers/private/fs_shell/KPath.h b/headers/private/fs_shell/KPath.h new file mode 100644 index 0000000000..b18d058641 --- /dev/null +++ b/headers/private/fs_shell/KPath.h @@ -0,0 +1,62 @@ +/* + * Copyright 2004-2007, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ +#ifndef _K_PATH_H +#define _K_PATH_H + + +#include "fssh_defs.h" +#include "fssh_kernel_export.h" + + +namespace FSShell { + +class KPath { + public: + KPath(fssh_size_t bufferSize = FSSH_B_PATH_NAME_LENGTH); + KPath(const char* path, bool normalize = false, + fssh_size_t bufferSize = FSSH_B_PATH_NAME_LENGTH); + KPath(const KPath& other); + ~KPath(); + + fssh_status_t SetTo(const char *path, bool normalize = false, + fssh_size_t bufferSize = FSSH_B_PATH_NAME_LENGTH); + + fssh_status_t InitCheck() const; + + fssh_status_t SetPath(const char *path, bool normalize = false); + const char *Path() const; + fssh_size_t Length() const { return fPathLength; } + + fssh_size_t BufferSize() const { return fBufferSize; } + char *LockBuffer(); + void UnlockBuffer(); + + const char *Leaf() const; + fssh_status_t ReplaceLeaf(const char *newLeaf); + + fssh_status_t Append(const char *toAppend, bool isComponent = true); + + KPath& operator=(const KPath& other); + KPath& operator=(const char* path); + + bool operator==(const KPath& other) const; + bool operator==(const char* path) const; + bool operator!=(const KPath& other) const; + bool operator!=(const char* path) const; + + private: + void _ChopTrailingSlashes(); + + char* fBuffer; + fssh_size_t fBufferSize; + fssh_size_t fPathLength; + bool fLocked; +}; + +} // namespace FSShell + +using FSShell::KPath; + +#endif /* _K_PATH_H */ diff --git a/headers/private/fs_shell/Stack.h b/headers/private/fs_shell/Stack.h new file mode 100644 index 0000000000..63b91d83c1 --- /dev/null +++ b/headers/private/fs_shell/Stack.h @@ -0,0 +1,87 @@ +/* Stack - a template stack class (plus some handy methods) + * + * Copyright 2001-2005, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ +#ifndef _FSSH_STACK_H +#define _FSSH_STACK_H + + +#include "fssh_defs.h" +#include "fssh_errors.h" + + +namespace FSShell { + + +template class Stack { + public: + Stack() + : + fArray(NULL), + fUsed(0), + fMax(0) + { + } + + ~Stack() + { + free(fArray); + } + + bool IsEmpty() const + { + return fUsed == 0; + } + + void MakeEmpty() + { + // could also free the memory + fUsed = 0; + } + + fssh_status_t Push(T value) + { + if (fUsed >= fMax) { + fMax += 16; + T *newArray = (T *)realloc(fArray, fMax * sizeof(T)); + if (newArray == NULL) + return FSSH_B_NO_MEMORY; + + fArray = newArray; + } + fArray[fUsed++] = value; + return FSSH_B_OK; + } + + bool Pop(T *value) + { + if (fUsed == 0) + return false; + + *value = fArray[--fUsed]; + return true; + } + + T *Array() + { + return fArray; + } + + int32_t CountItems() const + { + return fUsed; + } + + private: + T *fArray; + int32_t fUsed; + int32_t fMax; +}; + +} // namespace FSShell + +using FSShell::Stack; + + +#endif /* _FSSH_STACK_H */ diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h new file mode 100644 index 0000000000..93b2aca864 --- /dev/null +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -0,0 +1,1477 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#ifndef _FSSH_API_WRAPPER_H +#define _FSSH_API_WRAPPER_H + +#include + +#include "fssh_dirent.h" +#include "fssh_errno.h" +#include "fssh_fcntl.h" +#include "fssh_stat.h" +#include "fssh_stdio.h" +#include "fssh_string.h" +#include "fssh_time.h" +#include "fssh_uio.h" +#include "fssh_unistd.h" + +#include "fssh_atomic.h" +#include "fssh_byte_order.h" +#include "fssh_defs.h" +#include "fssh_disk_device_defs.h" +#include "fssh_disk_device_manager.h" +#include "fssh_drivers.h" +#include "fssh_errors.h" +#include "fssh_fs_attr.h" +#include "fssh_fs_cache.h" +#include "fssh_fs_index.h" +#include "fssh_fs_info.h" +#include "fssh_fs_interface.h" +#include "fssh_fs_query.h" +#include "fssh_fs_volume.h" +#include "fssh_kernel_export.h" +#include "fssh_module.h" +#include "fssh_node_monitor.h" +#include "fssh_os.h" +#include "fssh_type_constants.h" +#include "fssh_types.h" + +#include "DoublyLinkedList.h" +#include "Stack.h" + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_atomic.h + +#define atomic_set fssh_atomic_set +#define atomic_test_and_set fssh_atomic_test_and_set +#define atomic_add fssh_atomic_add +#define atomic_and fssh_atomic_and +#define atomic_or fssh_atomic_or +#define atomic_get fssh_atomic_get + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_bytes_order.h + +// swap directions +#define B_SWAP_HOST_TO_LENDIAN FSSH_B_SWAP_HOST_TO_LENDIAN +#define B_SWAP_HOST_TO_BENDIAN FSSH_B_SWAP_HOST_TO_BENDIAN +#define B_SWAP_LENDIAN_TO_HOST FSSH_B_SWAP_LENDIAN_TO_HOST +#define B_SWAP_BENDIAN_TO_HOST FSSH_B_SWAP_BENDIAN_TO_HOST +#define B_SWAP_ALWAYS FSSH_B_SWAP_ALWAYS + +#define swap_action fssh_swap_action + +// BSD/networking macros +#define htonl(x) fssh_htonl(x) +#define ntohl(x) fssh_ntohl(x) +#define htons(x) fssh_htons(x) +#define ntohs(x) fssh_ntohs(x) + +// always swap macros +#define B_SWAP_DOUBLE(arg) FSSH_B_SWAP_DOUBLE(arg) +#define B_SWAP_FLOAT(arg) FSSH_B_SWAP_FLOAT(arg) +#define B_SWAP_INT64(arg) FSSH_B_SWAP_INT64(arg) +#define B_SWAP_INT32(arg) FSSH_B_SWAP_INT32(arg) +#define B_SWAP_INT16(arg) FSSH_B_SWAP_INT16(arg) + +#define B_HOST_IS_LENDIAN FSSH_B_HOST_IS_LENDIAN +#define B_HOST_IS_BENDIAN FSSH_B_HOST_IS_BENDIAN + +// Host native to little endian +#define B_HOST_TO_LENDIAN_DOUBLE(arg) FSSH_B_HOST_TO_LENDIAN_DOUBLE(arg) +#define B_HOST_TO_LENDIAN_FLOAT(arg) FSSH_B_HOST_TO_LENDIAN_FLOAT(arg) +#define B_HOST_TO_LENDIAN_INT64(arg) FSSH_B_HOST_TO_LENDIAN_INT64(arg) +#define B_HOST_TO_LENDIAN_INT32(arg) FSSH_B_HOST_TO_LENDIAN_INT32(arg) +#define B_HOST_TO_LENDIAN_INT16(arg) FSSH_B_HOST_TO_LENDIAN_INT16(arg) + +// Little endian to host native +#define B_LENDIAN_TO_HOST_DOUBLE(arg) FSSH_B_LENDIAN_TO_HOST_DOUBLE(arg) +#define B_LENDIAN_TO_HOST_FLOAT(arg) FSSH_B_LENDIAN_TO_HOST_FLOAT(arg) +#define B_LENDIAN_TO_HOST_INT64(arg) FSSH_B_LENDIAN_TO_HOST_INT64(arg) +#define B_LENDIAN_TO_HOST_INT32(arg) FSSH_B_LENDIAN_TO_HOST_INT32(arg) +#define B_LENDIAN_TO_HOST_INT16(arg) FSSH_B_LENDIAN_TO_HOST_INT16(arg) + +// Host native to big endian +#define B_HOST_TO_BENDIAN_DOUBLE(arg) FSSH_B_HOST_TO_BENDIAN_DOUBLE(arg) +#define B_HOST_TO_BENDIAN_FLOAT(arg) FSSH_B_HOST_TO_BENDIAN_FLOAT(arg) +#define B_HOST_TO_BENDIAN_INT64(arg) FSSH_B_HOST_TO_BENDIAN_INT64(arg) +#define B_HOST_TO_BENDIAN_INT32(arg) FSSH_B_HOST_TO_BENDIAN_INT32(arg) +#define B_HOST_TO_BENDIAN_INT16(arg) FSSH_B_HOST_TO_BENDIAN_INT16(arg) + +// Big endian to host native +#define B_BENDIAN_TO_HOST_DOUBLE(arg) FSSH_B_BENDIAN_TO_HOST_DOUBLE(arg) +#define B_BENDIAN_TO_HOST_FLOAT(arg) FSSH_B_BENDIAN_TO_HOST_FLOAT(arg) +#define B_BENDIAN_TO_HOST_INT64(arg) FSSH_B_BENDIAN_TO_HOST_INT64(arg) +#define B_BENDIAN_TO_HOST_INT32(arg) FSSH_B_BENDIAN_TO_HOST_INT32(arg) +#define B_BENDIAN_TO_HOST_INT16(arg) FSSH_B_BENDIAN_TO_HOST_INT16(arg) + +#define swap_data fssh_swap_data +#define is_type_swapped + + +// Private implementations +#define __fssh_swap_double +#define __fssh_swap_float +#define __fssh_swap_int64 +#define __fssh_swap_int32 +#define __fssh_swap_int16 + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_defs.h + +// Limits +#define B_DEV_NAME_LENGTH FSSH_B_DEV_NAME_LENGTH +#define B_FILE_NAME_LENGTH FSSH_B_FILE_NAME_LENGTH +#define B_PATH_NAME_LENGTH FSSH_B_PATH_NAME_LENGTH +#define B_ATTR_NAME_LENGTH FSSH_B_ATTR_NAME_LENGTH +#define B_MIME_TYPE_LENGTH FSSH_B_MIME_TYPE_LENGTH +#define B_MAX_SYMLINKS FSSH_B_MAX_SYMLINKS + +// Open Modes +#define B_READ_ONLY FSSH_B_READ_ONLY +#define B_WRITE_ONLY FSSH_B_WRITE_ONLY +#define B_READ_WRITE FSSH_B_READ_WRITE + +#define B_FAIL_IF_EXISTS FSSH_B_FAIL_IF_EXISTS +#define B_CREATE_FILE FSSH_B_CREATE_FILE +#define B_ERASE_FILE FSSH_B_ERASE_FILE +#define B_OPEN_AT_END FSSH_B_OPEN_AT_END + +// Node Flavors +#define node_flavor fssh_node_flavor +#define B_FILE_NODE FSSH_B_FILE_NODE +#define B_SYMLINK_NODE FSSH_B_SYMLINK_NODE +#define B_DIRECTORY_NODE FSSH_B_DIRECTORY_NODE +#define B_ANY_NODE FSSH_B_ANY_NODE + +#define offsetof(type,member) fssh_offsetof(type,member) + +#define min_c(a,b) fssh_min_c(a,b) +#define max_c(a,b) fssh_max_c(a,b) + +#define _PACKED _FSSH_PACKED + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_dirent.h + +#define dirent fssh_dirent +#define dirent_t fssh_dirent_t + +#define DIR fssh_DIR + + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_disk_device_defs.h + +#define partition_id fssh_partition_id +#define disk_system_id fssh_disk_system_id +#define disk_job_id fssh_disk_job_id + +// partition flags +#define B_PARTITION_IS_DEVICE FSSH_B_PARTITION_IS_DEVICE +#define B_PARTITION_FILE_SYSTEM FSSH_B_PARTITION_FILE_SYSTEM +#define B_PARTITION_PARTITIONING_SYSTEM FSSH_B_PARTITION_PARTITIONING_SYSTEM +#define B_PARTITION_READ_ONLY FSSH_B_PARTITION_READ_ONLY +#define B_PARTITION_MOUNTED FSSH_B_PARTITION_MOUNTED +#define B_PARTITION_BUSY FSSH_B_PARTITION_BUSY +#define B_PARTITION_DESCENDANT_BUSY FSSH_B_PARTITION_DESCENDANT_BUSY + +// partition statuses +#define B_PARTITION_VALID FSSH_B_PARTITION_VALID +#define B_PARTITION_CORRUPT FSSH_B_PARTITION_CORRUPT +#define B_PARTITION_UNRECOGNIZED FSSH_B_PARTITION_UNRECOGNIZED +#define B_PARTITION_UNINITIALIZED FSSH_B_PARTITION_UNINITIALIZED + +// partition change flags +#define B_PARTITION_CHANGED_OFFSET FSSH_B_PARTITION_CHANGED_OFFSET +#define B_PARTITION_CHANGED_SIZE FSSH_B_PARTITION_CHANGED_SIZE +#define B_PARTITION_CHANGED_CONTENT_SIZE FSSH_B_PARTITION_CHANGED_CONTENT_SIZE +#define B_PARTITION_CHANGED_BLOCK_SIZE FSSH_B_PARTITION_CHANGED_BLOCK_SIZE +#define B_PARTITION_CHANGED_STATUS FSSH_B_PARTITION_CHANGED_STATUS +#define B_PARTITION_CHANGED_FLAGS FSSH_B_PARTITION_CHANGED_FLAGS +#define B_PARTITION_CHANGED_VOLUME FSSH_B_PARTITION_CHANGED_VOLUME +#define B_PARTITION_CHANGED_NAME FSSH_B_PARTITION_CHANGED_NAME +#define B_PARTITION_CHANGED_CONTENT_NAME FSSH_B_PARTITION_CHANGED_CONTENT_NAME +#define B_PARTITION_CHANGED_TYPE FSSH_B_PARTITION_CHANGED_TYPE +#define B_PARTITION_CHANGED_CONTENT_TYPE FSSH_B_PARTITION_CHANGED_CONTENT_TYPE +#define B_PARTITION_CHANGED_PARAMETERS FSSH_B_PARTITION_CHANGED_PARAMETERS +#define B_PARTITION_CHANGED_CONTENT_PARAMETERS FSSH_B_PARTITION_CHANGED_CONTENT_PARAMETERS +#define B_PARTITION_CHANGED_CHILDREN FSSH_B_PARTITION_CHANGED_CHILDREN +#define B_PARTITION_CHANGED_DESCENDANTS FSSH_B_PARTITION_CHANGED_DESCENDANTS +#define B_PARTITION_CHANGED_DEFRAGMENTATION FSSH_B_PARTITION_CHANGED_DEFRAGMENTATION +#define B_PARTITION_CHANGED_CHECK FSSH_B_PARTITION_CHANGED_CHECK +#define B_PARTITION_CHANGED_REPAIR FSSH_B_PARTITION_CHANGED_REPAIR +#define B_PARTITION_CHANGED_INITIALIZATION FSSH_B_PARTITION_CHANGED_INITIALIZATION + +// disk device flags +#define B_DISK_DEVICE_REMOVABLE FSSH_B_DISK_DEVICE_REMOVABLE +#define B_DISK_DEVICE_HAS_MEDIA FSSH_B_DISK_DEVICE_HAS_MEDIA +#define B_DISK_DEVICE_READ_ONLY FSSH_B_DISK_DEVICE_READ_ONLY +#define B_DISK_DEVICE_WRITE_ONCE FSSH_B_DISK_DEVICE_WRITE_ONCE + +// disk system flags +#define B_DISK_SYSTEM_IS_FILE_SYSTEM FSSH_B_DISK_SYSTEM_IS_FILE_SYSTEM + +// flags common for both file and partitioning systems +#define B_DISK_SYSTEM_SUPPORTS_CHECKING FSSH_B_DISK_SYSTEM_SUPPORTS_CHECKING +#define B_DISK_SYSTEM_SUPPORTS_REPAIRING FSSH_B_DISK_SYSTEM_SUPPORTS_REPAIRING +#define B_DISK_SYSTEM_SUPPORTS_RESIZING FSSH_B_DISK_SYSTEM_SUPPORTS_RESIZING +#define B_DISK_SYSTEM_SUPPORTS_MOVING FSSH_B_DISK_SYSTEM_SUPPORTS_MOVING +#define B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME +#define B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS + +// file system specific flags +#define B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING FSSH_B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING +#define B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED FSSH_B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED +#define B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED FSSH_B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED +#define B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED FSSH_B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED +#define B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED FSSH_B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED +#define B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED FSSH_B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED +#define B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED +#define B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED + +// partitioning system specific flags +#define B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD FSSH_B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD +#define B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD FSSH_B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD +#define B_DISK_SYSTEM_SUPPORTS_SETTING_NAME FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_NAME +#define B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE +#define B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS +#define B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD FSSH_B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD +#define B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD FSSH_B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD +#define B_DISK_SYSTEM_SUPPORTS_INITIALIZING FSSH_B_DISK_SYSTEM_SUPPORTS_INITIALIZING + +// disk device job types +#define B_DISK_DEVICE_JOB_BAD_TYPE FSSH_B_DISK_DEVICE_JOB_BAD_TYPE +#define B_DISK_DEVICE_JOB_DEFRAGMENT FSSH_B_DISK_DEVICE_JOB_DEFRAGMENT +#define B_DISK_DEVICE_JOB_REPAIR FSSH_B_DISK_DEVICE_JOB_REPAIR +#define B_DISK_DEVICE_JOB_RESIZE FSSH_B_DISK_DEVICE_JOB_RESIZE +#define B_DISK_DEVICE_JOB_MOVE FSSH_B_DISK_DEVICE_JOB_MOVE +#define B_DISK_DEVICE_JOB_SET_NAME FSSH_B_DISK_DEVICE_JOB_SET_NAME +#define B_DISK_DEVICE_JOB_SET_CONTENT_NAME FSSH_B_DISK_DEVICE_JOB_SET_CONTENT_NAME +#define B_DISK_DEVICE_JOB_SET_TYPE FSSH_B_DISK_DEVICE_JOB_SET_TYPE +#define B_DISK_DEVICE_JOB_SET_PARMETERS FSSH_B_DISK_DEVICE_JOB_SET_PARMETERS +#define B_DISK_DEVICE_JOB_SET_CONTENT_PARMETERS FSSH_B_DISK_DEVICE_JOB_SET_CONTENT_PARMETERS +#define B_DISK_DEVICE_JOB_INITIALIZE FSSH_B_DISK_DEVICE_JOB_INITIALIZE +#define B_DISK_DEVICE_JOB_UNINITIALIZE FSSH_B_DISK_DEVICE_JOB_UNINITIALIZE +#define B_DISK_DEVICE_JOB_CREATE FSSH_B_DISK_DEVICE_JOB_CREATE +#define B_DISK_DEVICE_JOB_DELETE FSSH_B_DISK_DEVICE_JOB_DELETE +#define B_DISK_DEVICE_JOB_SCAN FSSH_B_DISK_DEVICE_JOB_SCAN + +// disk device job statuses +#define B_DISK_DEVICE_JOB_UNINITIALIZED FSSH_B_DISK_DEVICE_JOB_UNINITIALIZED +#define B_DISK_DEVICE_JOB_SCHEDULED FSSH_B_DISK_DEVICE_JOB_SCHEDULED +#define B_DISK_DEVICE_JOB_IN_PROGRESS FSSH_B_DISK_DEVICE_JOB_IN_PROGRESS +#define B_DISK_DEVICE_JOB_SUCCEEDED FSSH_B_DISK_DEVICE_JOB_SUCCEEDED +#define B_DISK_DEVICE_JOB_FAILED FSSH_B_DISK_DEVICE_JOB_FAILED +#define B_DISK_DEVICE_JOB_CANCELED FSSH_B_DISK_DEVICE_JOB_CANCELED + +#define disk_device_job_progress_info fssh_disk_device_job_progress_info + +// disk device job interrupt properties +#define B_DISK_DEVICE_JOB_CAN_CANCEL FSSH_B_DISK_DEVICE_JOB_CAN_CANCEL +#define B_DISK_DEVICE_JOB_STOP_ON_CANCEL FSSH_B_DISK_DEVICE_JOB_STOP_ON_CANCEL +#define B_DISK_DEVICE_JOB_REVERSE_ON_CANCEL FSSH_B_DISK_DEVICE_JOB_REVERSE_ON_CANCEL +#define B_DISK_DEVICE_JOB_CAN_PAUSE FSSH_B_DISK_DEVICE_JOB_CAN_PAUSE + +// string length constants, all of which include the NULL terminator +#define B_DISK_DEVICE_TYPE_LENGTH FSSH_B_DISK_DEVICE_TYPE_LENGTH +#define B_DISK_DEVICE_NAME_LENGTH FSSH_B_DISK_DEVICE_NAME_LENGTH +#define B_DISK_SYSTEM_NAME_LENGTH FSSH_B_DISK_SYSTEM_NAME_LENGTH + +// max size of parameter string buffers, including NULL terminator +#define B_DISK_DEVICE_MAX_PARAMETER_SIZE FSSH_B_DISK_DEVICE_MAX_PARAMETER_SIZE + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_disk_device_manager.h + +#define partition_data fssh_partition_data +#define disk_device_data fssh_disk_device_data +#define partitionable_space_data fssh_partitionable_space_data + +// operations on partitions +#define B_PARTITION_DEFRAGMENT FSSH_B_PARTITION_DEFRAGMENT +#define B_PARTITION_REPAIR FSSH_B_PARTITION_REPAIR +#define B_PARTITION_RESIZE FSSH_B_PARTITION_RESIZE +#define B_PARTITION_RESIZE_CHILD FSSH_B_PARTITION_RESIZE_CHILD +#define B_PARTITION_MOVE FSSH_B_PARTITION_MOVE +#define B_PARTITION_MOVE_CHILD FSSH_B_PARTITION_MOVE_CHILD +#define B_PARTITION_SET_NAME FSSH_B_PARTITION_SET_NAME +#define B_PARTITION_SET_CONTENT_NAME FSSH_B_PARTITION_SET_CONTENT_NAME +#define B_PARTITION_SET_TYPE FSSH_B_PARTITION_SET_TYPE +#define B_PARTITION_SET_PARAMETERS FSSH_B_PARTITION_SET_PARAMETERS +#define B_PARTITION_SET_CONTENT_PARAMETERS FSSH_B_PARTITION_SET_CONTENT_PARAMETERS +#define B_PARTITION_INITIALIZE FSSH_B_PARTITION_INITIALIZE +#define B_PARTITION_CREATE_CHILD FSSH_B_PARTITION_CREATE_CHILD +#define B_PARTITION_DELETE_CHILD FSSH_B_PARTITION_DELETE_CHILD + +// disk device job cancel status +#define B_DISK_DEVICE_JOB_CONTINUE FSSH_B_DISK_DEVICE_JOB_CONTINUE +#define B_DISK_DEVICE_JOB_CANCEL FSSH_B_DISK_DEVICE_JOB_CANCEL +#define B_DISK_DEVICE_JOB_REVERSE FSSH_B_DISK_DEVICE_JOB_REVERSE + +// disk device locking +#define write_lock_disk_device fssh_write_lock_disk_device +#define write_unlock_disk_device fssh_write_unlock_disk_device +#define read_lock_disk_device fssh_read_lock_disk_device +#define read_unlock_disk_device fssh_read_unlock_disk_device + +// getting disk devices/partitions by path +#define find_disk_device fssh_find_disk_device +#define find_partition fssh_find_partition + +// disk device/partition read access +#define get_disk_device fssh_get_disk_device +#define get_partition fssh_get_partition +#define get_parent_partition fssh_get_parent_partition +#define get_child_partition fssh_get_child_partition + +// partition write access +#define create_child_partition fssh_create_child_partition +#define delete_partition fssh_delete_partition +#define partition_modified fssh_partition_modified + +// disk systems +#define find_disk_system fssh_find_disk_system + +// jobs +#define update_disk_device_job_progress fssh_update_disk_device_job_progress +#define update_disk_device_job_extra_progress fssh_update_disk_device_job_extra_progress +#define set_disk_device_job_error_message fssh_set_disk_device_job_error_message +#define update_disk_device_job_interrupt_properties fssh_update_disk_device_job_interrupt_properties + + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_drivers.h + +#define device_open_hook fssh_device_open_hook +#define device_close_hook fssh_device_close_hook +#define device_free_hook fssh_device_free_hook +#define device_control_hook fssh_device_control_hook +#define device_read_hook fssh_device_read_hook +#define device_write_hook fssh_device_write_hook +#define device_select_hook fssh_device_select_hook +#define device_deselect_hook fssh_device_deselect_hook +#define device_read_pages_hook fssh_device_read_pages_hook +#define device_write_pages_hook fssh_device_write_pages_hook + +#define B_CUR_DRIVER_API_VERSION FSSH_B_CUR_DRIVER_API_VERSION + +#define device_hooks fssh_device_hooks + +#define init_hardware fssh_init_hardware +#define publish_devices fssh_publish_devices +#define find_device fssh_find_device +#define init_driver fssh_init_driver +#define uninit_driver fssh_uninit_driver + +#define api_version fssh_api_version + +#define B_GET_DEVICE_SIZE FSSH_B_GET_DEVICE_SIZE +#define B_SET_DEVICE_SIZE FSSH_B_SET_DEVICE_SIZE +#define B_SET_NONBLOCKING_IO FSSH_B_SET_NONBLOCKING_IO +#define B_SET_BLOCKING_IO FSSH_B_SET_BLOCKING_IO +#define B_GET_READ_STATUS FSSH_B_GET_READ_STATUS +#define B_GET_WRITE_STATUS FSSH_B_GET_WRITE_STATUS +#define B_GET_GEOMETRY FSSH_B_GET_GEOMETRY +#define B_GET_DRIVER_FOR_DEVICE FSSH_B_GET_DRIVER_FOR_DEVICE +#define B_GET_PARTITION_INFO FSSH_B_GET_PARTITION_INFO +#define B_SET_PARTITION FSSH_B_SET_PARTITION +#define B_FORMAT_DEVICE FSSH_B_FORMAT_DEVICE +#define B_EJECT_DEVICE FSSH_B_EJECT_DEVICE +#define B_GET_ICON FSSH_B_GET_ICON +#define B_GET_BIOS_GEOMETRY FSSH_B_GET_BIOS_GEOMETRY +#define B_GET_MEDIA_STATUS FSSH_B_GET_MEDIA_STATUS +#define B_LOAD_MEDIA FSSH_B_LOAD_MEDIA +#define B_GET_BIOS_DRIVE_ID FSSH_B_GET_BIOS_DRIVE_ID +#define B_SET_UNINTERRUPTABLE_IO FSSH_B_SET_UNINTERRUPTABLE_IO +#define B_SET_INTERRUPTABLE_IO FSSH_B_SET_INTERRUPTABLE_IO +#define B_FLUSH_DRIVE_CACHE FSSH_B_FLUSH_DRIVE_CACHE +#define B_GET_PATH_FOR_DEVICE FSSH_B_GET_PATH_FOR_DEVICE +#define B_GET_NEXT_OPEN_DEVICE FSSH_B_GET_NEXT_OPEN_DEVICE +#define B_ADD_FIXED_DRIVER FSSH_B_ADD_FIXED_DRIVER +#define B_REMOVE_FIXED_DRIVER FSSH_B_REMOVE_FIXED_DRIVER +#define B_AUDIO_DRIVER_BASE FSSH_B_AUDIO_DRIVER_BASE +#define B_MIDI_DRIVER_BASE FSSH_B_MIDI_DRIVER_BASE +#define B_JOYSTICK_DRIVER_BASE FSSH_B_JOYSTICK_DRIVER_BASE +#define B_GRAPHIC_DRIVER_BASE FSSH_B_GRAPHIC_DRIVER_BASE +#define B_DEVICE_OP_CODES_END FSSH_B_DEVICE_OP_CODES_END + +#define device_geometry fssh_device_geometry + +#define B_DISK FSSH_B_DISK +#define B_TAPE FSSH_B_TAPE +#define B_PRINTER FSSH_B_PRINTER +#define B_CPU FSSH_B_CPU +#define B_WORM FSSH_B_WORM +#define B_CD FSSH_B_CD +#define B_SCANNER FSSH_B_SCANNER +#define B_OPTICAL FSSH_B_OPTICAL +#define B_JUKEBOX FSSH_B_JUKEBOX +#define B_NETWORK FSSH_B_NETWORK + + +#define partition_info fssh_partition_info +#define driver_path fssh_driver_path +#define open_device_iterator fssh_open_device_iterator +#define device_icon fssh_device_icon + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_errors.h + +/* Error baselines */ +#define B_GENERAL_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE +#define B_OS_ERROR_BASE FSSH_B_OS_ERROR_BASE +#define B_APP_ERROR_BASE FSSH_B_APP_ERROR_BASE +#define B_INTERFACE_ERROR_BASE FSSH_B_INTERFACE_ERROR_BASE +#define B_MEDIA_ERROR_BASE FSSH_B_MEDIA_ERROR_BASE +#define B_TRANSLATION_ERROR_BASE FSSH_B_TRANSLATION_ERROR_BASE +#define B_MIDI_ERROR_BASE FSSH_B_MIDI_ERROR_BASE +#define B_STORAGE_ERROR_BASE FSSH_B_STORAGE_ERROR_BASE +#define B_POSIX_ERROR_BASE FSSH_B_POSIX_ERROR_BASE +#define B_MAIL_ERROR_BASE FSSH_B_MAIL_ERROR_BASE +#define B_PRINT_ERROR_BASE FSSH_B_PRINT_ERROR_BASE +#define B_DEVICE_ERROR_BASE FSSH_B_DEVICE_ERROR_BASE + +/* Developer-defined errors start at (B_ERRORS_END+1) */ +#define B_ERRORS_END FSSH_B_ERRORS_END + +/* General Errors */ +#define B_NO_MEMORY FSSH_B_NO_MEMORY +#define B_IO_ERROR FSSH_B_IO_ERROR +#define B_PERMISSION_DENIED FSSH_B_PERMISSION_DENIED +#define B_BAD_INDEX FSSH_B_BAD_INDEX +#define B_BAD_TYPE FSSH_B_BAD_TYPE +#define B_BAD_VALUE FSSH_B_BAD_VALUE +#define B_MISMATCHED_VALUES FSSH_B_MISMATCHED_VALUES +#define B_NAME_NOT_FOUND FSSH_B_NAME_NOT_FOUND +#define B_NAME_IN_USE FSSH_B_NAME_IN_USE +#define B_TIMED_OUT FSSH_B_TIMED_OUT +#define B_INTERRUPTED FSSH_B_INTERRUPTED +#define B_WOULD_BLOCK FSSH_B_WOULD_BLOCK +#define B_CANCELED FSSH_B_CANCELED +#define B_NO_INIT FSSH_B_NO_INIT +#define B_BUSY FSSH_B_BUSY +#define B_NOT_ALLOWED FSSH_B_NOT_ALLOWED +#define B_BAD_DATA FSSH_B_BAD_DATA +#define B_DONT_DO_THAT FSSH_B_DONT_DO_THAT + +#define B_ERROR FSSH_B_ERROR +#define B_OK FSSH_B_OK +#define B_NO_ERROR FSSH_B_NO_ERROR + +/* Kernel Kit Errors */ +#define B_BAD_SEM_ID FSSH_B_BAD_SEM_ID +#define B_NO_MORE_SEMS FSSH_B_NO_MORE_SEMS + +#define B_BAD_THREAD_ID FSSH_B_BAD_THREAD_ID +#define B_NO_MORE_THREADS FSSH_B_NO_MORE_THREADS +#define B_BAD_THREAD_STATE FSSH_B_BAD_THREAD_STATE +#define B_BAD_TEAM_ID FSSH_B_BAD_TEAM_ID +#define B_NO_MORE_TEAMS FSSH_B_NO_MORE_TEAMS + +#define B_BAD_PORT_ID FSSH_B_BAD_PORT_ID +#define B_NO_MORE_PORTS FSSH_B_NO_MORE_PORTS + +#define B_BAD_IMAGE_ID FSSH_B_BAD_IMAGE_ID +#define B_BAD_ADDRESS FSSH_B_BAD_ADDRESS +#define B_NOT_AN_EXECUTABLE FSSH_B_NOT_AN_EXECUTABLE +#define B_MISSING_LIBRARY FSSH_B_MISSING_LIBRARY +#define B_MISSING_SYMBOL FSSH_B_MISSING_SYMBOL + +#define B_DEBUGGER_ALREADY_INSTALLED FSSH_B_DEBUGGER_ALREADY_INSTALLED + +/* Application Kit Errors */ +#define B_BAD_REPLY FSSH_B_BAD_REPLY +#define B_DUPLICATE_REPLY FSSH_B_DUPLICATE_REPLY +#define B_MESSAGE_TO_SELF FSSH_B_MESSAGE_TO_SELF +#define B_BAD_HANDLER FSSH_B_BAD_HANDLER +#define B_ALREADY_RUNNING FSSH_B_ALREADY_RUNNING +#define B_LAUNCH_FAILED FSSH_B_LAUNCH_FAILED +#define B_AMBIGUOUS_APP_LAUNCH FSSH_B_AMBIGUOUS_APP_LAUNCH +#define B_UNKNOWN_MIME_TYPE FSSH_B_UNKNOWN_MIME_TYPE +#define B_BAD_SCRIPT_SYNTAX FSSH_B_BAD_SCRIPT_SYNTAX +#define B_LAUNCH_FAILED_NO_RESOLVE_LINK FSSH_B_LAUNCH_FAILED_NO_RESOLVE_LINK +#define B_LAUNCH_FAILED_EXECUTABLE FSSH_B_LAUNCH_FAILED_EXECUTABLE +#define B_LAUNCH_FAILED_APP_NOT_FOUND FSSH_B_LAUNCH_FAILED_APP_NOT_FOUND +#define B_LAUNCH_FAILED_APP_IN_TRASH FSSH_B_LAUNCH_FAILED_APP_IN_TRASH +#define B_LAUNCH_FAILED_NO_PREFERRED_APP FSSH_B_LAUNCH_FAILED_NO_PREFERRED_APP +#define B_LAUNCH_FAILED_FILES_APP_NOT_FOUND FSSH_B_LAUNCH_FAILED_FILES_APP_NOT_FOUND +#define B_BAD_MIME_SNIFFER_RULE FSSH_B_BAD_MIME_SNIFFER_RULE +#define B_NOT_A_MESSAGE FSSH_B_NOT_A_MESSAGE +#define B_SHUTDOWN_CANCELLED FSSH_B_SHUTDOWN_CANCELLED +#define B_SHUTTING_DOWN FSSH_B_SHUTTING_DOWN + +/* Storage Kit/File System Errors */ +#define B_FILE_ERROR FSSH_B_FILE_ERROR +#define B_FILE_NOT_FOUND FSSH_B_FILE_NOT_FOUND +#define B_FILE_EXISTS FSSH_B_FILE_EXISTS +#define B_ENTRY_NOT_FOUND FSSH_B_ENTRY_NOT_FOUND +#define B_NAME_TOO_LONG FSSH_B_NAME_TOO_LONG +#define B_NOT_A_DIRECTORY FSSH_B_NOT_A_DIRECTORY +#define B_DIRECTORY_NOT_EMPTY FSSH_B_DIRECTORY_NOT_EMPTY +#define B_DEVICE_FULL FSSH_B_DEVICE_FULL +#define B_READ_ONLY_DEVICE FSSH_B_READ_ONLY_DEVICE +#define B_IS_A_DIRECTORY FSSH_B_IS_A_DIRECTORY +#define B_NO_MORE_FDS FSSH_B_NO_MORE_FDS +#define B_CROSS_DEVICE_LINK FSSH_B_CROSS_DEVICE_LINK +#define B_LINK_LIMIT FSSH_B_LINK_LIMIT +#define B_BUSTED_PIPE FSSH_B_BUSTED_PIPE +#define B_UNSUPPORTED FSSH_B_UNSUPPORTED +#define B_PARTITION_TOO_SMALL FSSH_B_PARTITION_TOO_SMALL + +/* POSIX Errors */ +#define E2BIG FSSH_E2BIG +#define ECHILD FSSH_ECHILD +#define EDEADLK FSSH_EDEADLK +#define EFBIG FSSH_EFBIG +#define EMLINK FSSH_EMLINK +#define ENFILE FSSH_ENFILE +#define ENODEV FSSH_ENODEV +#define ENOLCK FSSH_ENOLCK +#define ENOSYS FSSH_ENOSYS +#define ENOTTY FSSH_ENOTTY +#define ENXIO FSSH_ENXIO +#define ESPIPE FSSH_ESPIPE +#define ESRCH FSSH_ESRCH +#define EFPOS FSSH_EFPOS +#define ESIGPARM FSSH_ESIGPARM +#define EDOM FSSH_EDOM +#define ERANGE FSSH_ERANGE +#define EPROTOTYPE FSSH_EPROTOTYPE +#define EPROTONOSUPPORT FSSH_EPROTONOSUPPORT +#define EPFNOSUPPORT FSSH_EPFNOSUPPORT +#define EAFNOSUPPORT FSSH_EAFNOSUPPORT +#define EADDRINUSE FSSH_EADDRINUSE +#define EADDRNOTAVAIL FSSH_EADDRNOTAVAIL +#define ENETDOWN FSSH_ENETDOWN +#define ENETUNREACH FSSH_ENETUNREACH +#define ENETRESET FSSH_ENETRESET +#define ECONNABORTED FSSH_ECONNABORTED +#define ECONNRESET FSSH_ECONNRESET +#define EISCONN FSSH_EISCONN +#define ENOTCONN FSSH_ENOTCONN +#define ESHUTDOWN FSSH_ESHUTDOWN +#define ECONNREFUSED FSSH_ECONNREFUSED +#define EHOSTUNREACH FSSH_EHOSTUNREACH +#define ENOPROTOOPT FSSH_ENOPROTOOPT +#define ENOBUFS FSSH_ENOBUFS +#define EINPROGRESS FSSH_EINPROGRESS +#define EALREADY FSSH_EALREADY +#define EILSEQ FSSH_EILSEQ +#define ENOMSG FSSH_ENOMSG +#define ESTALE FSSH_ESTALE +#define EOVERFLOW FSSH_EOVERFLOW +#define EMSGSIZE FSSH_EMSGSIZE +#define EOPNOTSUPP FSSH_EOPNOTSUPP +#define ENOTSOCK FSSH_ENOTSOCK +#define EHOSTDOWN FSSH_EHOSTDOWN +#define EBADMSG FSSH_EBADMSG +#define ECANCELED FSSH_ECANCELED +#define EDESTADDRREQ FSSH_EDESTADDRREQ +#define EDQUOT FSSH_EDQUOT +#define EIDRM FSSH_EIDRM +#define EMULTIHOP FSSH_EMULTIHOP +#define ENODATA FSSH_ENODATA +#define ENOLINK FSSH_ENOLINK +#define ENOSR FSSH_ENOSR +#define ENOSTR FSSH_ENOSTR +#define ENOTSUP FSSH_ENOTSUP +#define EPROTO FSSH_EPROTO +#define ETIME FSSH_ETIME +#define ETXTBSY FSSH_ETXTBSY + +/* POSIX errors that can be mapped to BeOS error codes */ +#define ENOMEM FSSH_ENOMEM +#define EACCES FSSH_EACCES +#define EINTR FSSH_EINTR +#define EIO FSSH_EIO +#define EBUSY FSSH_EBUSY +#define EFAULT FSSH_EFAULT +#define ETIMEDOUT FSSH_ETIMEDOUT +#define EAGAIN FSSH_EAGAIN +#define EWOULDBLOCK FSSH_EWOULDBLOCK +#define EBADF FSSH_EBADF +#define EEXIST FSSH_EEXIST +#define EINVAL FSSH_EINVAL +#define ENAMETOOLONG FSSH_ENAMETOOLONG +#define ENOENT FSSH_ENOENT +#define EPERM FSSH_EPERM +#define ENOTDIR FSSH_ENOTDIR +#define EISDIR FSSH_EISDIR +#define ENOTEMPTY FSSH_ENOTEMPTY +#define ENOSPC FSSH_ENOSPC +#define EROFS FSSH_EROFS +#define EMFILE FSSH_EMFILE +#define EXDEV FSSH_EXDEV +#define ELOOP FSSH_ELOOP +#define ENOEXEC FSSH_ENOEXEC +#define EPIPE FSSH_EPIPE + +/* new error codes that can be mapped to POSIX errors */ +#define B_BUFFER_OVERFLOW FSSH_B_BUFFER_OVERFLOW +#define B_TOO_MANY_ARGS FSSH_B_TOO_MANY_ARGS +#define B_FILE_TOO_LARGE FSSH_B_FILE_TOO_LARGE +#define B_RESULT_NOT_REPRESENTABLE FSSH_B_RESULT_NOT_REPRESENTABLE +#define B_DEVICE_NOT_FOUND FSSH_B_DEVICE_NOT_FOUND +#define B_NOT_SUPPORTED FSSH_B_NOT_SUPPORTED + +/* Media Kit Errors */ +#define B_STREAM_NOT_FOUND FSSH_B_STREAM_NOT_FOUND +#define B_SERVER_NOT_FOUND FSSH_B_SERVER_NOT_FOUND +#define B_RESOURCE_NOT_FOUND FSSH_B_RESOURCE_NOT_FOUND +#define B_RESOURCE_UNAVAILABLE FSSH_B_RESOURCE_UNAVAILABLE +#define B_BAD_SUBSCRIBER FSSH_B_BAD_SUBSCRIBER +#define B_SUBSCRIBER_NOT_ENTERED FSSH_B_SUBSCRIBER_NOT_ENTERED +#define B_BUFFER_NOT_AVAILABLE FSSH_B_BUFFER_NOT_AVAILABLE +#define B_LAST_BUFFER_ERROR FSSH_B_LAST_BUFFER_ERROR + +/* Mail Kit Errors */ +#define B_MAIL_NO_DAEMON FSSH_B_MAIL_NO_DAEMON +#define B_MAIL_UNKNOWN_USER FSSH_B_MAIL_UNKNOWN_USER +#define B_MAIL_WRONG_PASSWORD FSSH_B_MAIL_WRONG_PASSWORD +#define B_MAIL_UNKNOWN_HOST FSSH_B_MAIL_UNKNOWN_HOST +#define B_MAIL_ACCESS_ERROR FSSH_B_MAIL_ACCESS_ERROR +#define B_MAIL_UNKNOWN_FIELD FSSH_B_MAIL_UNKNOWN_FIELD +#define B_MAIL_NO_RECIPIENT FSSH_B_MAIL_NO_RECIPIENT +#define B_MAIL_INVALID_MAIL FSSH_B_MAIL_INVALID_MAIL + +/* Printing Errors */ +#define B_NO_PRINT_SERVER FSSH_B_NO_PRINT_SERVER + +/* Device Kit Errors */ +#define B_DEV_INVALID_IOCTL FSSH_B_DEV_INVALID_IOCTL +#define B_DEV_NO_MEMORY FSSH_B_DEV_NO_MEMORY +#define B_DEV_BAD_DRIVE_NUM FSSH_B_DEV_BAD_DRIVE_NUM +#define B_DEV_NO_MEDIA FSSH_B_DEV_NO_MEDIA +#define B_DEV_UNREADABLE FSSH_B_DEV_UNREADABLE +#define B_DEV_FORMAT_ERROR FSSH_B_DEV_FORMAT_ERROR +#define B_DEV_TIMEOUT FSSH_B_DEV_TIMEOUT +#define B_DEV_RECALIBRATE_ERROR FSSH_B_DEV_RECALIBRATE_ERROR +#define B_DEV_SEEK_ERROR FSSH_B_DEV_SEEK_ERROR +#define B_DEV_ID_ERROR FSSH_B_DEV_ID_ERROR +#define B_DEV_READ_ERROR FSSH_B_DEV_READ_ERROR +#define B_DEV_WRITE_ERROR FSSH_B_DEV_WRITE_ERROR +#define B_DEV_NOT_READY FSSH_B_DEV_NOT_READY +#define B_DEV_MEDIA_CHANGED FSSH_B_DEV_MEDIA_CHANGED +#define B_DEV_MEDIA_CHANGE_REQUESTED FSSH_B_DEV_MEDIA_CHANGE_REQUESTED +#define B_DEV_RESOURCE_CONFLICT FSSH_B_DEV_RESOURCE_CONFLICT +#define B_DEV_CONFIGURATION_ERROR FSSH_B_DEV_CONFIGURATION_ERROR +#define B_DEV_DISABLED_BY_USER FSSH_B_DEV_DISABLED_BY_USER +#define B_DEV_DOOR_OPEN FSSH_B_DEV_DOOR_OPEN + +#define B_DEV_INVALID_PIPE FSSH_B_DEV_INVALID_PIPE +#define B_DEV_CRC_ERROR FSSH_B_DEV_CRC_ERROR +#define B_DEV_STALLED FSSH_B_DEV_STALLED +#define B_DEV_BAD_PID FSSH_B_DEV_BAD_PID +#define B_DEV_UNEXPECTED_PID FSSH_B_DEV_UNEXPECTED_PID +#define B_DEV_DATA_OVERRUN FSSH_B_DEV_DATA_OVERRUN +#define B_DEV_DATA_UNDERRUN FSSH_B_DEV_DATA_UNDERRUN +#define B_DEV_FIFO_OVERRUN FSSH_B_DEV_FIFO_OVERRUN +#define B_DEV_FIFO_UNDERRUN FSSH_B_DEV_FIFO_UNDERRUN +#define B_DEV_PENDING FSSH_B_DEV_PENDING +#define B_DEV_MULTIPLE_ERRORS FSSH_B_DEV_MULTIPLE_ERRORS +#define B_DEV_TOO_LATE FSSH_B_DEV_TOO_LATE + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_errno.h + +#define ENOERR FSSH_ENOERR +#define EOK FSSH_EOK + +#define errno fssh_errno + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fcntl.h + +/* commands that can be passed to fcntl() */ +#define F_DUPFD FSSH_F_DUPFD +#define F_GETFD FSSH_F_GETFD +#define F_SETFD FSSH_F_SETFD +#define F_GETFL FSSH_F_GETFL +#define F_SETFL FSSH_F_SETFL +#define F_GETLK FSSH_F_GETLK +#define F_SETLK FSSH_F_SETLK +#define F_SETLKW FSSH_F_SETLKW + +/* advisory locking types */ +#define F_RDLCK FSSH_F_RDLCK +#define F_UNLCK FSSH_F_UNLCK +#define F_WRLCK FSSH_F_WRLCK + +/* file descriptor flags for fcntl() */ +#define FD_CLOEXEC FSSH_FD_CLOEXEC + +/* file access modes for open() */ +#define O_RDONLY FSSH_O_RDONLY +#define O_WRONLY FSSH_O_WRONLY +#define O_RDWR FSSH_O_RDWR +#define O_ACCMODE FSSH_O_ACCMODE +#define O_RWMASK FSSH_O_RWMASK + +/* flags for open() */ +#define O_EXCL FSSH_O_EXCL +#define O_CREAT FSSH_O_CREAT +#define O_TRUNC FSSH_O_TRUNC +#define O_NOCTTY FSSH_O_NOCTTY +#define O_NOTRAVERSE FSSH_O_NOTRAVERSE + +/* flags for open() and fcntl() */ +#define O_CLOEXEC FSSH_O_CLOEXEC +#define O_NONBLOCK FSSH_O_NONBLOCK +#define O_APPEND FSSH_O_APPEND +#define O_TEXT FSSH_O_TEXT +#define O_BINARY FSSH_O_BINARY +#define O_SYNC FSSH_O_SYNC +#define O_RSYNC FSSH_O_RSYNC +#define O_DSYNC FSSH_O_DSYNC + +#define O_NOFOLLOW FSSH_O_NOFOLLOW +#define O_NOCACHE FSSH_O_NOCACHE +#define O_DIRECT FSSH_O_DIRECT +#define O_MOUNT FSSH_O_MOUNT +#define O_TEMPORARY FSSH_O_TEMPORARY +#define O_SHLOCK FSSH_O_SHLOCK +#define O_EXLOCK FSSH_O_EXLOCK + +#define S_IREAD FSSH_S_IREAD +#define S_IWRITE FSSH_S_IWRITE + +#define creat fssh_creat +#define open fssh_open +#define fcntl fssh_fcntl + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fs_attr.h + +#define attr_info fssh_attr_info + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fs_cache.h + +#define transaction_notification_hook fssh_transaction_notification_hook + +/* transactions */ +#define cache_start_transaction fssh_cache_start_transaction +#define cache_sync_transaction fssh_cache_sync_transaction +#define cache_end_transaction fssh_cache_end_transaction +#define cache_abort_transaction fssh_cache_abort_transaction +#define cache_detach_sub_transaction fssh_cache_detach_sub_transaction +#define cache_abort_sub_transaction fssh_cache_abort_sub_transaction +#define cache_start_sub_transaction fssh_cache_start_sub_transaction +#define cache_next_block_in_transaction fssh_cache_next_block_in_transaction +#define cache_blocks_in_transaction fssh_cache_blocks_in_transaction +#define cache_blocks_in_sub_transaction fssh_cache_blocks_in_sub_transaction + +/* block cache */ +#define block_cache_delete fssh_block_cache_delete +#define block_cache_create fssh_block_cache_create +#define block_cache_sync fssh_block_cache_sync +#define block_cache_sync_etc fssh_block_cache_sync_etc +#define block_cache_make_writable fssh_block_cache_make_writable +#define block_cache_get_writable_etc fssh_block_cache_get_writable_etc +#define block_cache_get_writable fssh_block_cache_get_writable +#define block_cache_get_empty fssh_block_cache_get_empty +#define block_cache_get_etc fssh_block_cache_get_etc +#define block_cache_get fssh_block_cache_get +#define block_cache_set_dirty fssh_block_cache_set_dirty +#define block_cache_put fssh_block_cache_put + +/* file cache */ +#define file_cache_create fssh_file_cache_create +#define file_cache_delete fssh_file_cache_delete +#define file_cache_set_size fssh_file_cache_set_size +#define file_cache_sync fssh_file_cache_sync +#define file_cache_invalidate_file_map fssh_file_cache_invalidate_file_map + +#define file_cache_read_pages fssh_file_cache_read_pages +#define file_cache_write_pages fssh_file_cache_write_pages +#define file_cache_read fssh_file_cache_read +#define file_cache_write fssh_file_cache_write + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fs_index.h + +#define index_info fssh_index_info + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fs_info.h + +/* fs_info.flags */ +#define B_FS_IS_READONLY FSSH_B_FS_IS_READONLY +#define B_FS_IS_REMOVABLE FSSH_B_FS_IS_REMOVABLE +#define B_FS_IS_PERSISTENT FSSH_B_FS_IS_PERSISTENT +#define B_FS_IS_SHARED FSSH_B_FS_IS_SHARED +#define B_FS_HAS_MIME FSSH_B_FS_HAS_MIME +#define B_FS_HAS_ATTR FSSH_B_FS_HAS_ATTR +#define B_FS_HAS_QUERY FSSH_B_FS_HAS_QUERY +// those additions are preliminary and may be removed +#define B_FS_HAS_SELF_HEALING_LINKS FSSH_B_FS_HAS_SELF_HEALING_LINKS +#define B_FS_HAS_ALIASES FSSH_B_FS_HAS_ALIASES +#define B_FS_SUPPORTS_NODE_MONITORING FSSH_B_FS_SUPPORTS_NODE_MONITORING + +#define fs_info fssh_fs_info + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fs_interface.h + +#define mount_id fssh_mount_id +#define vnode_id fssh_vnode_id + +/* the file system's private data structures */ +#define fs_volume fssh_fs_volume +#define fs_cookie fssh_fs_cookie +#define fs_vnode fssh_fs_vnode + +/* passed to write_stat() */ +#define write_stat_mask fssh_write_stat_mask + +#define FS_WRITE_STAT_MODE FSSH_FS_WRITE_STAT_MODE +#define FS_WRITE_STAT_UID FSSH_FS_WRITE_STAT_UID +#define FS_WRITE_STAT_GID FSSH_FS_WRITE_STAT_GID +#define FS_WRITE_STAT_SIZE FSSH_FS_WRITE_STAT_SIZE +#define FS_WRITE_STAT_ATIME FSSH_FS_WRITE_STAT_ATIME +#define FS_WRITE_STAT_MTIME FSSH_FS_WRITE_STAT_MTIME +#define FS_WRITE_STAT_CRTIME FSSH_FS_WRITE_STAT_CRTIME + +/* passed to write_fs_info() */ +#define FS_WRITE_FSINFO_NAME FSSH_FS_WRITE_FSINFO_NAME + +#define file_io_vec fssh_file_io_vec + +#define B_CURRENT_FS_API_VERSION FSSH_B_CURRENT_FS_API_VERSION + +#define file_system_module_info fssh_file_system_module_info + + +/* file system add-ons only prototypes */ +#define new_vnode fssh_new_vnode +#define publish_vnode fssh_publish_vnode +#define get_vnode fssh_get_vnode +#define put_vnode fssh_put_vnode +#define remove_vnode fssh_remove_vnode +#define unremove_vnode fssh_unremove_vnode +#define get_vnode_removed fssh_get_vnode_removed + +#define notify_entry_created fssh_notify_entry_created +#define notify_entry_removed fssh_notify_entry_removed +#define notify_entry_moved fssh_notify_entry_moved +#define notify_stat_changed fssh_notify_stat_changed +#define notify_attribute_changed fssh_notify_attribute_changed + +#define notify_query_entry_created fssh_notify_query_entry_created +#define notify_query_entry_removed fssh_notify_query_entry_removed + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fs_query.h + +#define B_LIVE_QUERY FSSH_B_LIVE_QUERY +#define B_QUERY_NON_INDEXED FSSH_B_QUERY_NON_INDEXED + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_fs_volume.h + +#define B_MOUNT_READ_ONLY FSSH_B_MOUNT_READ_ONLY +#define B_MOUNT_VIRTUAL_DEVICE FSSH_B_MOUNT_VIRTUAL_DEVICE + +/* unmount flags */ +#define B_FORCE_UNMOUNT FSSH_B_FORCE_UNMOUNT + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_kernel_export.h + +/* kernel threads */ + +#define spawn_kernel_thread fssh_spawn_kernel_thread + +/* primitive kernel debugging facilities */ +#define dprintf fssh_dprintf +#define kprintf fssh_kprintf +#define dump_block fssh_dump_block +#define panic fssh_panic +#define kernel_debugger fssh_kernel_debugger +#define parse_expression fssh_parse_expression + +#define debugger_command_hook fssh_debugger_command_hook +#define add_debugger_command fssh_add_debugger_command +#define remove_debugger_command fssh_remove_debugger_command + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_module.h + +#define module_info fssh_module_info + +/* module standard operations */ +#define B_MODULE_INIT FSSH_B_MODULE_INIT +#define B_MODULE_UNINIT FSSH_B_MODULE_UNINIT + +/* module flags */ +#define B_KEEP_LOADED FSSH_B_KEEP_LOADED + + +#define module_dependency fssh_module_dependency + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_node_monitor.h + +#define B_STOP_WATCHING FSSH_B_STOP_WATCHING +#define B_WATCH_NAME FSSH_B_WATCH_NAME +#define B_WATCH_STAT FSSH_B_WATCH_STAT +#define B_WATCH_ATTR FSSH_B_WATCH_ATTR +#define B_WATCH_DIRECTORY FSSH_B_WATCH_DIRECTORY +#define B_WATCH_ALL FSSH_B_WATCH_ALL +#define B_WATCH_MOUNT FSSH_B_WATCH_MOUNT + +#define B_ENTRY_CREATED FSSH_B_ENTRY_CREATED +#define B_ENTRY_REMOVED FSSH_B_ENTRY_REMOVED +#define B_ENTRY_MOVED FSSH_B_ENTRY_MOVED +#define B_STAT_CHANGED FSSH_B_STAT_CHANGED +#define B_ATTR_CHANGED FSSH_B_ATTR_CHANGED +#define B_DEVICE_MOUNTED FSSH_B_DEVICE_MOUNTED +#define B_DEVICE_UNMOUNTED FSSH_B_DEVICE_UNMOUNTED + +#define B_ATTR_CREATED FSSH_B_ATTR_CREATED +#define B_ATTR_REMOVED FSSH_B_ATTR_REMOVED + +#define B_STAT_MODE FSSH_B_STAT_MODE +#define B_STAT_UID FSSH_B_STAT_UID +#define B_STAT_GID FSSH_B_STAT_GID +#define B_STAT_SIZE FSSH_B_STAT_SIZE +#define B_STAT_ACCESS_TIME FSSH_B_STAT_ACCESS_TIME +#define B_STAT_MODIFICATION_TIME FSSH_B_STAT_MODIFICATION_TIME +#define B_STAT_CREATION_TIME FSSH_B_STAT_CREATION_TIME +#define B_STAT_CHANGE_TIME FSSH_B_STAT_CHANGE_TIME + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_os.h + +/* System constants */ + +#define B_OS_NAME_LENGTH FSSH_B_OS_NAME_LENGTH +#define B_PAGE_SIZE FSSH_B_PAGE_SIZE +#define B_INFINITE_TIMEOUT FSSH_B_INFINITE_TIMEOUT + + +/* Types */ + +#define area_id fssh_area_id +#define port_id fssh_port_id +#define sem_id fssh_sem_id +#define team_id fssh_team_id +#define thread_id fssh_thread_id + + +/* Semaphores */ + +#define sem_info fssh_sem_info + +/* semaphore flags */ +#define B_CAN_INTERRUPT FSSH_B_CAN_INTERRUPT +#define B_CHECK_PERMISSION FSSH_B_CHECK_PERMISSION +#define B_KILL_CAN_INTERRUPT FSSH_B_KILL_CAN_INTERRUPT + +#define B_DO_NOT_RESCHEDULE FSSH_B_DO_NOT_RESCHEDULE +#define B_RELEASE_ALL FSSH_B_RELEASE_ALL +#define B_RELEASE_IF_WAITING_ONLY FSSH_B_RELEASE_IF_WAITING_ONLY + +#define create_sem fssh_create_sem +#define delete_sem fssh_delete_sem +#define acquire_sem fssh_acquire_sem +#define acquire_sem_etc fssh_acquire_sem_etc +#define release_sem fssh_release_sem +#define release_sem_etc fssh_release_sem_etc +#define get_sem_count fssh_get_sem_count +#define set_sem_owner fssh_set_sem_owner + +#define get_sem_info(sem, info) fssh_get_sem_info(sem, info) +#define get_next_sem_info(team, cookie, info) fssh_get_next_sem_info(team, cookie, info) + +#define B_TIMEOUT FSSH_B_TIMEOUT +#define B_RELATIVE_TIMEOUT FSSH_B_RELATIVE_TIMEOUT +#define B_ABSOLUTE_TIMEOUT FSSH_B_ABSOLUTE_TIMEOUT + + +/* Teams */ + +#define B_CURRENT_TEAM FSSH_B_CURRENT_TEAM +#define B_SYSTEM_TEAM FSSH_B_SYSTEM_TEAM + + +/* Threads */ + +#define B_THREAD_RUNNING FSSH_B_THREAD_RUNNING +#define B_THREAD_READY FSSH_B_THREAD_READY +#define B_THREAD_RECEIVING FSSH_B_THREAD_RECEIVING +#define B_THREAD_ASLEEP FSSH_B_THREAD_ASLEEP +#define B_THREAD_SUSPENDED FSSH_B_THREAD_SUSPENDED +#define B_THREAD_WAITING FSSH_B_THREAD_WAITING + +#define thread_state fssh_thread_state +#define thread_info fssh_thread_info + +#define B_IDLE_PRIORITY FSSH_B_IDLE_PRIORITY +#define B_LOWEST_ACTIVE_PRIORITY FSSH_B_LOWEST_ACTIVE_PRIORITY +#define B_LOW_PRIORITY FSSH_B_LOW_PRIORITY +#define B_NORMAL_PRIORITY FSSH_B_NORMAL_PRIORITY +#define B_DISPLAY_PRIORITY FSSH_B_DISPLAY_PRIORITY +#define B_URGENT_DISPLAY_PRIORITY FSSH_B_URGENT_DISPLAY_PRIORITY +#define B_REAL_TIME_DISPLAY_PRIORITY FSSH_B_REAL_TIME_DISPLAY_PRIORITY +#define B_URGENT_PRIORITY FSSH_B_URGENT_PRIORITY +#define B_REAL_TIME_PRIORITY FSSH_B_REAL_TIME_PRIORITY + +#define B_FIRST_REAL_TIME_PRIORITY FSSH_B_FIRST_REAL_TIME_PRIORITY +#define B_MIN_PRIORITY FSSH_B_MIN_PRIORITY +#define B_MAX_PRIORITY FSSH_B_MAX_PRIORITY + +#define B_SYSTEM_TIMEBASE FSSH_B_SYSTEM_TIMEBASE + +#define thread_func fssh_thread_func +#define thread_entry fssh_thread_entry + +#define spawn_thread fssh_spawn_thread +#define kill_thread fssh_kill_thread +#define resume_thread fssh_resume_thread +#define suspend_thread fssh_suspend_thread + +#define rename_thread fssh_rename_thread +#define set_thread_priority fssh_set_thread_priority +#define exit_thread fssh_exit_thread +#define wait_for_thread fssh_wait_for_thread +#define on_exit_thread fssh_on_exit_thread + +#define find_thread fssh_find_thread + +#define send_data fssh_send_data +#define receive_data fssh_receive_data +#define has_data fssh_has_data + +#define snooze fssh_snooze +#define snooze_etc fssh_snooze_etc +#define snooze_until fssh_snooze_until + +#define get_thread_info(id, info) fssh_get_thread_info(id, info) +#define get_next_thread_info(team, cookie, info) fssh_get_next_thread_info(team, cookie, info) + + +/* Time */ + +#define real_time_clock fssh_real_time_clock +#define set_real_time_clock fssh_set_real_time_clock +#define real_time_clock_usecs fssh_real_time_clock_usecs +#define set_timezone fssh_set_timezone +#define system_time fssh_system_time + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_stat.h + +#define stat fssh_stat + +// struct stat fields +#undef st_dev +#undef st_ino +#undef st_mode +#undef st_nlink +#undef st_uid +#undef st_gid +#undef st_size +#undef st_rdev +#undef st_blksize +#undef st_atime +#undef st_mtime +#undef st_ctime +#undef st_crtime +#undef st_type + +#define st_dev fssh_st_dev +#define st_ino fssh_st_ino +#define st_mode fssh_st_mode +#define st_nlink fssh_st_nlink +#define st_uid fssh_st_uid +#define st_gid fssh_st_gid +#define st_size fssh_st_size +#define st_rdev fssh_st_rdev +#define st_blksize fssh_st_blksize +#define st_atime fssh_st_atime +#define st_mtime fssh_st_mtime +#define st_ctime fssh_st_ctime +#define st_crtime fssh_st_crtime +#define st_type fssh_st_type + +/* extended file types */ +#define S_ATTR_DIR FSSH_S_ATTR_DIR +#define S_ATTR FSSH_S_ATTR +#define S_INDEX_DIR FSSH_S_INDEX_DIR +#define S_STR_INDEX FSSH_S_STR_INDEX +#define S_INT_INDEX FSSH_S_INT_INDEX +#define S_UINT_INDEX FSSH_S_UINT_INDEX +#define S_LONG_LONG_INDEX FSSH_S_LONG_LONG_INDEX +#define S_ULONG_LONG_INDEX FSSH_S_ULONG_LONG_INDEX +#define S_FLOAT_INDEX FSSH_S_FLOAT_INDEX +#define S_DOUBLE_INDEX FSSH_S_DOUBLE_INDEX +#define S_ALLOW_DUPS FSSH_S_ALLOW_DUPS + +/* link types */ +#define S_LINK_SELF_HEALING FSSH_S_LINK_SELF_HEALING +#define S_LINK_AUTO_DELETE FSSH_S_LINK_AUTO_DELETE + +/* standard file types */ +#define S_IFMT FSSH_S_IFMT +#define S_IFLNK FSSH_S_IFLNK +#define S_IFREG FSSH_S_IFREG +#define S_IFBLK FSSH_S_IFBLK +#define S_IFDIR FSSH_S_IFDIR +#define S_IFCHR FSSH_S_IFCHR +#define S_IFIFO FSSH_S_IFIFO + +#define S_ISREG(mode) FSSH_S_ISREG(mode) +#define S_ISLNK(mode) FSSH_S_ISLNK(mode) +#define S_ISBLK(mode) FSSH_S_ISBLK(mode) +#define S_ISDIR(mode) FSSH_S_ISDIR(mode) +#define S_ISCHR(mode) FSSH_S_ISCHR(mode) +#define S_ISFIFO(mode) FSSH_S_ISFIFO(mode) +#define S_ISINDEX(mode) FSSH_S_ISINDEX(mode) + +#define S_IUMSK FSSH_S_IUMSK + +#define S_ISUID FSSH_S_ISUID +#define S_ISGID FSSH_S_ISGID + +#define S_ISVTX FSSH_S_ISVTX + +#define S_IRWXU FSSH_S_IRWXU +#define S_IRUSR FSSH_S_IRUSR +#define S_IWUSR FSSH_S_IWUSR +#define S_IXUSR FSSH_S_IXUSR +#define S_IRWXG FSSH_S_IRWXG +#define S_IRGRP FSSH_S_IRGRP +#define S_IWGRP FSSH_S_IWGRP +#define S_IXGRP FSSH_S_IXGRP +#define S_IRWXO FSSH_S_IRWXO +#define S_IROTH FSSH_S_IROTH +#define S_IWOTH FSSH_S_IWOTH +#define S_IXOTH FSSH_S_IXOTH + +#define ACCESSPERMS FSSH_ACCESSPERMS +#define ALLPERMS FSSH_ALLPERMS +#define DEFFILEMODE FSSH_DEFFILEMODE + +#define chmod fssh_chmod +#define fchmod fssh_fchmod +#define mkdir fssh_mkdir +#define mkfifo fssh_mkfifo +#define umask fssh_umask + +//#define stat fssh_stat + // Already mapped above for "struct stat". +#define fstat fssh_fstat +#define lstat fssh_lstat + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_stdio.h + +#define EOF FSSH_EOF + +/* file operations */ +#define remove fssh_remove +#define rename fssh_rename + +/* formatted I/O */ +#define sprintf fssh_sprintf +#define snprintf fssh_snprintf +#define vsprintf fssh_vsprintf +#define vsnprintf fssh_vsnprintf + +#define sscanf fssh_sscanf +#define vsscanf fssh_vsscanf + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_string.h + +/* memXXX() functions */ +#define memchr fssh_memchr +#define memcmp fssh_memcmp +#define memcpy fssh_memcpy +#define memccpy fssh_memccpy +#define memmove fssh_memmove +#define memset fssh_memset + +/* string functions */ +#define strcpy fssh_strcpy +#define strncpy fssh_strncpy +#define strcat fssh_strcat +#define strncat fssh_strncat +#define strlen fssh_strlen +#define strcmp fssh_strcmp +#define strncmp fssh_strncmp +#define strchr fssh_strchr +#define strrchr fssh_strrchr +#define strstr fssh_strstr +#define strchrnul fssh_strchrnul +#define strpbrk fssh_strpbrk +#define strtok fssh_strtok +#define strtok_r fssh_strtok_r +#define strspn fssh_strspn +#define strcspn fssh_strcspn +#define strcoll fssh_strcoll +#define strxfrm fssh_strxfrm +#define strerror fssh_strerror +#define strerror_r fssh_strerror_r + +/* non-standard string functions */ +#define strcasecmp fssh_strcasecmp +#define strncasecmp fssh_strncasecmp +#define strcasestr fssh_strcasestr +#define strdup fssh_strdup +#define stpcpy fssh_stpcpy +#define strtcopy fssh_strtcopy +#define strlcat fssh_strlcat +#define strlcpy fssh_strlcpy +#define strnlen fssh_strnlen + +#define ffs fssh_ffs +#define index fssh_index +#define rindex fssh_rindex + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_time.h + +#define clock_t fssh_clock_t +#define time_t fssh_time_t +#define suseconds_t fssh_suseconds_t +#define useconds_t fssh_useconds_t + +#define CLOCKS_PER_SEC FSSH_CLOCKS_PER_SEC +#define CLK_TCK FSSH_CLK_TCK +#define MAX_TIMESTR FSSH_MAX_TIMESTR + +#define timespec fssh_timespec +#define itimerspec fssh_itimerspec +#define tm fssh_tm + +/* special timezone support */ +#define tzname fssh_tzname +#define daylight fssh_daylight +#define timezone fssh_timezone + +#define clock fssh_clock +#define difftime fssh_difftime +#define mktime fssh_mktime +#define time fssh_time +#define asctime fssh_asctime +#define asctime_r fssh_asctime_r +#define ctime fssh_ctime +#define ctime_r fssh_ctime_r +#define gmtime fssh_gmtime +#define gmtime_r fssh_gmtime_r +#define localtime fssh_localtime +#define localtime_r fssh_localtime_r +#define strftime fssh_strftime +#define strptime fssh_strptime + +/* special timezone support */ +#define tzset fssh_tzset +#define stime fssh_stime + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_type_constants.h + +#define B_ANY_TYPE FSSH_B_ANY_TYPE +#define B_ATOM_TYPE FSSH_B_ATOM_TYPE +#define B_ATOMREF_TYPE FSSH_B_ATOMREF_TYPE +#define B_BOOL_TYPE FSSH_B_BOOL_TYPE +#define B_CHAR_TYPE FSSH_B_CHAR_TYPE +#define B_COLOR_8_BIT_TYPE FSSH_B_COLOR_8_BIT_TYPE +#define B_DOUBLE_TYPE FSSH_B_DOUBLE_TYPE +#define B_FLOAT_TYPE FSSH_B_FLOAT_TYPE +#define B_GRAYSCALE_8_BIT_TYPE FSSH_B_GRAYSCALE_8_BIT_TYPE +#define B_INT16_TYPE FSSH_B_INT16_TYPE +#define B_INT32_TYPE FSSH_B_INT32_TYPE +#define B_INT64_TYPE FSSH_B_INT64_TYPE +#define B_INT8_TYPE FSSH_B_INT8_TYPE +#define B_LARGE_ICON_TYPE FSSH_B_LARGE_ICON_TYPE +#define B_MEDIA_PARAMETER_GROUP_TYPE FSSH_B_MEDIA_PARAMETER_GROUP_TYPE +#define B_MEDIA_PARAMETER_TYPE FSSH_B_MEDIA_PARAMETER_TYPE +#define B_MEDIA_PARAMETER_WEB_TYPE FSSH_B_MEDIA_PARAMETER_WEB_TYPE +#define B_MESSAGE_TYPE FSSH_B_MESSAGE_TYPE +#define B_MESSENGER_TYPE FSSH_B_MESSENGER_TYPE +#define B_MIME_TYPE FSSH_B_MIME_TYPE +#define B_MINI_ICON_TYPE FSSH_B_MINI_ICON_TYPE +#define B_MONOCHROME_1_BIT_TYPE FSSH_B_MONOCHROME_1_BIT_TYPE +#define B_OBJECT_TYPE FSSH_B_OBJECT_TYPE +#define B_OFF_T_TYPE FSSH_B_OFF_T_TYPE +#define B_PATTERN_TYPE FSSH_B_PATTERN_TYPE +#define B_POINTER_TYPE FSSH_B_POINTER_TYPE +#define B_POINT_TYPE FSSH_B_POINT_TYPE +#define B_PROPERTY_INFO_TYPE FSSH_B_PROPERTY_INFO_TYPE +#define B_RAW_TYPE FSSH_B_RAW_TYPE +#define B_RECT_TYPE FSSH_B_RECT_TYPE +#define B_REF_TYPE FSSH_B_REF_TYPE +#define B_RGB_32_BIT_TYPE FSSH_B_RGB_32_BIT_TYPE +#define B_RGB_COLOR_TYPE FSSH_B_RGB_COLOR_TYPE +#define B_SIZE_T_TYPE FSSH_B_SIZE_T_TYPE +#define B_SSIZE_T_TYPE FSSH_B_SSIZE_T_TYPE +#define B_STRING_TYPE FSSH_B_STRING_TYPE +#define B_TIME_TYPE FSSH_B_TIME_TYPE +#define B_UINT16_TYPE FSSH_B_UINT16_TYPE +#define B_UINT32_TYPE FSSH_B_UINT32_TYPE +#define B_UINT64_TYPE FSSH_B_UINT64_TYPE +#define B_UINT8_TYPE FSSH_B_UINT8_TYPE +#define B_VECTOR_ICON_TYPE FSSH_B_VECTOR_ICON_TYPE +#define B_ASCII_TYPE FSSH_B_ASCII_TYPE + +//----- System-wide MIME types for handling URL's ------------------------------ + +#define B_URL_HTTP FSSH_B_URL_HTTP +#define B_URL_HTTPS FSSH_B_URL_HTTPS +#define B_URL_FTP FSSH_B_URL_FTP +#define B_URL_GOPHER FSSH_B_URL_GOPHER +#define B_URL_MAILTO FSSH_B_URL_MAILTO +#define B_URL_NEWS FSSH_B_URL_NEWS +#define B_URL_NNTP FSSH_B_URL_NNTP +#define B_URL_TELNET FSSH_B_URL_TELNET +#define B_URL_RLOGIN FSSH_B_URL_RLOGIN +#define B_URL_TN3270 FSSH_B_URL_TN3270 +#define B_URL_WAIS FSSH_B_URL_WAIS +#define B_URL_FILE FSSH_B_URL_FILE + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_types.h + +#define ulong fssh_ulong + +#define addr_t fssh_addr_t + +#define dev_t fssh_dev_t +#define ino_t fssh_ino_t + +#define size_t fssh_size_t +#define ssize_t fssh_ssize_t +#define off_t fssh_off_t + +#define bigtime_t fssh_bigtime_t + +#define status_t fssh_status_t +#define type_code fssh_type_code + +#define mode_t fssh_mode_t +#define nlink_t fssh_nlink_t +#define uid_t fssh_uid_t +#define gid_t fssh_gid_t +#define pid_t fssh_pid_t + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_uio.h + +#define iovec fssh_iovec + +#define readv fssh_readv +#define readv_pos fssh_readv_pos +#define writev fssh_writev +#define writev_pos fssh_writev_pos + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - fssh_unistd.h + +/* access modes */ +#define R_OK FSSH_R_OK +#define W_OK FSSH_W_OK +#define X_OK FSSH_X_OK +#define F_OK FSSH_F_OK + +/* standard file descriptors */ +#define STDIN_FILENO FSSH_STDIN_FILENO +#define STDOUT_FILENO FSSH_STDOUT_FILENO +#define STDERR_FILENO FSSH_STDERR_FILENO + +/* lseek() constants */ +#define SEEK_SET FSSH_SEEK_SET +#define SEEK_CUR FSSH_SEEK_CUR +#define SEEK_END FSSH_SEEK_END + +/* file functions */ +#define access fssh_access + +#define chdir fssh_chdir +#define fchdir fssh_fchdir +#define getcwd fssh_getcwd + +#define dup fssh_dup +#define dup2 fssh_dup2 +#define close fssh_close +#define link fssh_link +#define unlink fssh_unlink +#define rmdir fssh_rmdir + +#define readlink fssh_readlink +#define symlink fssh_symlink + +#define ftruncate fssh_ftruncate +#define truncate fssh_truncate +#define ioctl fssh_ioctl + +#define read fssh_read +#define read_pos fssh_read_pos +#define pread fssh_pread +#define write fssh_write +#define write_pos fssh_write_pos +#define pwrite fssh_pwrite +#define lseek fssh_lseek + +#define sync fssh_sync +#define fsync fssh_fsync + +/* access permissions */ +#define getegid fssh_getegid +#define geteuid fssh_geteuid +#define getgid fssh_getgid +#define getgroups fssh_getgroups +#define getuid fssh_getuid + + +//////////////////////////////////////////////////////////////////////////////// +// int types + +#define int8 int8_t +#define uint8 uint8_t +#define int16 int16_t +#define uint16 uint16_t +#define int32 int32_t +#define uint32 uint32_t +#define int64 int64_t +#define uint64 uint64_t + +#define vint32 vint32_t + + +#endif // _FSSH_API_WRAPPER_H diff --git a/headers/private/fs_shell/fssh_atomic.h b/headers/private/fs_shell/fssh_atomic.h new file mode 100644 index 0000000000..31093612e1 --- /dev/null +++ b/headers/private/fs_shell/fssh_atomic.h @@ -0,0 +1,32 @@ +/* Modules Definitions +** +** Distributed under the terms of the OpenBeOS License. +*/ + +#ifndef _FSSH_ATOMIC_H +#define _FSSH_ATOMIC_H + + +#include "fssh_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +int32_t fssh_atomic_set(vint32_t *value, int32_t newValue); +int32_t fssh_atomic_test_and_set(vint32_t *value, int32_t newValue, + int32_t testAgainst); +int32_t fssh_atomic_add(vint32_t *value, int32_t addValue); +int32_t fssh_atomic_and(vint32_t *value, int32_t andValue); +int32_t fssh_atomic_or(vint32_t *value, int32_t orValue); +int32_t fssh_atomic_get(vint32_t *value); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _FSSH_ATOMIC_H */ diff --git a/headers/private/fs_shell/fssh_auto_locker.h b/headers/private/fs_shell/fssh_auto_locker.h new file mode 100644 index 0000000000..628487bfb1 --- /dev/null +++ b/headers/private/fs_shell/fssh_auto_locker.h @@ -0,0 +1,162 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@users.sf.net. + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_AUTO_LOCKER_H +#define _FSSH_AUTO_LOCKER_H + + +namespace FSShell { + +// AutoLockerStandardLocking +template +class AutoLockerStandardLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->Lock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->Unlock(); + } +}; + +// AutoLockerReadLocking +template +class AutoLockerReadLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->ReadLock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->ReadUnlock(); + } +}; + +// AutoLockerWriteLocking +template +class AutoLockerWriteLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->WriteLock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->WriteUnlock(); + } +}; + +// AutoLocker +template > +class AutoLocker { +private: + typedef AutoLocker ThisClass; +public: + inline AutoLocker() + : fLockable(NULL), + fLocked(false) + { + } + + inline AutoLocker(Lockable *lockable, bool alreadyLocked = false, + bool lockIfNotLocked = true) + : fLockable(lockable), + fLocked(fLockable && alreadyLocked) + { + if (!alreadyLocked && lockIfNotLocked) + Lock(); + } + + inline AutoLocker(Lockable &lockable, bool alreadyLocked = false, + bool lockIfNotLocked = true) + : fLockable(&lockable), + fLocked(fLockable && alreadyLocked) + { + if (!alreadyLocked && lockIfNotLocked) + Lock(); + } + + inline ~AutoLocker() + { + Unlock(); + } + + inline void SetTo(Lockable *lockable, bool alreadyLocked, + bool lockIfNotLocked = true) + { + Unlock(); + fLockable = lockable; + fLocked = alreadyLocked; + if (!alreadyLocked && lockIfNotLocked) + Lock(); + } + + inline void SetTo(Lockable &lockable, bool alreadyLocked, + bool lockIfNotLocked = true) + { + SetTo(&lockable, alreadyLocked, lockIfNotLocked); + } + + inline void Unset() + { + Unlock(); + Detach(); + } + + inline bool Lock() + { + if (fLockable && !fLocked) + fLocked = fLocking.Lock(fLockable); + return fLocked; + } + + inline void Unlock() + { + if (fLockable && fLocked) { + fLocking.Unlock(fLockable); + fLocked = false; + } + } + + inline void Detach() + { + fLockable = NULL; + fLocked = false; + } + + inline AutoLocker &operator=(Lockable *lockable) + { + SetTo(lockable); + return *this; + } + + inline AutoLocker &operator=(Lockable &lockable) + { + SetTo(&lockable); + return *this; + } + + inline bool IsLocked() const { return fLocked; } + + inline operator bool() const { return fLocked; } + +private: + Lockable *fLockable; + bool fLocked; + Locking fLocking; +}; + + +} // namespace FSShell + +using FSShell::AutoLocker; + +#endif // _FSSH_AUTO_LOCKER_H diff --git a/headers/private/fs_shell/fssh_byte_order.h b/headers/private/fs_shell/fssh_byte_order.h new file mode 100644 index 0000000000..70ce1e561a --- /dev/null +++ b/headers/private/fs_shell/fssh_byte_order.h @@ -0,0 +1,131 @@ +/* + * Copyright 2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_BYTEORDER_H +#define _FSSH_BYTEORDER_H + +#include + // platform endian.h + + +#include "fssh_defs.h" + + +// swap directions +typedef enum { + FSSH_B_SWAP_HOST_TO_LENDIAN, + FSSH_B_SWAP_HOST_TO_BENDIAN, + FSSH_B_SWAP_LENDIAN_TO_HOST, + FSSH_B_SWAP_BENDIAN_TO_HOST, + FSSH_B_SWAP_ALWAYS +} fssh_swap_action; + + +// BSD/networking macros +#ifndef fssh_htonl +# define fssh_htonl(x) FSSH_B_HOST_TO_BENDIAN_INT32(x) +# define fssh_ntohl(x) FSSH_B_BENDIAN_TO_HOST_INT32(x) +# define fssh_htons(x) FSSH_B_HOST_TO_BENDIAN_INT16(x) +# define fssh_ntohs(x) FSSH_B_BENDIAN_TO_HOST_INT16(x) +#endif + +// always swap macros +#define FSSH_B_SWAP_DOUBLE(arg) __fssh_swap_double(arg) +#define FSSH_B_SWAP_FLOAT(arg) __fssh_swap_float(arg) +#define FSSH_B_SWAP_INT64(arg) __fssh_swap_int64(arg) +#define FSSH_B_SWAP_INT32(arg) __fssh_swap_int32(arg) +#define FSSH_B_SWAP_INT16(arg) __fssh_swap_int16(arg) + +#if BYTE_ORDER == __LITTLE_ENDIAN +// Host is little endian + +#define FSSH_B_HOST_IS_LENDIAN 1 +#define FSSH_B_HOST_IS_BENDIAN 0 + +// Host native to little endian +#define FSSH_B_HOST_TO_LENDIAN_DOUBLE(arg) (double)(arg) +#define FSSH_B_HOST_TO_LENDIAN_FLOAT(arg) (float)(arg) +#define FSSH_B_HOST_TO_LENDIAN_INT64(arg) (uint64_t)(arg) +#define FSSH_B_HOST_TO_LENDIAN_INT32(arg) (uint32_t)(arg) +#define FSSH_B_HOST_TO_LENDIAN_INT16(arg) (uint16_t)(arg) + +// Little endian to host native +#define FSSH_B_LENDIAN_TO_HOST_DOUBLE(arg) (double)(arg) +#define FSSH_B_LENDIAN_TO_HOST_FLOAT(arg) (float)(arg) +#define FSSH_B_LENDIAN_TO_HOST_INT64(arg) (uint64_t)(arg) +#define FSSH_B_LENDIAN_TO_HOST_INT32(arg) (uint32_t)(arg) +#define FSSH_B_LENDIAN_TO_HOST_INT16(arg) (uint16_t)(arg) + +// Host native to big endian +#define FSSH_B_HOST_TO_BENDIAN_DOUBLE(arg) __fssh_swap_double(arg) +#define FSSH_B_HOST_TO_BENDIAN_FLOAT(arg) __fssh_swap_float(arg) +#define FSSH_B_HOST_TO_BENDIAN_INT64(arg) __fssh_swap_int64(arg) +#define FSSH_B_HOST_TO_BENDIAN_INT32(arg) __fssh_swap_int32(arg) +#define FSSH_B_HOST_TO_BENDIAN_INT16(arg) __fssh_swap_int16(arg) + +// Big endian to host native +#define FSSH_B_BENDIAN_TO_HOST_DOUBLE(arg) __fssh_swap_double(arg) +#define FSSH_B_BENDIAN_TO_HOST_FLOAT(arg) __fssh_swap_float(arg) +#define FSSH_B_BENDIAN_TO_HOST_INT64(arg) __fssh_swap_int64(arg) +#define FSSH_B_BENDIAN_TO_HOST_INT32(arg) __fssh_swap_int32(arg) +#define FSSH_B_BENDIAN_TO_HOST_INT16(arg) __fssh_swap_int16(arg) + +#else // BYTE_ORDER +// Host is big endian + +#define FSSH_B_HOST_IS_LENDIAN 0 +#define FSSH_B_HOST_IS_BENDIAN 1 + +// Host native to little endian +#define FSSH_B_HOST_TO_LENDIAN_DOUBLE(arg) __fssh_swap_double(arg) +#define FSSH_B_HOST_TO_LENDIAN_FLOAT(arg) __fssh_swap_float(arg) +#define FSSH_B_HOST_TO_LENDIAN_INT64(arg) __fssh_swap_int64(arg) +#define FSSH_B_HOST_TO_LENDIAN_INT32(arg) __fssh_swap_int32(arg) +#define FSSH_B_HOST_TO_LENDIAN_INT16(arg) __fssh_swap_int16(arg) + +// Little endian to host native +#define FSSH_B_LENDIAN_TO_HOST_DOUBLE(arg) __fssh_swap_double(arg) +#define FSSH_B_LENDIAN_TO_HOST_FLOAT(arg) __fssh_swap_float(arg) +#define FSSH_B_LENDIAN_TO_HOST_INT64(arg) __fssh_swap_int64(arg) +#define FSSH_B_LENDIAN_TO_HOST_INT32(arg) __fssh_swap_int32(arg) +#define FSSH_B_LENDIAN_TO_HOST_INT16(arg) __fssh_swap_int16(arg) + +// Host native to big endian +#define FSSH_B_HOST_TO_BENDIAN_DOUBLE(arg) (double)(arg) +#define FSSH_B_HOST_TO_BENDIAN_FLOAT(arg) (float)(arg) +#define FSSH_B_HOST_TO_BENDIAN_INT64(arg) (uint64_t)(arg) +#define FSSH_B_HOST_TO_BENDIAN_INT32(arg) (uint32_t)(arg) +#define FSSH_B_HOST_TO_BENDIAN_INT16(arg) (uint16_t)(arg) + +// Big endian to host native +#define FSSH_B_BENDIAN_TO_HOST_DOUBLE(arg) (double)(arg) +#define FSSH_B_BENDIAN_TO_HOST_FLOAT(arg) (float)(arg) +#define FSSH_B_BENDIAN_TO_HOST_INT64(arg) (uint64_t)(arg) +#define FSSH_B_BENDIAN_TO_HOST_INT32(arg) (uint32_t)(arg) +#define FSSH_B_BENDIAN_TO_HOST_INT16(arg) (uint16_t)(arg) + +#endif // BYTE_ORDER + + +#ifdef __cplusplus +extern "C" { +#endif + +extern fssh_status_t fssh_swap_data(fssh_type_code type, void *data, + fssh_size_t length, fssh_swap_action action); +extern bool is_type_swapped(fssh_type_code type); + + +// Private implementations +extern double __fssh_swap_double(double arg); +extern float __fssh_swap_float(float arg); +extern uint64_t __fssh_swap_int64(uint64_t arg); +extern uint32_t __fssh_swap_int32(uint32_t arg); +extern uint16_t __fssh_swap_int16(uint16_t arg); + +#ifdef __cplusplus +} +#endif + +#endif // _FSSH_BYTEORDER_H diff --git a/headers/private/fs_shell/fssh_defs.h b/headers/private/fs_shell/fssh_defs.h new file mode 100644 index 0000000000..84433a6ad0 --- /dev/null +++ b/headers/private/fs_shell/fssh_defs.h @@ -0,0 +1,51 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#ifndef _FSSH_DEFS_H +#define _FSSH_DEFS_H + +#include "fssh_types.h" + + +// Limits +#define FSSH_B_DEV_NAME_LENGTH 128 +#define FSSH_B_FILE_NAME_LENGTH 256 +#define FSSH_B_PATH_NAME_LENGTH 1024 +#define FSSH_B_ATTR_NAME_LENGTH (FSSH_B_FILE_NAME_LENGTH-1) +#define FSSH_B_MIME_TYPE_LENGTH (FSSH_B_ATTR_NAME_LENGTH - 15) +#define FSSH_B_MAX_SYMLINKS 16 + +// Open Modes +#define FSSH_B_READ_ONLY FSSH_O_RDONLY // read only +#define FSSH_B_WRITE_ONLY FSSH_O_WRONLY // write only +#define FSSH_B_READ_WRITE FSSH_O_RDWR // read and write + +#define FSSH_B_FAIL_IF_EXISTS FSSH_O_EXCL // exclusive create +#define FSSH_B_CREATE_FILE FSSH_O_CREAT // create the file +#define FSSH_B_ERASE_FILE FSSH_O_TRUNC // erase the file's data +#define FSSH_B_OPEN_AT_END FSSH_O_APPEND // point to the end of the data + +// Node Flavors +enum fssh_node_flavor { + FSSH_B_FILE_NODE = 0x01, + FSSH_B_SYMLINK_NODE = 0x02, + FSSH_B_DIRECTORY_NODE = 0x04, + FSSH_B_ANY_NODE = 0x07 +}; + + +#if defined(__GNUC__) && __GNUC__ > 3 +#define fssh_offsetof(type,member) __builtin_offsetof(type, member) +#else +#define fssh_offsetof(type,member) ((size_t)&((type*)0)->member) +#endif + +#define fssh_min_c(a,b) ((a)>(b)?(b):(a)) +#define fssh_max_c(a,b) ((a)>(b)?(a):(b)) + +#define _FSSH_PACKED __attribute__((packed)) + + +#endif // _FSSH_DEFS_H diff --git a/headers/private/fs_shell/fssh_dirent.h b/headers/private/fs_shell/fssh_dirent.h new file mode 100644 index 0000000000..1b0cdb6681 --- /dev/null +++ b/headers/private/fs_shell/fssh_dirent.h @@ -0,0 +1,42 @@ +/* +** Distributed under the terms of the Haiku License. +*/ +#ifndef _FSSH_DIRENT_H +#define _FSSH_DIRENT_H + + +#include "fssh_defs.h" + + +typedef struct fssh_dirent { + fssh_dev_t d_dev; /* device */ + fssh_dev_t d_pdev; /* parent device (only for queries) */ + fssh_ino_t d_ino; /* inode number */ + fssh_ino_t d_pino; /* parent inode (only for queries) */ + unsigned short d_reclen; /* length of this record, not the name */ + char d_name[1]; /* name of the entry (null byte terminated) */ +} fssh_dirent_t; + +typedef struct { + int fd; + struct fssh_dirent ent; +} fssh_DIR; + +#ifdef __cplusplus +extern "C" { +#endif + +fssh_DIR *fssh_opendir(const char *dirname); +struct fssh_dirent *fssh_readdir(fssh_DIR *dir); +int fssh_readdir_r(fssh_DIR *dir, struct fssh_dirent *entry, + struct fssh_dirent **_result); +int fssh_closedir(fssh_DIR *dir); +void fssh_rewinddir(fssh_DIR *dir); +void fssh_seekdir(fssh_DIR *dir, long int loc); +long int fssh_telldir(fssh_DIR *); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_DIRENT_H */ diff --git a/headers/private/fs_shell/fssh_disk_device_defs.h b/headers/private/fs_shell/fssh_disk_device_defs.h new file mode 100644 index 0000000000..20fcfec1e4 --- /dev/null +++ b/headers/private/fs_shell/fssh_disk_device_defs.h @@ -0,0 +1,156 @@ +/* + * Copyright 2003-2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_DISK_DEVICE_DEFS_H +#define _FSSH_DISK_DEVICE_DEFS_H + + +#include "fssh_defs.h" + + +typedef int32_t fssh_partition_id; +typedef int32_t fssh_disk_system_id; +typedef int32_t fssh_disk_job_id; + +// partition flags +enum { + FSSH_B_PARTITION_IS_DEVICE = 0x01, + FSSH_B_PARTITION_FILE_SYSTEM = 0x02, + FSSH_B_PARTITION_PARTITIONING_SYSTEM = 0x04, + FSSH_B_PARTITION_READ_ONLY = 0x08, + FSSH_B_PARTITION_MOUNTED = 0x10, // needed? + FSSH_B_PARTITION_BUSY = 0x20, + FSSH_B_PARTITION_DESCENDANT_BUSY = 0x40, +}; + +// partition statuses +enum { + FSSH_B_PARTITION_VALID, + FSSH_B_PARTITION_CORRUPT, + FSSH_B_PARTITION_UNRECOGNIZED, + FSSH_B_PARTITION_UNINITIALIZED, // Only when uninitialized manually. + // When not recognized while scanning it's + // B_PARTITION_UNRECOGNIZED. +}; + +// partition change flags +enum { + FSSH_B_PARTITION_CHANGED_OFFSET = 0x000001, + FSSH_B_PARTITION_CHANGED_SIZE = 0x000002, + FSSH_B_PARTITION_CHANGED_CONTENT_SIZE = 0x000004, + FSSH_B_PARTITION_CHANGED_BLOCK_SIZE = 0x000008, + FSSH_B_PARTITION_CHANGED_STATUS = 0x000010, + FSSH_B_PARTITION_CHANGED_FLAGS = 0x000020, + FSSH_B_PARTITION_CHANGED_VOLUME = 0x000040, + FSSH_B_PARTITION_CHANGED_NAME = 0x000080, + FSSH_B_PARTITION_CHANGED_CONTENT_NAME = 0x000100, + FSSH_B_PARTITION_CHANGED_TYPE = 0x000200, + FSSH_B_PARTITION_CHANGED_CONTENT_TYPE = 0x000400, + FSSH_B_PARTITION_CHANGED_PARAMETERS = 0x000800, + FSSH_B_PARTITION_CHANGED_CONTENT_PARAMETERS = 0x001000, + FSSH_B_PARTITION_CHANGED_CHILDREN = 0x002000, + FSSH_B_PARTITION_CHANGED_DESCENDANTS = 0x004000, + FSSH_B_PARTITION_CHANGED_DEFRAGMENTATION = 0x008000, + FSSH_B_PARTITION_CHANGED_CHECK = 0x010000, + FSSH_B_PARTITION_CHANGED_REPAIR = 0x020000, + FSSH_B_PARTITION_CHANGED_INITIALIZATION = 0x040000, +}; + +// disk device flags +enum { + FSSH_B_DISK_DEVICE_REMOVABLE = 0x01, + FSSH_B_DISK_DEVICE_HAS_MEDIA = 0x02, + FSSH_B_DISK_DEVICE_READ_ONLY = 0x04, + FSSH_B_DISK_DEVICE_WRITE_ONCE = 0x08, +}; + +// disk system flags +enum { + FSSH_B_DISK_SYSTEM_IS_FILE_SYSTEM = 0x0001, + + // flags common for both file and partitioning systems + FSSH_B_DISK_SYSTEM_SUPPORTS_CHECKING = 0x0002, + FSSH_B_DISK_SYSTEM_SUPPORTS_REPAIRING = 0x0004, + FSSH_B_DISK_SYSTEM_SUPPORTS_RESIZING = 0x0008, + FSSH_B_DISK_SYSTEM_SUPPORTS_MOVING = 0x0010, + FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME = 0x0020, + FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS = 0x0040, + + // file system specific flags + FSSH_B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING = 0x0100, + FSSH_B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED = 0x0200, + FSSH_B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED = 0x0400, + FSSH_B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED = 0x0800, + FSSH_B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED = 0x1000, + FSSH_B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED = 0x2000, + FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED = 0x4000, + FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED = 0x8000, + + // partitioning system specific flags + FSSH_B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD = 0x0100, + FSSH_B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD = 0x0200, + FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_NAME = 0x0400, + FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE = 0x0800, + FSSH_B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS = 0x1000, + FSSH_B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD = 0x2000, + FSSH_B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD = 0x4000, + FSSH_B_DISK_SYSTEM_SUPPORTS_INITIALIZING = 0x8000, +}; + +// disk device job types +enum { + FSSH_B_DISK_DEVICE_JOB_BAD_TYPE, + FSSH_B_DISK_DEVICE_JOB_DEFRAGMENT, + FSSH_B_DISK_DEVICE_JOB_REPAIR, + FSSH_B_DISK_DEVICE_JOB_RESIZE, + FSSH_B_DISK_DEVICE_JOB_MOVE, + FSSH_B_DISK_DEVICE_JOB_SET_NAME, + FSSH_B_DISK_DEVICE_JOB_SET_CONTENT_NAME, + FSSH_B_DISK_DEVICE_JOB_SET_TYPE, + FSSH_B_DISK_DEVICE_JOB_SET_PARMETERS, + FSSH_B_DISK_DEVICE_JOB_SET_CONTENT_PARMETERS, + FSSH_B_DISK_DEVICE_JOB_INITIALIZE, + FSSH_B_DISK_DEVICE_JOB_UNINITIALIZE, + FSSH_B_DISK_DEVICE_JOB_CREATE, + FSSH_B_DISK_DEVICE_JOB_DELETE, + FSSH_B_DISK_DEVICE_JOB_SCAN, +}; + +// disk device job statuses +enum { + FSSH_B_DISK_DEVICE_JOB_UNINITIALIZED, + FSSH_B_DISK_DEVICE_JOB_SCHEDULED, + FSSH_B_DISK_DEVICE_JOB_IN_PROGRESS, + FSSH_B_DISK_DEVICE_JOB_SUCCEEDED, + FSSH_B_DISK_DEVICE_JOB_FAILED, + FSSH_B_DISK_DEVICE_JOB_CANCELED, +}; + +// disk device job progress info +typedef struct fssh_disk_device_job_progress_info { + uint32_t status; + uint32_t interrupt_properties; + int32_t task_count; + int32_t completed_tasks; + float current_task_progress; + char current_task_description[256]; +} fssh_disk_device_job_progress_info; + +// disk device job interrupt properties +enum { + FSSH_B_DISK_DEVICE_JOB_CAN_CANCEL = 0x01, + FSSH_B_DISK_DEVICE_JOB_STOP_ON_CANCEL = 0x02, + FSSH_B_DISK_DEVICE_JOB_REVERSE_ON_CANCEL = 0x04, + FSSH_B_DISK_DEVICE_JOB_CAN_PAUSE = 0x08, +}; + +// string length constants, all of which include the NULL terminator +#define FSSH_B_DISK_DEVICE_TYPE_LENGTH FSSH_B_FILE_NAME_LENGTH +#define FSSH_B_DISK_DEVICE_NAME_LENGTH FSSH_B_FILE_NAME_LENGTH +#define FSSH_B_DISK_SYSTEM_NAME_LENGTH FSSH_B_PATH_NAME_LENGTH + +// max size of parameter string buffers, including NULL terminator +#define FSSH_B_DISK_DEVICE_MAX_PARAMETER_SIZE (32 * 1024) + +#endif // _FSSH_DISK_DEVICE_DEFS_H diff --git a/headers/private/fs_shell/fssh_disk_device_manager.h b/headers/private/fs_shell/fssh_disk_device_manager.h new file mode 100644 index 0000000000..9fb1a1cb73 --- /dev/null +++ b/headers/private/fs_shell/fssh_disk_device_manager.h @@ -0,0 +1,134 @@ +/* + * Copyright 2003-2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_DISK_DEVICE_MANAGER_H +#define _FSSH_DISK_DEVICE_MANAGER_H + + +#include "fssh_disk_device_defs.h" +#include "fssh_drivers.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +// C API partition representation +// Fields marked [sys] are set by the system and are not to be changed by +// the disk system modules. +typedef struct fssh_partition_data { + fssh_partition_id id; // [sys] + fssh_off_t offset; + fssh_off_t size; + fssh_off_t content_size; + uint32_t block_size; + int32_t child_count; + int32_t index; // [sys] + uint32_t status; + uint32_t flags; + fssh_dev_t volume; // [sys] + void *mount_cookie; // [sys] + char *name; // max: B_OS_NAME_LENGTH + char *content_name; // + char *type; // + const char *content_type; // [sys] + char *parameters; + char *content_parameters; + void *cookie; + void *content_cookie; +} fssh_partition_data; + +// C API disk device representation +typedef struct fssh_disk_device_data { + fssh_partition_id id; // equal to that of the root partition + uint32_t flags; + char *path; + fssh_device_geometry geometry; +} fssh_disk_device_data; + +// C API partitionable space representation +typedef struct fssh_partitionable_space_data { + fssh_off_t offset; + fssh_off_t size; +} fssh_partitionable_space_data; + +// operations on partitions +enum { + FSSH_B_PARTITION_DEFRAGMENT, + FSSH_B_PARTITION_REPAIR, + FSSH_B_PARTITION_RESIZE, + FSSH_B_PARTITION_RESIZE_CHILD, + FSSH_B_PARTITION_MOVE, + FSSH_B_PARTITION_MOVE_CHILD, + FSSH_B_PARTITION_SET_NAME, + FSSH_B_PARTITION_SET_CONTENT_NAME, + FSSH_B_PARTITION_SET_TYPE, + FSSH_B_PARTITION_SET_PARAMETERS, + FSSH_B_PARTITION_SET_CONTENT_PARAMETERS, + FSSH_B_PARTITION_INITIALIZE, + FSSH_B_PARTITION_CREATE_CHILD, + FSSH_B_PARTITION_DELETE_CHILD, +}; + +// disk device job cancel status +enum { + FSSH_B_DISK_DEVICE_JOB_CONTINUE, + FSSH_B_DISK_DEVICE_JOB_CANCEL, + FSSH_B_DISK_DEVICE_JOB_REVERSE, +}; + +// disk device locking +fssh_disk_device_data* fssh_write_lock_disk_device( + fssh_partition_id partitionID); +void fssh_write_unlock_disk_device( + fssh_partition_id partitionID); +fssh_disk_device_data* fssh_read_lock_disk_device( + fssh_partition_id partitionID); +void fssh_read_unlock_disk_device( + fssh_partition_id partitionID); + // parameter is the ID of any partition on the device + +// getting disk devices/partitions by path +// (no locking required) +int32_t fssh_find_disk_device(const char *path); +int32_t fssh_find_partition(const char *path); + +// disk device/partition read access +// (read lock required) +fssh_disk_device_data* fssh_get_disk_device(fssh_partition_id partitionID); +fssh_partition_data* fssh_get_partition(fssh_partition_id partitionID); +fssh_partition_data* fssh_get_parent_partition( + fssh_partition_id partitionID); +fssh_partition_data* fssh_get_child_partition(fssh_partition_id partitionID, + int32_t index); + +// partition write access +// (write lock required) +fssh_partition_data* fssh_create_child_partition( + fssh_partition_id partitionID, int32_t index, + fssh_partition_id childID); + // childID is an optional input parameter -- -1 to be ignored +bool fssh_delete_partition(fssh_partition_id partitionID); +void fssh_partition_modified(fssh_partition_id partitionID); + // tells the disk device manager, that the parition has been modified + +// disk systems +fssh_disk_system_id fssh_find_disk_system(const char *name); + +// jobs +bool fssh_update_disk_device_job_progress(fssh_disk_job_id jobID, + float progress); +bool fssh_update_disk_device_job_extra_progress(fssh_disk_job_id jobID, + const char *info); +bool fssh_set_disk_device_job_error_message(fssh_disk_job_id jobID, + const char *message); +uint32_t fssh_update_disk_device_job_interrupt_properties( + fssh_disk_job_id jobID, uint32_t interruptProperties); + // returns one of B_DISK_DEVICE_JOB_{CONTINUE,CANCEL,REVERSE} + +#ifdef __cplusplus +} +#endif + +#endif // _FSSH_DISK_DEVICE_MANAGER_H diff --git a/headers/private/fs_shell/fssh_drivers.h b/headers/private/fs_shell/fssh_drivers.h new file mode 100644 index 0000000000..a4d25bbd08 --- /dev/null +++ b/headers/private/fs_shell/fssh_drivers.h @@ -0,0 +1,217 @@ +#ifndef _FSSH_DRIVERS_DRIVERS_H +#define _FSSH_DRIVERS_DRIVERS_H + +#include "fssh_defs.h" +#include "fssh_fs_interface.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- + these hooks are how the kernel accesses the device +--- */ + +typedef fssh_status_t (*fssh_device_open_hook) (const char *name, + uint32_t flags, void **cookie); +typedef fssh_status_t (*fssh_device_close_hook) (void *cookie); +typedef fssh_status_t (*fssh_device_free_hook) (void *cookie); +typedef fssh_status_t (*fssh_device_control_hook) (void *cookie, uint32_t op, + void *data, fssh_size_t len); +typedef fssh_status_t (*fssh_device_read_hook) (void *cookie, + fssh_off_t position, void *data, + fssh_size_t *numBytes); +typedef fssh_status_t (*fssh_device_write_hook) (void *cookie, + fssh_off_t position, const void *data, + fssh_size_t *numBytes); +typedef fssh_status_t (*fssh_device_select_hook) (void *cookie, uint8_t event, + uint32_t ref, fssh_selectsync *sync); +typedef fssh_status_t (*fssh_device_deselect_hook) (void *cookie, uint8_t event, + fssh_selectsync *sync); +typedef fssh_status_t (*fssh_device_read_pages_hook)(void *cookie, + fssh_off_t position, const fssh_iovec *vec, + fssh_size_t count, fssh_size_t *_numBytes); +typedef fssh_status_t (*fssh_device_write_pages_hook) (void *cookie, + fssh_off_t position, const fssh_iovec *vec, + fssh_size_t count, fssh_size_t *_numBytes); + +#define FSSH_B_CUR_DRIVER_API_VERSION 2 + +/* --- + the device_hooks structure is a descriptor for the device, giving its + entry points. +--- */ + +typedef struct { + fssh_device_open_hook open; /* called to open the device */ + fssh_device_close_hook close; /* called to close the device */ + fssh_device_free_hook free; /* called to free the cookie */ + fssh_device_control_hook control; /* called to control the device */ + fssh_device_read_hook read; /* reads from the device */ + fssh_device_write_hook write; /* writes to the device */ + fssh_device_select_hook select; /* start select */ + fssh_device_deselect_hook deselect; /* stop select */ + fssh_device_read_pages_hook read_pages; /* scatter-gather physical read from the device */ + fssh_device_write_pages_hook write_pages; /* scatter-gather physical write to the device */ +} fssh_device_hooks; + +fssh_status_t fssh_init_hardware(void); +const char **fssh_publish_devices(void); +fssh_device_hooks *fssh_find_device(const char *name); +fssh_status_t fssh_init_driver(void); +void fssh_uninit_driver(void); + +extern int32_t fssh_api_version; + +enum { + FSSH_B_GET_DEVICE_SIZE = 1, /* get # bytes */ + /* returns size_t in *data */ + + FSSH_B_SET_DEVICE_SIZE, /* set # bytes */ + /* passed size_t in *data */ + + FSSH_B_SET_NONBLOCKING_IO, /* set to non-blocking i/o */ + + FSSH_B_SET_BLOCKING_IO, /* set to blocking i/o */ + + FSSH_B_GET_READ_STATUS, /* check if can read w/o blocking */ + /* returns bool in *data */ + + FSSH_B_GET_WRITE_STATUS, /* check if can write w/o blocking */ + /* returns bool in *data */ + + FSSH_B_GET_GEOMETRY, /* get info about device geometry */ + /* returns struct geometry in *data */ + + FSSH_B_GET_DRIVER_FOR_DEVICE, /* get the path of the executable serving that device */ + + FSSH_B_GET_PARTITION_INFO, /* get info about a device partition */ + /* returns struct partition_info in *data */ + + FSSH_B_SET_PARTITION, /* create a user-defined partition */ + + FSSH_B_FORMAT_DEVICE, /* low-level device format */ + + FSSH_B_EJECT_DEVICE, /* eject the media if supported */ + + FSSH_B_GET_ICON, /* return device icon (see struct below) */ + + FSSH_B_GET_BIOS_GEOMETRY, /* get info about device geometry */ + /* as reported by the bios */ + /* returns struct geometry in *data */ + + FSSH_B_GET_MEDIA_STATUS, /* get status of media. */ + /* return fssh_status_t in *data: */ + /* B_NO_ERROR: media ready */ + /* B_DEV_NO_MEDIA: no media */ + /* B_DEV_NOT_READY: device not ready */ + /* B_DEV_MEDIA_CHANGED: media changed */ + /* since open or last B_GET_MEDIA_STATUS */ + /* B_DEV_MEDIA_CHANGE_REQUESTED: user */ + /* pressed button on drive */ + /* B_DEV_DOOR_OPEN: door open */ + + FSSH_B_LOAD_MEDIA, /* load the media if supported */ + + FSSH_B_GET_BIOS_DRIVE_ID, /* get bios id for this device */ + + FSSH_B_SET_UNINTERRUPTABLE_IO, /* prevent cntl-C from interrupting i/o */ + FSSH_B_SET_INTERRUPTABLE_IO, /* allow cntl-C to interrupt i/o */ + + FSSH_B_FLUSH_DRIVE_CACHE, /* flush drive cache */ + + FSSH_B_GET_PATH_FOR_DEVICE, /* get the absolute path of the device */ + + FSSH_B_GET_NEXT_OPEN_DEVICE = 1000, /* iterate through open devices */ + FSSH_B_ADD_FIXED_DRIVER, /* private */ + FSSH_B_REMOVE_FIXED_DRIVER, /* private */ + + FSSH_B_AUDIO_DRIVER_BASE = 8000, /* base for codes in audio_driver.h */ + FSSH_B_MIDI_DRIVER_BASE = 8100, /* base for codes in midi_driver.h */ + FSSH_B_JOYSTICK_DRIVER_BASE = 8200, /* base for codes in joystick.h */ + FSSH_B_GRAPHIC_DRIVER_BASE = 8300, /* base for codes in graphic_driver.h */ + + FSSH_B_DEVICE_OP_CODES_END = 9999 /* end of Be-defined contol id's */ +}; + +/* --- + geometry structure for the B_GET_GEOMETRY opcode +--- */ + +typedef struct { + uint32_t bytes_per_sector; /* sector size in bytes */ + uint32_t sectors_per_track; /* # sectors per track */ + uint32_t cylinder_count; /* # cylinders */ + uint32_t head_count; /* # heads */ + uint8_t device_type; /* type */ + bool removable; /* non-zero if removable */ + bool read_only; /* non-zero if read only */ + bool write_once; /* non-zero if write-once */ +} fssh_device_geometry; + + +/* --- + Be-defined device types returned by B_GET_GEOMETRY. Use these if it makes + sense for your device. +--- */ + +enum { + FSSH_B_DISK = 0, /* Hard disks, floppy disks, etc. */ + FSSH_B_TAPE, /* Tape drives */ + FSSH_B_PRINTER, /* Printers */ + FSSH_B_CPU, /* CPU devices */ + FSSH_B_WORM, /* Write-once, read-many devices */ + FSSH_B_CD, /* CD ROMS */ + FSSH_B_SCANNER, /* Scanners */ + FSSH_B_OPTICAL, /* Optical devices */ + FSSH_B_JUKEBOX, /* Jukeboxes */ + FSSH_B_NETWORK /* Network devices */ +}; + + +/* --- + partition_info structure used by B_GET_PARTITION_INFO and B_SET_PARTITION +--- */ + +typedef struct { + fssh_off_t offset; /* offset (in bytes) */ + fssh_off_t size; /* size (in bytes) */ + int32_t logical_block_size; /* logical block size of partition */ + int32_t session; /* id of session */ + int32_t partition; /* id of partition */ + char device[256]; /* path to the physical device */ +} fssh_partition_info; + +/* --- + driver_path structure returned by the B_GET_DRIVER_FOR_DEVICE +--- */ + +typedef char fssh_driver_path[256]; + + +/* --- + open_device_iterator structure used by the B_GET_NEXT_OPEN_DEVICE opcode +--- */ + +typedef struct { + uint32_t cookie; /* must be set to 0 before iterating */ + char device[256]; /* device path */ +} fssh_open_device_iterator; + + +/* --- + icon structure for the B_GET_ICON opcode +--- */ + +typedef struct { + int32_t icon_size; /* icon size requested */ + void *icon_data; /* where to put 'em (usually BBitmap->Bits()) */ +} fssh_device_icon; + + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_DRIVERS_DRIVERS_H */ diff --git a/headers/private/fs_shell/fssh_errno.h b/headers/private/fs_shell/fssh_errno.h new file mode 100644 index 0000000000..2bfa252304 --- /dev/null +++ b/headers/private/fs_shell/fssh_errno.h @@ -0,0 +1,25 @@ +#ifndef _FSSH_ERRNO_H +#define _FSSH_ERRNO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fssh_errors.h" + +#define FSSH_ENOERR 0 +#define FSSH_EOK FSSH_ENOERR /* some code assumes EOK exists */ + +extern int *_fssh_errnop(void); +#define fssh_errno (*(_fssh_errnop())) + +extern int fssh_get_errno(void); +extern void fssh_set_errno(int error); + + +#ifdef __cplusplus +} /* "C" */ +#endif + +#endif /* _FSSH_ERRNO_H */ diff --git a/headers/private/fs_shell/fssh_errors.h b/headers/private/fs_shell/fssh_errors.h new file mode 100644 index 0000000000..79043d9ad3 --- /dev/null +++ b/headers/private/fs_shell/fssh_errors.h @@ -0,0 +1,283 @@ +/* + * Copyright 2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_ERRORS_H +#define _FSSH_ERRORS_H + + +#include + + +/* Error baselines */ +#define FSSH_B_GENERAL_ERROR_BASE LONG_MIN +#define FSSH_B_OS_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x1000 +#define FSSH_B_APP_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x2000 +#define FSSH_B_INTERFACE_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x3000 +#define FSSH_B_MEDIA_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x4000 /* - 0x41ff */ +#define FSSH_B_TRANSLATION_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x4800 /* - 0x48ff */ +#define FSSH_B_MIDI_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x5000 +#define FSSH_B_STORAGE_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x6000 +#define FSSH_B_POSIX_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x7000 +#define FSSH_B_MAIL_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x8000 +#define FSSH_B_PRINT_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0x9000 +#define FSSH_B_DEVICE_ERROR_BASE FSSH_B_GENERAL_ERROR_BASE + 0xa000 + +/* Developer-defined errors start at (B_ERRORS_END+1) */ +#define FSSH_B_ERRORS_END (FSSH_B_GENERAL_ERROR_BASE + 0xffff) + +/* General Errors */ +enum { + FSSH_B_NO_MEMORY = FSSH_B_GENERAL_ERROR_BASE, + FSSH_B_IO_ERROR, + FSSH_B_PERMISSION_DENIED, + FSSH_B_BAD_INDEX, + FSSH_B_BAD_TYPE, + FSSH_B_BAD_VALUE, + FSSH_B_MISMATCHED_VALUES, + FSSH_B_NAME_NOT_FOUND, + FSSH_B_NAME_IN_USE, + FSSH_B_TIMED_OUT, + FSSH_B_INTERRUPTED, + FSSH_B_WOULD_BLOCK, + FSSH_B_CANCELED, + FSSH_B_NO_INIT, + FSSH_B_BUSY, + FSSH_B_NOT_ALLOWED, + FSSH_B_BAD_DATA, + FSSH_B_DONT_DO_THAT, + + FSSH_B_ERROR = -1, + FSSH_B_OK = 0, + FSSH_B_NO_ERROR = 0 +}; + +/* Kernel Kit Errors */ +enum { + FSSH_B_BAD_SEM_ID = FSSH_B_OS_ERROR_BASE, + FSSH_B_NO_MORE_SEMS, + + FSSH_B_BAD_THREAD_ID = FSSH_B_OS_ERROR_BASE + 0x100, + FSSH_B_NO_MORE_THREADS, + FSSH_B_BAD_THREAD_STATE, + FSSH_B_BAD_TEAM_ID, + FSSH_B_NO_MORE_TEAMS, + + FSSH_B_BAD_PORT_ID = FSSH_B_OS_ERROR_BASE + 0x200, + FSSH_B_NO_MORE_PORTS, + + FSSH_B_BAD_IMAGE_ID = FSSH_B_OS_ERROR_BASE + 0x300, + FSSH_B_BAD_ADDRESS, + FSSH_B_NOT_AN_EXECUTABLE, + FSSH_B_MISSING_LIBRARY, + FSSH_B_MISSING_SYMBOL, + + FSSH_B_DEBUGGER_ALREADY_INSTALLED = FSSH_B_OS_ERROR_BASE + 0x400 +}; + +/* Application Kit Errors */ +enum { + FSSH_B_BAD_REPLY = FSSH_B_APP_ERROR_BASE, + FSSH_B_DUPLICATE_REPLY, + FSSH_B_MESSAGE_TO_SELF, + FSSH_B_BAD_HANDLER, + FSSH_B_ALREADY_RUNNING, + FSSH_B_LAUNCH_FAILED, + FSSH_B_AMBIGUOUS_APP_LAUNCH, + FSSH_B_UNKNOWN_MIME_TYPE, + FSSH_B_BAD_SCRIPT_SYNTAX, + FSSH_B_LAUNCH_FAILED_NO_RESOLVE_LINK, + FSSH_B_LAUNCH_FAILED_EXECUTABLE, + FSSH_B_LAUNCH_FAILED_APP_NOT_FOUND, + FSSH_B_LAUNCH_FAILED_APP_IN_TRASH, + FSSH_B_LAUNCH_FAILED_NO_PREFERRED_APP, + FSSH_B_LAUNCH_FAILED_FILES_APP_NOT_FOUND, + FSSH_B_BAD_MIME_SNIFFER_RULE, + FSSH_B_NOT_A_MESSAGE, + FSSH_B_SHUTDOWN_CANCELLED, + FSSH_B_SHUTTING_DOWN +}; + +/* Storage Kit/File System Errors */ +enum { + FSSH_B_FILE_ERROR = FSSH_B_STORAGE_ERROR_BASE, + FSSH_B_FILE_NOT_FOUND, /* deprecated: use FSSH_B_ENTRY_NOT_FOUND instead */ + FSSH_B_FILE_EXISTS, + FSSH_B_ENTRY_NOT_FOUND, + FSSH_B_NAME_TOO_LONG, + FSSH_B_NOT_A_DIRECTORY, + FSSH_B_DIRECTORY_NOT_EMPTY, + FSSH_B_DEVICE_FULL, + FSSH_B_READ_ONLY_DEVICE, + FSSH_B_IS_A_DIRECTORY, + FSSH_B_NO_MORE_FDS, + FSSH_B_CROSS_DEVICE_LINK, + FSSH_B_LINK_LIMIT, + FSSH_B_BUSTED_PIPE, + FSSH_B_UNSUPPORTED, + FSSH_B_PARTITION_TOO_SMALL +}; + +/* POSIX Errors */ +#define FSSH_E2BIG (FSSH_B_POSIX_ERROR_BASE + 1) +#define FSSH_ECHILD (FSSH_B_POSIX_ERROR_BASE + 2) +#define FSSH_EDEADLK (FSSH_B_POSIX_ERROR_BASE + 3) +#define FSSH_EFBIG (FSSH_B_POSIX_ERROR_BASE + 4) +#define FSSH_EMLINK (FSSH_B_POSIX_ERROR_BASE + 5) +#define FSSH_ENFILE (FSSH_B_POSIX_ERROR_BASE + 6) +#define FSSH_ENODEV (FSSH_B_POSIX_ERROR_BASE + 7) +#define FSSH_ENOLCK (FSSH_B_POSIX_ERROR_BASE + 8) +#define FSSH_ENOSYS (FSSH_B_POSIX_ERROR_BASE + 9) +#define FSSH_ENOTTY (FSSH_B_POSIX_ERROR_BASE + 10) +#define FSSH_ENXIO (FSSH_B_POSIX_ERROR_BASE + 11) +#define FSSH_ESPIPE (FSSH_B_POSIX_ERROR_BASE + 12) +#define FSSH_ESRCH (FSSH_B_POSIX_ERROR_BASE + 13) +#define FSSH_EFPOS (FSSH_B_POSIX_ERROR_BASE + 14) +#define FSSH_ESIGPARM (FSSH_B_POSIX_ERROR_BASE + 15) +#define FSSH_EDOM (FSSH_B_POSIX_ERROR_BASE + 16) +#define FSSH_ERANGE (FSSH_B_POSIX_ERROR_BASE + 17) +#define FSSH_EPROTOTYPE (FSSH_B_POSIX_ERROR_BASE + 18) +#define FSSH_EPROTONOSUPPORT (FSSH_B_POSIX_ERROR_BASE + 19) +#define FSSH_EPFNOSUPPORT (FSSH_B_POSIX_ERROR_BASE + 20) +#define FSSH_EAFNOSUPPORT (FSSH_B_POSIX_ERROR_BASE + 21) +#define FSSH_EADDRINUSE (FSSH_B_POSIX_ERROR_BASE + 22) +#define FSSH_EADDRNOTAVAIL (FSSH_B_POSIX_ERROR_BASE + 23) +#define FSSH_ENETDOWN (FSSH_B_POSIX_ERROR_BASE + 24) +#define FSSH_ENETUNREACH (FSSH_B_POSIX_ERROR_BASE + 25) +#define FSSH_ENETRESET (FSSH_B_POSIX_ERROR_BASE + 26) +#define FSSH_ECONNABORTED (FSSH_B_POSIX_ERROR_BASE + 27) +#define FSSH_ECONNRESET (FSSH_B_POSIX_ERROR_BASE + 28) +#define FSSH_EISCONN (FSSH_B_POSIX_ERROR_BASE + 29) +#define FSSH_ENOTCONN (FSSH_B_POSIX_ERROR_BASE + 30) +#define FSSH_ESHUTDOWN (FSSH_B_POSIX_ERROR_BASE + 31) +#define FSSH_ECONNREFUSED (FSSH_B_POSIX_ERROR_BASE + 32) +#define FSSH_EHOSTUNREACH (FSSH_B_POSIX_ERROR_BASE + 33) +#define FSSH_ENOPROTOOPT (FSSH_B_POSIX_ERROR_BASE + 34) +#define FSSH_ENOBUFS (FSSH_B_POSIX_ERROR_BASE + 35) +#define FSSH_EINPROGRESS (FSSH_B_POSIX_ERROR_BASE + 36) +#define FSSH_EALREADY (FSSH_B_POSIX_ERROR_BASE + 37) +#define FSSH_EILSEQ (FSSH_B_POSIX_ERROR_BASE + 38) +#define FSSH_ENOMSG (FSSH_B_POSIX_ERROR_BASE + 39) +#define FSSH_ESTALE (FSSH_B_POSIX_ERROR_BASE + 40) +#define FSSH_EOVERFLOW (FSSH_B_POSIX_ERROR_BASE + 41) +#define FSSH_EMSGSIZE (FSSH_B_POSIX_ERROR_BASE + 42) +#define FSSH_EOPNOTSUPP (FSSH_B_POSIX_ERROR_BASE + 43) +#define FSSH_ENOTSOCK (FSSH_B_POSIX_ERROR_BASE + 44) +#define FSSH_EHOSTDOWN (FSSH_B_POSIX_ERROR_BASE + 45) +#define FSSH_EBADMSG (FSSH_B_POSIX_ERROR_BASE + 46) +#define FSSH_ECANCELED (FSSH_B_POSIX_ERROR_BASE + 47) +#define FSSH_EDESTADDRREQ (FSSH_B_POSIX_ERROR_BASE + 48) +#define FSSH_EDQUOT (FSSH_B_POSIX_ERROR_BASE + 49) +#define FSSH_EIDRM (FSSH_B_POSIX_ERROR_BASE + 50) +#define FSSH_EMULTIHOP (FSSH_B_POSIX_ERROR_BASE + 51) +#define FSSH_ENODATA (FSSH_B_POSIX_ERROR_BASE + 52) +#define FSSH_ENOLINK (FSSH_B_POSIX_ERROR_BASE + 53) +#define FSSH_ENOSR (FSSH_B_POSIX_ERROR_BASE + 54) +#define FSSH_ENOSTR (FSSH_B_POSIX_ERROR_BASE + 55) +#define FSSH_ENOTSUP (FSSH_B_POSIX_ERROR_BASE + 56) +#define FSSH_EPROTO (FSSH_B_POSIX_ERROR_BASE + 57) +#define FSSH_ETIME (FSSH_B_POSIX_ERROR_BASE + 58) +#define FSSH_ETXTBSY (FSSH_B_POSIX_ERROR_BASE + 59) + +/* POSIX errors that can be mapped to BeOS error codes */ +#define FSSH_ENOMEM FSSH_B_NO_MEMORY +#define FSSH_EACCES FSSH_B_PERMISSION_DENIED +#define FSSH_EINTR FSSH_B_INTERRUPTED +#define FSSH_EIO FSSH_B_IO_ERROR +#define FSSH_EBUSY FSSH_B_BUSY +#define FSSH_EFAULT FSSH_B_BAD_ADDRESS +#define FSSH_ETIMEDOUT FSSH_B_TIMED_OUT +#define FSSH_EAGAIN FSSH_B_WOULD_BLOCK /* SysV compatibility */ +#define FSSH_EWOULDBLOCK FSSH_B_WOULD_BLOCK /* BSD compatibility */ +#define FSSH_EBADF FSSH_B_FILE_ERROR +#define FSSH_EEXIST FSSH_B_FILE_EXISTS +#define FSSH_EINVAL FSSH_B_BAD_VALUE +#define FSSH_ENAMETOOLONG FSSH_B_NAME_TOO_LONG +#define FSSH_ENOENT FSSH_B_ENTRY_NOT_FOUND +#define FSSH_EPERM FSSH_B_NOT_ALLOWED +#define FSSH_ENOTDIR FSSH_B_NOT_A_DIRECTORY +#define FSSH_EISDIR FSSH_B_IS_A_DIRECTORY +#define FSSH_ENOTEMPTY FSSH_B_DIRECTORY_NOT_EMPTY +#define FSSH_ENOSPC FSSH_B_DEVICE_FULL +#define FSSH_EROFS FSSH_B_READ_ONLY_DEVICE +#define FSSH_EMFILE FSSH_B_NO_MORE_FDS +#define FSSH_EXDEV FSSH_B_CROSS_DEVICE_LINK +#define FSSH_ELOOP FSSH_B_LINK_LIMIT +#define FSSH_ENOEXEC FSSH_B_NOT_AN_EXECUTABLE +#define FSSH_EPIPE FSSH_B_BUSTED_PIPE + +/* new error codes that can be mapped to POSIX errors */ +#define FSSH_B_BUFFER_OVERFLOW FSSH_EOVERFLOW +#define FSSH_B_TOO_MANY_ARGS FSSH_E2BIG +#define FSSH_B_FILE_TOO_LARGE FSSH_EFBIG +#define FSSH_B_RESULT_NOT_REPRESENTABLE FSSH_ERANGE +#define FSSH_B_DEVICE_NOT_FOUND FSSH_ENODEV +#define FSSH_B_NOT_SUPPORTED FSSH_EOPNOTSUPP + +/* Media Kit Errors */ +enum { + FSSH_B_STREAM_NOT_FOUND = FSSH_B_MEDIA_ERROR_BASE, + FSSH_B_SERVER_NOT_FOUND, + FSSH_B_RESOURCE_NOT_FOUND, + FSSH_B_RESOURCE_UNAVAILABLE, + FSSH_B_BAD_SUBSCRIBER, + FSSH_B_SUBSCRIBER_NOT_ENTERED, + FSSH_B_BUFFER_NOT_AVAILABLE, + FSSH_B_LAST_BUFFER_ERROR +}; + +/* Mail Kit Errors */ +enum { + FSSH_B_MAIL_NO_DAEMON = FSSH_B_MAIL_ERROR_BASE, + FSSH_B_MAIL_UNKNOWN_USER, + FSSH_B_MAIL_WRONG_PASSWORD, + FSSH_B_MAIL_UNKNOWN_HOST, + FSSH_B_MAIL_ACCESS_ERROR, + FSSH_B_MAIL_UNKNOWN_FIELD, + FSSH_B_MAIL_NO_RECIPIENT, + FSSH_B_MAIL_INVALID_MAIL +}; + +/* Printing Errors */ +enum { + FSSH_B_NO_PRINT_SERVER = FSSH_B_PRINT_ERROR_BASE +}; + +/* Device Kit Errors */ +enum { + FSSH_B_DEV_INVALID_IOCTL = FSSH_B_DEVICE_ERROR_BASE, + FSSH_B_DEV_NO_MEMORY, + FSSH_B_DEV_BAD_DRIVE_NUM, + FSSH_B_DEV_NO_MEDIA, + FSSH_B_DEV_UNREADABLE, + FSSH_B_DEV_FORMAT_ERROR, + FSSH_B_DEV_TIMEOUT, + FSSH_B_DEV_RECALIBRATE_ERROR, + FSSH_B_DEV_SEEK_ERROR, + FSSH_B_DEV_ID_ERROR, + FSSH_B_DEV_READ_ERROR, + FSSH_B_DEV_WRITE_ERROR, + FSSH_B_DEV_NOT_READY, + FSSH_B_DEV_MEDIA_CHANGED, + FSSH_B_DEV_MEDIA_CHANGE_REQUESTED, + FSSH_B_DEV_RESOURCE_CONFLICT, + FSSH_B_DEV_CONFIGURATION_ERROR, + FSSH_B_DEV_DISABLED_BY_USER, + FSSH_B_DEV_DOOR_OPEN, + + FSSH_B_DEV_INVALID_PIPE, + FSSH_B_DEV_CRC_ERROR, + FSSH_B_DEV_STALLED, + FSSH_B_DEV_BAD_PID, + FSSH_B_DEV_UNEXPECTED_PID, + FSSH_B_DEV_DATA_OVERRUN, + FSSH_B_DEV_DATA_UNDERRUN, + FSSH_B_DEV_FIFO_OVERRUN, + FSSH_B_DEV_FIFO_UNDERRUN, + FSSH_B_DEV_PENDING, + FSSH_B_DEV_MULTIPLE_ERRORS, + FSSH_B_DEV_TOO_LATE +}; + + +#endif /* _FSSH_ERRORS_H */ diff --git a/headers/private/fs_shell/fssh_fcntl.h b/headers/private/fs_shell/fssh_fcntl.h new file mode 100644 index 0000000000..0444eeb95e --- /dev/null +++ b/headers/private/fs_shell/fssh_fcntl.h @@ -0,0 +1,83 @@ +/* + * Copyright 2002-2007, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_FCNTL_H +#define _FSSH_FCNTL_H + + +#include "fssh_types.h" + + +/* commands that can be passed to fcntl() */ +#define FSSH_F_DUPFD 0x0001 /* duplicate fd */ +#define FSSH_F_GETFD 0x0002 /* get fd flags */ +#define FSSH_F_SETFD 0x0004 /* set fd flags */ +#define FSSH_F_GETFL 0x0008 /* get file status flags and access mode */ +#define FSSH_F_SETFL 0x0010 /* set file status flags */ +#define FSSH_F_GETLK 0x0020 /* get locking information */ +#define FSSH_F_SETLK 0x0080 /* set locking information */ +#define FSSH_F_SETLKW 0x0100 /* as above, but waits if blocked */ + +/* advisory locking types */ +#define FSSH_F_RDLCK 0x0040 /* read or shared lock */ +#define FSSH_F_UNLCK 0x0200 /* unlock */ +#define FSSH_F_WRLCK 0x0400 /* write or exclusive lock */ + +/* file descriptor flags for fcntl() */ +#define FSSH_FD_CLOEXEC 1 /* close on exec */ + +/* file access modes for open() */ +#define FSSH_O_RDONLY 0x0000 /* read only */ +#define FSSH_O_WRONLY 0x0001 /* write only */ +#define FSSH_O_RDWR 0x0002 /* read and write */ +#define FSSH_O_ACCMODE 0x0003 /* mask to get the access modes above */ +#define FSSH_O_RWMASK FSSH_O_ACCMODE + +/* flags for open() */ +#define FSSH_O_EXCL 0x0100 /* exclusive creat */ +#define FSSH_O_CREAT 0x0200 /* create and open file */ +#define FSSH_O_TRUNC 0x0400 /* open with truncation */ +#define FSSH_O_NOCTTY 0x1000 /* currently unsupported */ +#define FSSH_O_NOTRAVERSE 0x2000 /* do not traverse leaf link */ + +/* flags for open() and fcntl() */ +#define FSSH_O_CLOEXEC 0x00000040 /* close on exec */ +#define FSSH_O_NONBLOCK 0x00000080 /* non blocking io */ +#define FSSH_O_APPEND 0x00000800 /* to end of file */ +#define FSSH_O_TEXT 0x00004000 /* CR-LF translation */ +#define FSSH_O_BINARY 0x00008000 /* no translation */ +#define FSSH_O_SYNC 0x00010000 /* write synchronized I/O file integrity */ +#define FSSH_O_RSYNC 0x00020000 /* read synchronized I/O file integrity */ +#define FSSH_O_DSYNC 0x00040000 /* write synchronized I/O data integrity */ + +// TODO: currently not implemented additions: +#define FSSH_O_NOFOLLOW 0x00080000 + /* should we implement this? it's similar to O_NOTRAVERSE but will fail on symlinks */ +#define FSSH_O_NOCACHE 0x00100000 /* doesn't use the file system cache if possible */ +#define FSSH_O_DIRECT FSSH_O_NOCACHE +#define FSSH_O_MOUNT 0x00200000 /* for file systems */ +#define FSSH_O_TEMPORARY 0x00400000 /* used to avoid writing temporary files to disk */ +#define FSSH_O_SHLOCK 0x01000000 /* obtain shared lock */ +#define FSSH_O_EXLOCK 0x02000000 /* obtain exclusive lock */ + +#define FSSH_S_IREAD 0x0100 /* owner may read */ +#define FSSH_S_IWRITE 0x0080 /* owner may write */ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern int fssh_creat(const char *path, fssh_mode_t mode); +extern int fssh_open(const char *pathname, int oflags, ...); + /* the third argument is the permissions of the created file when O_CREAT + is passed in oflags */ + +extern int fssh_fcntl(int fd, int op, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FCNTL_H */ diff --git a/headers/private/fs_shell/fssh_fs_attr.h b/headers/private/fs_shell/fssh_fs_attr.h new file mode 100644 index 0000000000..84af792513 --- /dev/null +++ b/headers/private/fs_shell/fssh_fs_attr.h @@ -0,0 +1,50 @@ +/* File System attributes +** +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef _FSSH_FS_ATTR_H +#define _FSSH_FS_ATTR_H + + +#include "fssh_defs.h" +#include "fssh_dirent.h" + + +typedef struct fssh_attr_info { + uint32_t type; + fssh_off_t size; +} fssh_attr_info; + + +#ifdef __cplusplus +extern "C" { +#endif + +extern fssh_ssize_t fssh_fs_read_attr(int fd, const char *attribute, + uint32_t type, fssh_off_t pos, void *buffer, + fssh_size_t readBytes); +extern fssh_ssize_t fssh_fs_write_attr(int fd, const char *attribute, + uint32_t type, fssh_off_t pos, const void *buffer, + fssh_size_t readBytes); +extern int fssh_fs_remove_attr(int fd, const char *attribute); +extern int fssh_fs_stat_attr(int fd, const char *attribute, + struct fssh_attr_info *attrInfo); + +// ToDo: the following three functions are not part of the R5 API, and +// are only preliminary - they may change or be removed at any point +//extern int fssh_fs_open_attr(const char *path, const char *attribute, uint32_t type, int openMode); +extern int fssh_fs_open_attr(int fd, const char *attribute, + uint32_t type, int openMode); +extern int fssh_fs_close_attr(int fd); + +extern fssh_DIR *fssh_fs_open_attr_dir(const char *path); +extern fssh_DIR *fssh_fs_fopen_attr_dir(int fd); +extern int fssh_fs_close_attr_dir(fssh_DIR *dir); +extern struct fssh_dirent *fssh_fs_read_attr_dir(fssh_DIR *dir); +extern void fssh_fs_rewind_attr_dir(fssh_DIR *dir); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FS_ATTR_H */ diff --git a/headers/private/fs_shell/fssh_fs_cache.h b/headers/private/fs_shell/fssh_fs_cache.h new file mode 100644 index 0000000000..37cfb05796 --- /dev/null +++ b/headers/private/fs_shell/fssh_fs_cache.h @@ -0,0 +1,96 @@ +/* + * Copyright 2004-2007, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_FS_CACHE_H +#define _FSSH_FS_CACHE_H + +//! File System File and Block Caches + + +#include "fssh_fs_interface.h" + + +typedef void (*fssh_transaction_notification_hook)(int32_t id, void *data); + +#ifdef __cplusplus +extern "C" { +#endif + +/* transactions */ +extern int32_t fssh_cache_start_transaction(void *_cache); +extern fssh_status_t fssh_cache_sync_transaction(void *_cache, int32_t id); +extern fssh_status_t fssh_cache_end_transaction(void *_cache, int32_t id, + fssh_transaction_notification_hook hook, + void *data); +extern fssh_status_t fssh_cache_abort_transaction(void *_cache, int32_t id); +extern int32_t fssh_cache_detach_sub_transaction(void *_cache, + int32_t id, fssh_transaction_notification_hook hook, + void *data); +extern fssh_status_t fssh_cache_abort_sub_transaction(void *_cache, + int32_t id); +extern fssh_status_t fssh_cache_start_sub_transaction(void *_cache, + int32_t id); +extern fssh_status_t fssh_cache_next_block_in_transaction(void *_cache, + int32_t id, uint32_t *_cookie, + fssh_off_t *_blockNumber, void **_data, + void **_unchangedData); +extern int32_t fssh_cache_blocks_in_transaction(void *_cache, + int32_t id); +extern int32_t fssh_cache_blocks_in_sub_transaction(void *_cache, + int32_t id); + +/* block cache */ +extern void fssh_block_cache_delete(void *_cache, bool allowWrites); +extern void * fssh_block_cache_create(int fd, fssh_off_t numBlocks, + fssh_size_t blockSize, bool readOnly); +extern fssh_status_t fssh_block_cache_sync(void *_cache); +extern fssh_status_t fssh_block_cache_sync_etc(void *_cache, + fssh_off_t blockNumber, fssh_size_t numBlocks); +extern fssh_status_t fssh_block_cache_make_writable(void *_cache, + fssh_off_t blockNumber, int32_t transaction); +extern void * fssh_block_cache_get_writable_etc(void *_cache, + fssh_off_t blockNumber, fssh_off_t base, + fssh_off_t length, int32_t transaction); +extern void * fssh_block_cache_get_writable(void *_cache, + fssh_off_t blockNumber, int32_t transaction); +extern void * fssh_block_cache_get_empty(void *_cache, + fssh_off_t blockNumber, int32_t transaction); +extern const void * fssh_block_cache_get_etc(void *_cache, + fssh_off_t blockNumber, fssh_off_t base, + fssh_off_t length); +extern const void * fssh_block_cache_get(void *_cache, + fssh_off_t blockNumber); +extern fssh_status_t fssh_block_cache_set_dirty(void *_cache, + fssh_off_t blockNumber, bool isDirty, + int32_t transaction); +extern void fssh_block_cache_put(void *_cache, + fssh_off_t blockNumber); + +/* file cache */ +extern void * fssh_file_cache_create(fssh_mount_id mountID, + fssh_vnode_id vnodeID, fssh_off_t size, int fd); +extern void fssh_file_cache_delete(void *_cacheRef); +extern fssh_status_t fssh_file_cache_set_size(void *_cacheRef, + fssh_off_t size); +extern fssh_status_t fssh_file_cache_sync(void *_cache); +extern fssh_status_t fssh_file_cache_invalidate_file_map(void *_cacheRef, + fssh_off_t offset, fssh_off_t size); + +extern fssh_status_t fssh_file_cache_read_pages(void *_cacheRef, + fssh_off_t offset, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes); +extern fssh_status_t fssh_file_cache_write_pages(void *_cacheRef, + fssh_off_t offset, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes); +extern fssh_status_t fssh_file_cache_read(void *_cacheRef, fssh_off_t offset, + void *bufferBase, fssh_size_t *_size); +extern fssh_status_t fssh_file_cache_write(void *_cacheRef, + fssh_off_t offset, const void *buffer, + fssh_size_t *_size); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FS_CACHE_H */ diff --git a/headers/private/fs_shell/fssh_fs_index.h b/headers/private/fs_shell/fssh_fs_index.h new file mode 100644 index 0000000000..5a681e0515 --- /dev/null +++ b/headers/private/fs_shell/fssh_fs_index.h @@ -0,0 +1,42 @@ +/* File System indices +** +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef _FSSH_FS_INDEX_H +#define _FSSH_FS_INDEX_H + + +#include "fssh_defs.h" +#include "fssh_dirent.h" + + +typedef struct fssh_index_info { + uint32_t type; + fssh_off_t size; + fssh_time_t modification_time; + fssh_time_t creation_time; + fssh_uid_t uid; + fssh_gid_t gid; +} fssh_index_info; + + +#ifdef __cplusplus +extern "C" { +#endif + +extern int fssh_fs_create_index(fssh_dev_t device, const char *name, + uint32_t type, uint32_t flags); +extern int fssh_fs_remove_index(fssh_dev_t device, const char *name); +extern int fssh_fs_stat_index(fssh_dev_t device, const char *name, + struct fssh_index_info *indexInfo); + +extern fssh_DIR *fssh_fs_open_index_dir(fssh_dev_t device); +extern int fssh_fs_close_index_dir(fssh_DIR *indexDirectory); +extern struct fssh_dirent *fssh_fs_read_index_dir(fssh_DIR *indexDirectory); +extern void fssh_fs_rewind_index_dir(fssh_DIR *indexDirectory); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FS_INDEX_H */ diff --git a/headers/private/fs_shell/fssh_fs_info.h b/headers/private/fs_shell/fssh_fs_info.h new file mode 100644 index 0000000000..66a6b522db --- /dev/null +++ b/headers/private/fs_shell/fssh_fs_info.h @@ -0,0 +1,53 @@ +/* General File System informations/capabilities +** +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef _FSSH_FS_INFO_H +#define _FSSH_FS_INFO_H + +#include "fssh_defs.h" +#include "fssh_os.h" + + +/* fs_info.flags */ +#define FSSH_B_FS_IS_READONLY 0x00000001 +#define FSSH_B_FS_IS_REMOVABLE 0x00000002 +#define FSSH_B_FS_IS_PERSISTENT 0x00000004 +#define FSSH_B_FS_IS_SHARED 0x00000008 +#define FSSH_B_FS_HAS_MIME 0x00010000 +#define FSSH_B_FS_HAS_ATTR 0x00020000 +#define FSSH_B_FS_HAS_QUERY 0x00040000 +// those additions are preliminary and may be removed +#define FSSH_B_FS_HAS_SELF_HEALING_LINKS 0x00080000 +#define FSSH_B_FS_HAS_ALIASES 0x00100000 +#define FSSH_B_FS_SUPPORTS_NODE_MONITORING 0x00200000 + +typedef struct fssh_fs_info { + fssh_dev_t dev; /* volume dev_t */ + fssh_ino_t root; /* root ino_t */ + uint32_t flags; /* flags (see above) */ + fssh_off_t block_size; /* fundamental block size */ + fssh_off_t io_size; /* optimal i/o size */ + fssh_off_t total_blocks; /* total number of blocks */ + fssh_off_t free_blocks; /* number of free blocks */ + fssh_off_t total_nodes; /* total number of nodes */ + fssh_off_t free_nodes; /* number of free nodes */ + char device_name[128]; /* device holding fs */ + char volume_name[FSSH_B_FILE_NAME_LENGTH]; /* volume name */ + char fsh_name[FSSH_B_OS_NAME_LENGTH]; /* name of fs handler */ +} fssh_fs_info; + + +#ifdef __cplusplus +extern "C" { +#endif + +extern fssh_dev_t dev_for_path(const char *path); +extern fssh_dev_t next_dev(int32_t *pos); +extern int fs_stat_dev(fssh_dev_t dev, fssh_fs_info *info); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FS_INFO_H */ diff --git a/headers/private/fs_shell/fssh_fs_interface.h b/headers/private/fs_shell/fssh_fs_interface.h new file mode 100644 index 0000000000..9457ab09e4 --- /dev/null +++ b/headers/private/fs_shell/fssh_fs_interface.h @@ -0,0 +1,284 @@ +/* File System Interface Layer Definition + * + * Copyright 2004-2006, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_FS_INTERFACE_H +#define _FSSH_FS_INTERFACE_H + + +#include "fssh_module.h" +#include "fssh_os.h" + + +struct fssh_dirent; +struct fssh_fs_info; +struct fssh_iovec; +struct fssh_partition_data; +struct fssh_selectsync; +struct fssh_stat; + +typedef fssh_dev_t fssh_mount_id; +typedef fssh_ino_t fssh_vnode_id; + +/* the file system's private data structures */ +typedef void *fssh_fs_volume; +typedef void *fssh_fs_cookie; +typedef void *fssh_fs_vnode; + +/* passed to write_stat() */ +enum fssh_write_stat_mask { + FSSH_FS_WRITE_STAT_MODE = 0x0001, + FSSH_FS_WRITE_STAT_UID = 0x0002, + FSSH_FS_WRITE_STAT_GID = 0x0004, + FSSH_FS_WRITE_STAT_SIZE = 0x0008, + FSSH_FS_WRITE_STAT_ATIME = 0x0010, + FSSH_FS_WRITE_STAT_MTIME = 0x0020, + FSSH_FS_WRITE_STAT_CRTIME = 0x0040 +}; + +/* passed to write_fs_info() */ +#define FSSH_FS_WRITE_FSINFO_NAME 0x0001 + +struct fssh_file_io_vec { + fssh_off_t offset; + fssh_off_t length; +}; + +#define FSSH_B_CURRENT_FS_API_VERSION "/v1" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct fssh_file_system_module_info { + struct fssh_module_info info; + const char *pretty_name; + + /* scanning (the device is write locked) */ + float (*identify_partition)(int fd, fssh_partition_data *partition, + void **cookie); + fssh_status_t (*scan_partition)(int fd, fssh_partition_data *partition, + void *cookie); + void (*free_identify_partition_cookie)(fssh_partition_data *partition, + void *cookie); + void (*free_partition_content_cookie)(fssh_partition_data *partition); + + /* general operations */ + fssh_status_t (*mount)(fssh_mount_id id, const char *device, uint32_t flags, + const char *args, fssh_fs_volume *_fs, + fssh_vnode_id *_rootVnodeID); + fssh_status_t (*unmount)(fssh_fs_volume fs); + + fssh_status_t (*read_fs_info)(fssh_fs_volume fs, struct fssh_fs_info *info); + fssh_status_t (*write_fs_info)(fssh_fs_volume fs, + const struct fssh_fs_info *info, uint32_t mask); + fssh_status_t (*sync)(fssh_fs_volume fs); + + /* vnode operations */ + fssh_status_t (*lookup)(fssh_fs_volume fs, fssh_fs_vnode dir, + const char *name, fssh_vnode_id *_id, int *_type); + fssh_status_t (*get_vnode_name)(fssh_fs_volume fs, fssh_fs_vnode vnode, + char *buffer, fssh_size_t bufferSize); + + fssh_status_t (*get_vnode)(fssh_fs_volume fs, fssh_vnode_id id, + fssh_fs_vnode *_vnode, bool reenter); + fssh_status_t (*put_vnode)(fssh_fs_volume fs, fssh_fs_vnode vnode, + bool reenter); + fssh_status_t (*remove_vnode)(fssh_fs_volume fs, fssh_fs_vnode vnode, + bool reenter); + + /* VM file access */ + bool (*can_page)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*read_pages)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, fssh_off_t pos, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes, bool reenter); + fssh_status_t (*write_pages)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, fssh_off_t pos, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes, bool reenter); + + /* cache file access */ + fssh_status_t (*get_file_map)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_off_t offset, fssh_size_t size, + struct fssh_file_io_vec *vecs, fssh_size_t *_count); + + /* common operations */ + fssh_status_t (*ioctl)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, fssh_ulong op, void *buffer, + fssh_size_t length); + fssh_status_t (*set_flags)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, int flags); + fssh_status_t (*select)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, uint8_t event, uint32_t ref, + fssh_selectsync *sync); + fssh_status_t (*deselect)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, uint8_t event, fssh_selectsync *sync); + fssh_status_t (*fsync)(fssh_fs_volume fs, fssh_fs_vnode vnode); + + fssh_status_t (*read_symlink)(fssh_fs_volume fs, fssh_fs_vnode link, + char *buffer, fssh_size_t *_bufferSize); + fssh_status_t (*create_symlink)(fssh_fs_volume fs, fssh_fs_vnode dir, + const char *name, const char *path, int mode); + + fssh_status_t (*link)(fssh_fs_volume fs, fssh_fs_vnode dir, + const char *name, fssh_fs_vnode vnode); + fssh_status_t (*unlink)(fssh_fs_volume fs, fssh_fs_vnode dir, + const char *name); + fssh_status_t (*rename)(fssh_fs_volume fs, fssh_fs_vnode fromDir, + const char *fromName, fssh_fs_vnode toDir, const char *toName); + + fssh_status_t (*access)(fssh_fs_volume fs, fssh_fs_vnode vnode, int mode); + fssh_status_t (*read_stat)(fssh_fs_volume fs, fssh_fs_vnode vnode, + struct fssh_stat *stat); + fssh_status_t (*write_stat)(fssh_fs_volume fs, fssh_fs_vnode vnode, + const struct fssh_stat *stat, uint32_t statMask); + + /* file operations */ + fssh_status_t (*create)(fssh_fs_volume fs, fssh_fs_vnode dir, + const char *name, int openMode, int perms, + fssh_fs_cookie *_cookie, fssh_vnode_id *_newVnodeID); + fssh_status_t (*open)(fssh_fs_volume fs, fssh_fs_vnode vnode, int openMode, + fssh_fs_cookie *_cookie); + fssh_status_t (*close)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*free_cookie)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*read)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, fssh_off_t pos, void *buffer, + fssh_size_t *length); + fssh_status_t (*write)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, fssh_off_t pos, const void *buffer, + fssh_size_t *length); + + /* directory operations */ + fssh_status_t (*create_dir)(fssh_fs_volume fs, fssh_fs_vnode parent, + const char *name, int perms, fssh_vnode_id *_newVnodeID); + fssh_status_t (*remove_dir)(fssh_fs_volume fs, fssh_fs_vnode parent, + const char *name); + fssh_status_t (*open_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie *_cookie); + fssh_status_t (*close_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*free_dir_cookie)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*read_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, struct fssh_dirent *buffer, + fssh_size_t bufferSize, uint32_t *_num); + fssh_status_t (*rewind_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + + /* attribute directory operations */ + fssh_status_t (*open_attr_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie *_cookie); + fssh_status_t (*close_attr_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*free_attr_dir_cookie)(fssh_fs_volume fs, + fssh_fs_vnode vnode, fssh_fs_cookie cookie); + fssh_status_t (*read_attr_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, struct fssh_dirent *buffer, + fssh_size_t bufferSize, uint32_t *_num); + fssh_status_t (*rewind_attr_dir)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + + /* attribute operations */ + fssh_status_t (*create_attr)(fssh_fs_volume fs, fssh_fs_vnode vnode, + const char *name, uint32_t type, int openMode, + fssh_fs_cookie *_cookie); + fssh_status_t (*open_attr)(fssh_fs_volume fs, fssh_fs_vnode vnode, + const char *name, int openMode, fssh_fs_cookie *_cookie); + fssh_status_t (*close_attr)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*free_attr_cookie)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie); + fssh_status_t (*read_attr)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, fssh_off_t pos, void *buffer, + fssh_size_t *length); + fssh_status_t (*write_attr)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, fssh_off_t pos, const void *buffer, + fssh_size_t *length); + + fssh_status_t (*read_attr_stat)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, struct fssh_stat *stat); + fssh_status_t (*write_attr_stat)(fssh_fs_volume fs, fssh_fs_vnode vnode, + fssh_fs_cookie cookie, const struct fssh_stat *stat, + int statMask); + fssh_status_t (*rename_attr)(fssh_fs_volume fs, fssh_fs_vnode fromVnode, + const char *fromName, fssh_fs_vnode toVnode, + const char *toName); + fssh_status_t (*remove_attr)(fssh_fs_volume fs, fssh_fs_vnode vnode, + const char *name); + + /* index directory & index operations */ + fssh_status_t (*open_index_dir)(fssh_fs_volume fs, fssh_fs_cookie *cookie); + fssh_status_t (*close_index_dir)(fssh_fs_volume fs, fssh_fs_cookie cookie); + fssh_status_t (*free_index_dir_cookie)(fssh_fs_volume fs, + fssh_fs_cookie cookie); + fssh_status_t (*read_index_dir)(fssh_fs_volume fs, fssh_fs_cookie cookie, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_num); + fssh_status_t (*rewind_index_dir)(fssh_fs_volume fs, fssh_fs_cookie cookie); + + fssh_status_t (*create_index)(fssh_fs_volume fs, const char *name, + uint32_t type, uint32_t flags); + fssh_status_t (*remove_index)(fssh_fs_volume fs, const char *name); + fssh_status_t (*read_index_stat)(fssh_fs_volume fs, const char *name, + struct fssh_stat *stat); + + /* query operations */ + fssh_status_t (*open_query)(fssh_fs_volume fs, const char *query, + uint32_t flags, fssh_port_id port, uint32_t token, + fssh_fs_cookie *_cookie); + fssh_status_t (*close_query)(fssh_fs_volume fs, fssh_fs_cookie cookie); + fssh_status_t (*free_query_cookie)(fssh_fs_volume fs, + fssh_fs_cookie cookie); + fssh_status_t (*read_query)(fssh_fs_volume fs, fssh_fs_cookie cookie, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_num); + fssh_status_t (*rewind_query)(fssh_fs_volume fs, fssh_fs_cookie cookie); +} fssh_file_system_module_info; + + +/* file system add-ons only prototypes */ +extern fssh_status_t fssh_new_vnode(fssh_mount_id mountID, + fssh_vnode_id vnodeID, fssh_fs_vnode privateNode); +extern fssh_status_t fssh_publish_vnode(fssh_mount_id mountID, + fssh_vnode_id vnodeID, fssh_fs_vnode privateNode); +extern fssh_status_t fssh_get_vnode(fssh_mount_id mountID, + fssh_vnode_id vnodeID, fssh_fs_vnode *_privateNode); +extern fssh_status_t fssh_put_vnode(fssh_mount_id mountID, + fssh_vnode_id vnodeID); +extern fssh_status_t fssh_remove_vnode(fssh_mount_id mountID, + fssh_vnode_id vnodeID); +extern fssh_status_t fssh_unremove_vnode(fssh_mount_id mountID, + fssh_vnode_id vnodeID); +extern fssh_status_t fssh_get_vnode_removed(fssh_mount_id mountID, + fssh_vnode_id vnodeID, bool* removed); + +extern fssh_status_t fssh_notify_entry_created(fssh_mount_id device, + fssh_vnode_id directory, const char *name, fssh_vnode_id node); +extern fssh_status_t fssh_notify_entry_removed(fssh_mount_id device, + fssh_vnode_id directory, const char *name, fssh_vnode_id node); +extern fssh_status_t fssh_notify_entry_moved(fssh_mount_id device, + fssh_vnode_id fromDirectory, const char *fromName, + fssh_vnode_id toDirectory, const char *toName, + fssh_vnode_id node); +extern fssh_status_t fssh_notify_stat_changed(fssh_mount_id device, + fssh_vnode_id node, uint32_t statFields); +extern fssh_status_t fssh_notify_attribute_changed(fssh_mount_id device, + fssh_vnode_id node, const char *attribute, int32_t cause); + +extern fssh_status_t fssh_notify_query_entry_created(fssh_port_id port, + int32_t token, fssh_mount_id device, + fssh_vnode_id directory, const char *name, + fssh_vnode_id node); +extern fssh_status_t fssh_notify_query_entry_removed(fssh_port_id port, + int32_t token, fssh_mount_id device, + fssh_vnode_id directory, const char *name, + fssh_vnode_id node); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FS_INTERFACE_H */ diff --git a/headers/private/fs_shell/fssh_fs_query.h b/headers/private/fs_shell/fssh_fs_query.h new file mode 100644 index 0000000000..aa543ef52d --- /dev/null +++ b/headers/private/fs_shell/fssh_fs_query.h @@ -0,0 +1,43 @@ +/* File System attribute queries +** +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef _FSSH_FS_QUERY_H +#define _FSSH_FS_QUERY_H + + +#include "fssh_os.h" +#include "fssh_dirent.h" + + +/* Flags for fs_open_[live_]query() */ + +#define FSSH_B_LIVE_QUERY 0x00000001 + // Note, if you specify B_LIVE_QUERY, you have to use fs_open_live_query(); + // it will be ignored in fs_open_query(). +#define FSSH_B_QUERY_NON_INDEXED 0x00000002 + // Only enable this feature for non time-critical things, it might + // take a long time to proceed. + // Also, not every file system might support this feature. + + +#ifdef __cplusplus +extern "C" { +#endif + +extern fssh_DIR* fssh_fs_open_query(fssh_dev_t device, + const char *query, uint32_t flags); +extern fssh_DIR* fssh_fs_open_live_query(fssh_dev_t device, + const char *query, uint32_t flags, + fssh_port_id port, int32_t token); +extern int fssh_fs_close_query(fssh_DIR *d); +extern struct fssh_dirent* fssh_fs_read_query(fssh_DIR *d); + +extern fssh_status_t fssh_get_path_for_dirent(struct fssh_dirent *dent, + char *buf, fssh_size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FS_QUERY_H */ diff --git a/headers/private/fs_shell/fssh_fs_volume.h b/headers/private/fs_shell/fssh_fs_volume.h new file mode 100644 index 0000000000..8be1a76aec --- /dev/null +++ b/headers/private/fs_shell/fssh_fs_volume.h @@ -0,0 +1,34 @@ +/* File System volume functions + * + * Copyright 2004-2005, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_FS_VOLUME_H +#define _FSSH_FS_VOLUME_H + +#include "fssh_os.h" + + +/* mount flags */ +#define FSSH_B_MOUNT_READ_ONLY 1 +#define FSSH_B_MOUNT_VIRTUAL_DEVICE 2 + +/* unmount flags */ +#define FSSH_B_FORCE_UNMOUNT 1 + + +#ifdef __cplusplus +extern "C" { +#endif + +extern fssh_dev_t fssh_fs_mount_volume(const char *where, + const char *device, const char *filesystem, + uint32_t flags, const char *parameters); +extern fssh_status_t fssh_fs_unmount_volume(const char *path, + uint32_t flags); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_FS_VOLUME_H */ diff --git a/headers/private/fs_shell/fssh_kernel_export.h b/headers/private/fs_shell/fssh_kernel_export.h new file mode 100644 index 0000000000..17a173e8b2 --- /dev/null +++ b/headers/private/fs_shell/fssh_kernel_export.h @@ -0,0 +1,51 @@ +#ifndef _FSSH_KERNEL_EXPORT_H +#define _FSSH_KERNEL_EXPORT_H + + +#include "fssh_defs.h" +#include "fssh_os.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/*-------------------------------------------------------------*/ +/* kernel threads */ + +extern fssh_thread_id fssh_spawn_kernel_thread(fssh_thread_func function, + const char *threadName, int32_t priority, + void *arg); + + +/*-------------------------------------------------------------*/ +/* primitive kernel debugging facilities */ + +extern void fssh_dprintf(const char *format, ...) /* just like printf */ + __attribute__ ((format (__printf__, 1, 2))); +extern void fssh_kprintf(const char *fmt, ...) /* only for debugger cmds */ + __attribute__ ((format (__printf__, 1, 2))); + +extern void fssh_dump_block(const char *buffer, int size, + const char *prefix); + +extern void fssh_panic(const char *format, ...) + __attribute__ ((format (__printf__, 1, 2))); + +extern void fssh_kernel_debugger(const char *message); /* enter kernel debugger */ +extern uint32_t fssh_parse_expression(const char *string); /* utility for debugger cmds */ + +typedef int (*fssh_debugger_command_hook)(int argc, char **argv); + +extern int fssh_add_debugger_command(char *name, + fssh_debugger_command_hook hook, char *help); +extern int fssh_remove_debugger_command(char *name, + fssh_debugger_command_hook hook); + + +#ifdef __cplusplus +} +#endif + + +#endif // _FSSH_KERNEL_EXPORT_H diff --git a/headers/private/fs_shell/fssh_kernel_priv.h b/headers/private/fs_shell/fssh_kernel_priv.h new file mode 100644 index 0000000000..0892102864 --- /dev/null +++ b/headers/private/fs_shell/fssh_kernel_priv.h @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ +#ifndef _FSSH_KERNEL_H +#define _FSSH_KERNEL_H + + +#include "fssh_os.h" + + +/* Passed in buffers from user-space shouldn't point into the kernel */ +#if 0 +#define IS_USER_ADDRESS(x) \ + ((addr_t)(x) < KERNEL_BASE || (addr_t)(x) > KERNEL_TOP) + +#define IS_KERNEL_ADDRESS(x) \ + ((addr_t)(x) >= KERNEL_BASE && (addr_t)(x) <= KERNEL_TOP) +#endif // 0 + +#define DEBUG_KERNEL_STACKS + // Note, debugging kernel stacks doesn't really work yet. Since the + // interrupt will also try to use the stack on a page fault, all + // you get is a double fault. + // At least, you then know that the stack overflows in this case :) + +/** Size of the kernel stack */ +#ifndef DEBUG_KERNEL_STACKS +# define KERNEL_STACK_SIZE (B_PAGE_SIZE * 2) // 8 kB +#else +# define KERNEL_STACK_SIZE (B_PAGE_SIZE * 3) // 8 kB + one guard page +#endif +#define KERNEL_STACK_GUARD_PAGES 1 + +/** Size of the stack given to teams in user space */ +#define USER_MAIN_THREAD_STACK_SIZE (16 * 1024 * 1024) // 16 MB +#define USER_STACK_SIZE (256 * 1024) // 256 kB +#define USER_STACK_GUARD_PAGES 4 // 16 kB + +/** Size of the environmental variables space for a process */ +#define ENV_SIZE (B_PAGE_SIZE * 8) + + +#define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) +#define ROUNDOWN(a, b) (((a) / (b)) * (b)) + + +#define CHECK_BIT(a, b) ((a) & (1 << (b))) +#define SET_BIT(a, b) ((a) | (1 << (b))) +#define CLEAR_BIT(a, b) ((a) & (~(1 << (b)))) + + +#endif /* _FSSH_KERNEL_H */ diff --git a/headers/private/fs_shell/fssh_module.h b/headers/private/fs_shell/fssh_module.h new file mode 100644 index 0000000000..e475f61177 --- /dev/null +++ b/headers/private/fs_shell/fssh_module.h @@ -0,0 +1,62 @@ +/* Modules Definitions +** +** Distributed under the terms of the OpenBeOS License. +*/ + +#ifndef _FSSH_MODULE_H +#define _FSSH_MODULE_H + + +#include "fssh_os.h" + + +/* Every module exports a list of module_info structures. + * It defines the interface of the module and the name + * that is used to access the interface. + */ + +typedef struct fssh_module_info { + const char *name; + uint32_t flags; + fssh_status_t (*std_ops)(int32_t, ...); +} fssh_module_info; + +/* module standard operations */ +#define FSSH_B_MODULE_INIT 1 +#define FSSH_B_MODULE_UNINIT 2 + +/* module flags */ +#define FSSH_B_KEEP_LOADED 0x00000001 + + +/* Use the module_dependency structure to let the + * kernel automatically load modules yet depend on + * before B_MODULE_INIT is called. + */ + +typedef struct fssh_module_dependency { + const char *name; + fssh_module_info **info; +} fssh_module_dependency; + + +#if 0 + +#ifdef __cplusplus +extern "C" { +#endif + +extern status_t get_module(const char *path, module_info **_info); +extern status_t put_module(const char *path); +extern status_t get_next_loaded_module_name(uint32 *cookie, char *buffer, size_t *_bufferSize); +extern void *open_module_list(const char *prefix); +extern status_t close_module_list(void *cookie); +extern status_t read_next_module_name(void *cookie, char *buffer, size_t *_bufferSize); + +#ifdef __cplusplus +} +#endif + +#endif // 0 + +#endif /* _FSSH_MODULE_H */ diff --git a/headers/private/fs_shell/fssh_node_monitor.h b/headers/private/fs_shell/fssh_node_monitor.h new file mode 100644 index 0000000000..0754106896 --- /dev/null +++ b/headers/private/fs_shell/fssh_node_monitor.h @@ -0,0 +1,67 @@ +#ifndef _FSSH_NODE_MONITOR_H +#define _FSSH_NODE_MONITOR_H +/* Node monitor calls for kernel add-ons +** +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "fssh_defs.h" + + +/* Flags for the watch_node() call. + * + * Note that B_WATCH_MOUNT is NOT included in B_WATCH_ALL. + * You may prefer to use BVolumeRoster for volume watching. + */ + +enum { + FSSH_B_STOP_WATCHING = 0x0000, + FSSH_B_WATCH_NAME = 0x0001, + FSSH_B_WATCH_STAT = 0x0002, + FSSH_B_WATCH_ATTR = 0x0004, + FSSH_B_WATCH_DIRECTORY = 0x0008, + FSSH_B_WATCH_ALL = 0x000f, + + FSSH_B_WATCH_MOUNT = 0x0010 +}; + + +/* The "opcode" field of the B_NODE_MONITOR notification message you get. + * + * The presence and meaning of the other fields in that message specifying what + * exactly caused the notification depend on this value. + */ + +#define FSSH_B_ENTRY_CREATED 1 +#define FSSH_B_ENTRY_REMOVED 2 +#define FSSH_B_ENTRY_MOVED 3 +#define FSSH_B_STAT_CHANGED 4 +#define FSSH_B_ATTR_CHANGED 5 +#define FSSH_B_DEVICE_MOUNTED 6 +#define FSSH_B_DEVICE_UNMOUNTED 7 + + +// More specific info in the "cause" field of B_ATTR_CHANGED notification +// messages. (Haiku only) +#define FSSH_B_ATTR_CREATED 1 +#define FSSH_B_ATTR_REMOVED 2 +// FSSH_B_ATTR_CHANGED is reused + + +// More specific info in the "fields" field of B_STAT_CHANGED notification +// messages, specifying what parts of the stat data have actually been +// changed. (Haiku only) +enum { + FSSH_B_STAT_MODE = 0x01, + FSSH_B_STAT_UID = 0x02, + FSSH_B_STAT_GID = 0x04, + FSSH_B_STAT_SIZE = 0x08, + FSSH_B_STAT_ACCESS_TIME = 0x10, + FSSH_B_STAT_MODIFICATION_TIME = 0x20, + FSSH_B_STAT_CREATION_TIME = 0x40, + FSSH_B_STAT_CHANGE_TIME = 0x80, +}; + + +#endif /* _FSSH_NODE_MONITOR_H */ diff --git a/headers/private/fs_shell/fssh_os.h b/headers/private/fs_shell/fssh_os.h new file mode 100644 index 0000000000..74a3d32ea2 --- /dev/null +++ b/headers/private/fs_shell/fssh_os.h @@ -0,0 +1,211 @@ +/* Kernel specific structures and functions + * + * Copyright 2004-2006, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef _FSSH_SEM_H +#define _FSSH_SEM_H + +#include "fssh_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*-------------------------------------------------------------*/ +/* System constants */ + +#define FSSH_B_OS_NAME_LENGTH 32 +#define FSSH_B_PAGE_SIZE 4096 +#define FSSH_B_INFINITE_TIMEOUT (9223372036854775807LL) + + +/*-------------------------------------------------------------*/ +/* Types */ + +typedef int32_t fssh_area_id; +typedef int32_t fssh_port_id; +typedef int32_t fssh_sem_id; +typedef int32_t fssh_team_id; +typedef int32_t fssh_thread_id; + + +/*-------------------------------------------------------------*/ +/* Semaphores */ + +typedef struct fssh_sem_info { + fssh_sem_id sem; + fssh_team_id team; + char name[FSSH_B_OS_NAME_LENGTH]; + int32_t count; + fssh_thread_id latest_holder; +} fssh_sem_info; + +/* semaphore flags */ +enum { + FSSH_B_CAN_INTERRUPT = 0x01, // acquisition of the semaphore can be + // interrupted (system use only) + FSSH_B_CHECK_PERMISSION = 0x04, // ownership will be checked (system use + // only) + FSSH_B_KILL_CAN_INTERRUPT = 0x20, // acquisition of the semaphore can be + // interrupted by SIGKILL[THR], even + // if not B_CAN_INTERRUPT (system use + // only) + + /* release_sem_etc() only flags */ + FSSH_B_DO_NOT_RESCHEDULE = 0x02, // thread is not rescheduled + FSSH_B_RELEASE_ALL = 0x08, // all waiting threads will be woken up, + // count will be zeroed + FSSH_B_RELEASE_IF_WAITING_ONLY = 0x10 // release count only if there are any + // threads waiting +}; + +extern fssh_sem_id fssh_create_sem(int32_t count, const char *name); +extern fssh_status_t fssh_delete_sem(fssh_sem_id id); +extern fssh_status_t fssh_acquire_sem(fssh_sem_id id); +extern fssh_status_t fssh_acquire_sem_etc(fssh_sem_id id, int32_t count, + uint32_t flags, fssh_bigtime_t timeout); +extern fssh_status_t fssh_release_sem(fssh_sem_id id); +extern fssh_status_t fssh_release_sem_etc(fssh_sem_id id, int32_t count, + uint32_t flags); +extern fssh_status_t fssh_get_sem_count(fssh_sem_id id, + int32_t *threadCount); +extern fssh_status_t fssh_set_sem_owner(fssh_sem_id id, fssh_team_id team); + +/* system private, use the macros instead */ +extern fssh_status_t _fssh_get_sem_info(fssh_sem_id id, + struct fssh_sem_info *info, fssh_size_t infoSize); +extern fssh_status_t _fssh_get_next_sem_info(fssh_team_id team, + int32_t *cookie, struct fssh_sem_info *info, + fssh_size_t infoSize); + +#define fssh_get_sem_info(sem, info) \ + _fssh_get_sem_info((sem), (info), sizeof(*(info))) + +#define fssh_get_next_sem_info(team, cookie, info) \ + _fssh_get_next_sem_info((team), (cookie), (info), sizeof(*(info))) + + + +enum { + FSSH_B_TIMEOUT = 8, /* relative timeout */ + FSSH_B_RELATIVE_TIMEOUT = 8, /* fails after a relative timeout with B_WOULD_BLOCK */ + FSSH_B_ABSOLUTE_TIMEOUT = 16 /* fails after an absolute timeout with B_WOULD BLOCK */ +}; + + +/*-------------------------------------------------------------*/ +/* Teams */ + +#define FSSH_B_CURRENT_TEAM 0 +#define FSSH_B_SYSTEM_TEAM 1 + + +/*-------------------------------------------------------------*/ +/* Threads */ + +typedef enum { + FSSH_B_THREAD_RUNNING = 1, + FSSH_B_THREAD_READY, + FSSH_B_THREAD_RECEIVING, + FSSH_B_THREAD_ASLEEP, + FSSH_B_THREAD_SUSPENDED, + FSSH_B_THREAD_WAITING +} fssh_thread_state; + +typedef struct { + fssh_thread_id thread; + fssh_team_id team; + char name[FSSH_B_OS_NAME_LENGTH]; + fssh_thread_state state; + int32_t priority; + fssh_sem_id sem; + fssh_bigtime_t user_time; + fssh_bigtime_t kernel_time; + void *stack_base; + void *stack_end; +} fssh_thread_info; + +#define FSSH_B_IDLE_PRIORITY 0 +#define FSSH_B_LOWEST_ACTIVE_PRIORITY 1 +#define FSSH_B_LOW_PRIORITY 5 +#define FSSH_B_NORMAL_PRIORITY 10 +#define FSSH_B_DISPLAY_PRIORITY 15 +#define FSSH_B_URGENT_DISPLAY_PRIORITY 20 +#define FSSH_B_REAL_TIME_DISPLAY_PRIORITY 100 +#define FSSH_B_URGENT_PRIORITY 110 +#define FSSH_B_REAL_TIME_PRIORITY 120 + +#define FSSH_B_FIRST_REAL_TIME_PRIORITY B_REAL_TIME_DISPLAY_PRIORITY +#define FSSH_B_MIN_PRIORITY B_IDLE_PRIORITY +#define FSSH_B_MAX_PRIORITY B_REAL_TIME_PRIORITY + +#define FSSH_B_SYSTEM_TIMEBASE 0 + +typedef fssh_status_t (*fssh_thread_func)(void *); +#define fssh_thread_entry fssh_thread_func + /* thread_entry is for backward compatibility only! Use thread_func */ + +extern fssh_thread_id fssh_spawn_thread(fssh_thread_func, const char *name, + int32_t priority, void *data); +extern fssh_status_t fssh_kill_thread(fssh_thread_id thread); +extern fssh_status_t fssh_resume_thread(fssh_thread_id thread); +extern fssh_status_t fssh_suspend_thread(fssh_thread_id thread); + +extern fssh_status_t fssh_rename_thread(fssh_thread_id thread, + const char *newName); +extern fssh_status_t fssh_set_thread_priority (fssh_thread_id thread, + int32_t newPriority); +extern void fssh_exit_thread(fssh_status_t status); +extern fssh_status_t fssh_wait_for_thread (fssh_thread_id thread, + fssh_status_t *threadReturnValue); +extern fssh_status_t fssh_on_exit_thread(void (*callback)(void *), + void *data); + +extern fssh_thread_id fssh_find_thread(const char *name); + +extern fssh_status_t fssh_send_data(fssh_thread_id thread, int32_t code, + const void *buffer, + fssh_size_t bufferSize); +extern int32_t fssh_receive_data(fssh_thread_id *sender, void *buffer, + fssh_size_t bufferSize); +extern bool fssh_has_data(fssh_thread_id thread); + +extern fssh_status_t fssh_snooze(fssh_bigtime_t amount); +extern fssh_status_t fssh_snooze_etc(fssh_bigtime_t amount, int timeBase, + uint32_t flags); +extern fssh_status_t fssh_snooze_until(fssh_bigtime_t time, int timeBase); + +/* system private, use macros instead */ +extern fssh_status_t _fssh_get_thread_info(fssh_thread_id id, + fssh_thread_info *info, fssh_size_t size); +extern fssh_status_t _fssh_get_next_thread_info(fssh_team_id team, + int32_t *cookie, fssh_thread_info *info, + fssh_size_t size); + +#define fssh_get_thread_info(id, info) \ + _fssh_get_thread_info((id), (info), sizeof(*(info))) + +#define fssh_get_next_thread_info(team, cookie, info) \ + _fssh_get_next_thread_info((team), (cookie), (info), sizeof(*(info))) + + +/*-------------------------------------------------------------*/ +/* Time */ + +extern uint32_t fssh_real_time_clock(void); +extern void fssh_set_real_time_clock(uint32_t secs_since_jan1_1970); +extern fssh_bigtime_t fssh_real_time_clock_usecs(void); +extern fssh_status_t fssh_set_timezone(char *timezone); +extern fssh_bigtime_t fssh_system_time(void); /* time since booting in microseconds */ + + +#ifdef __cplusplus +} +#endif + + +#endif // _FSSH_TYPES_H diff --git a/headers/private/fs_shell/fssh_stat.h b/headers/private/fs_shell/fssh_stat.h new file mode 100644 index 0000000000..b85f5dea7d --- /dev/null +++ b/headers/private/fs_shell/fssh_stat.h @@ -0,0 +1,114 @@ +/* + * Copyright 2002-2007, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_SYS_STAT_H_ +#define _FSSH_SYS_STAT_H_ + + +#include "fssh_defs.h" +#include "fssh_time.h" + + +struct fssh_stat { + fssh_dev_t fssh_st_dev; /* "device" that this file resides on */ + fssh_ino_t fssh_st_ino; /* this file's inode #, unique per device */ + fssh_mode_t fssh_st_mode; /* mode bits (rwx for user, group, etc) */ + fssh_nlink_t fssh_st_nlink; /* number of hard links to this file */ + fssh_uid_t fssh_st_uid; /* user id of the owner of this file */ + fssh_gid_t fssh_st_gid; /* group id of the owner of this file */ + fssh_off_t fssh_st_size; /* size in bytes of this file */ + fssh_dev_t fssh_st_rdev; /* device type (not used) */ + fssh_size_t fssh_st_blksize; /* preferred block size for i/o */ + fssh_time_t fssh_st_atime; /* last access time */ + fssh_time_t fssh_st_mtime; /* last modification time */ + fssh_time_t fssh_st_ctime; /* last change time, not creation time */ + fssh_time_t fssh_st_crtime; /* creation time */ + + // Haiku extensions: + // TODO: we might also define special types for files and TTYs + // TODO: we should find another solution for this, as BStatable::GetStat() + // can only retrieve the R5 stat structure + unsigned int fssh_st_type; /* attribute/index type */ +}; + +/* extended file types */ +#define FSSH_S_ATTR_DIR 01000000000 /* attribute directory */ +#define FSSH_S_ATTR 02000000000 /* attribute */ +#define FSSH_S_INDEX_DIR 04000000000 /* index (or index directory) */ +#define FSSH_S_STR_INDEX 00100000000 /* string index */ +#define FSSH_S_INT_INDEX 00200000000 /* int32 index */ +#define FSSH_S_UINT_INDEX 00400000000 /* uint32 index */ +#define FSSH_S_LONG_LONG_INDEX 00010000000 /* int64 index */ +#define FSSH_S_ULONG_LONG_INDEX 00020000000 /* uint64 index */ +#define FSSH_S_FLOAT_INDEX 00040000000 /* float index */ +#define FSSH_S_DOUBLE_INDEX 00001000000 /* double index */ +#define FSSH_S_ALLOW_DUPS 00002000000 /* allow duplicate entries (currently unused) */ + +/* link types */ +#define FSSH_S_LINK_SELF_HEALING 00001000000 /* link will be updated if you move its target */ +#define FSSH_S_LINK_AUTO_DELETE 00002000000 /* link will be deleted if you delete its target */ + +/* standard file types */ +#define FSSH_S_IFMT 00000170000 /* type of file */ +#define FSSH_S_IFLNK 00000120000 /* symbolic link */ +#define FSSH_S_IFREG 00000100000 /* regular */ +#define FSSH_S_IFBLK 00000060000 /* block special */ +#define FSSH_S_IFDIR 00000040000 /* directory */ +#define FSSH_S_IFCHR 00000020000 /* character special */ +#define FSSH_S_IFIFO 00000010000 /* fifo */ + +#define FSSH_S_ISREG(mode) (((mode) & FSSH_S_IFMT) == FSSH_S_IFREG) +#define FSSH_S_ISLNK(mode) (((mode) & FSSH_S_IFMT) == FSSH_S_IFLNK) +#define FSSH_S_ISBLK(mode) (((mode) & FSSH_S_IFMT) == FSSH_S_IFBLK) +#define FSSH_S_ISDIR(mode) (((mode) & FSSH_S_IFMT) == FSSH_S_IFDIR) +#define FSSH_S_ISCHR(mode) (((mode) & FSSH_S_IFMT) == FSSH_S_IFCHR) +#define FSSH_S_ISFIFO(mode) (((mode) & FSSH_S_IFMT) == FSSH_S_IFIFO) +#define FSSH_S_ISINDEX(mode) (((mode) & FSSH_S_INDEX_DIR) == FSSH_S_INDEX_DIR) + +#define FSSH_S_IUMSK 07777 /* user settable bits */ + +#define FSSH_S_ISUID 04000 /* set user id on execution */ +#define FSSH_S_ISGID 02000 /* set group id on execution */ + +#define FSSH_S_ISVTX 01000 /* save swapped text even after use */ + +#define FSSH_S_IRWXU 00700 /* read, write, execute: owner */ +#define FSSH_S_IRUSR 00400 /* read permission: owner */ +#define FSSH_S_IWUSR 00200 /* write permission: owner */ +#define FSSH_S_IXUSR 00100 /* execute permission: owner */ +#define FSSH_S_IRWXG 00070 /* read, write, execute: group */ +#define FSSH_S_IRGRP 00040 /* read permission: group */ +#define FSSH_S_IWGRP 00020 /* write permission: group */ +#define FSSH_S_IXGRP 00010 /* execute permission: group */ +#define FSSH_S_IRWXO 00007 /* read, write, execute: other */ +#define FSSH_S_IROTH 00004 /* read permission: other */ +#define FSSH_S_IWOTH 00002 /* write permission: other */ +#define FSSH_S_IXOTH 00001 /* execute permission: other */ + +#define FSSH_ACCESSPERMS (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) +#define FSSH_ALLPERMS (FSSH_S_ISUID | FSSH_S_ISGID | FSSH_S_ISTXT \ + | FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) +#define FSSH_DEFFILEMODE (FSSH_S_IRUSR | FSSH_S_IWUSR | FSSH_S_IRGRP \ + | FSSH_S_IWGRP | FSSH_S_IROTH | FSSH_S_IWOTH) + /* default file mode, everyone can read/write */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int fssh_chmod(const char *path, fssh_mode_t mode); +extern int fssh_fchmod(int fd, fssh_mode_t mode); +extern int fssh_mkdir(const char *path, fssh_mode_t mode); +extern int fssh_mkfifo(const char *path, fssh_mode_t mode); +extern fssh_mode_t fssh_umask(fssh_mode_t cmask); + +extern int fssh_stat(const char *path, struct fssh_stat *st); +extern int fssh_fstat(int fd, struct fssh_stat *st); +extern int fssh_lstat(const char *path, struct fssh_stat *st); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_SYS_STAT_H_ */ diff --git a/headers/private/fs_shell/fssh_stdio.h b/headers/private/fs_shell/fssh_stdio.h new file mode 100644 index 0000000000..e931ee7e77 --- /dev/null +++ b/headers/private/fs_shell/fssh_stdio.h @@ -0,0 +1,40 @@ +#ifndef _FSSH_STDIO_H_ +#define _FSSH_STDIO_H_ + +#include + +#include "fssh_defs.h" + + +#ifdef FSSH_EOF +# undef FSSH_EOF +#endif +#define FSSH_EOF -1 + + +#ifdef __cplusplus +extern "C" { +#endif + +/* file operations */ +extern int fssh_remove(const char *name); +extern int fssh_rename(const char *from, const char *to); + +/* formatted I/O */ +extern int fssh_sprintf(char *string, char const *format, ...) + __attribute__ ((format (__printf__, 2, 3))); +extern int fssh_snprintf(char *string, fssh_size_t size, + char const *format, ...) + __attribute__ ((format (__printf__, 3, 4))); +extern int fssh_vsprintf(char *string, char const *format, va_list ap); +extern int fssh_vsnprintf(char *string, fssh_size_t size, + char const *format, va_list ap); + +extern int fssh_sscanf(char const *str, char const *format, ...); +extern int fssh_vsscanf(char const *str, char const *format, va_list ap); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_STDIO_H_ */ diff --git a/headers/private/fs_shell/fssh_string.h b/headers/private/fs_shell/fssh_string.h new file mode 100644 index 0000000000..d8aa70c898 --- /dev/null +++ b/headers/private/fs_shell/fssh_string.h @@ -0,0 +1,91 @@ +/* + * Copyright 2004-2007, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_STRING_H +#define _FSSH_STRING_H + + +#include "fssh_defs.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* memXXX() functions */ +extern void *fssh_memchr(const void *source, int value, fssh_size_t length); +extern int fssh_memcmp(const void *buffer1, const void *buffer2, + fssh_size_t length); +extern void *fssh_memcpy(void *dest, const void *source, + fssh_size_t length); +extern void *fssh_memccpy(void *dest, const void *source, int stopByte, + fssh_size_t length); +extern void *fssh_memmove(void *dest, const void *source, + fssh_size_t length); +extern void *fssh_memset(void *dest, int value, fssh_size_t length); + +/* string functions */ +extern char *fssh_strcpy(char *dest, const char *source); +extern char *fssh_strncpy(char *dest, const char *source, + fssh_size_t length); +extern char *fssh_strcat(char *dest, const char *source); +extern char *fssh_strncat(char *dest, const char *source, + fssh_size_t length); + +extern fssh_size_t fssh_strlen(const char *string); +extern int fssh_strcmp(const char *string1, const char *string2); +extern int fssh_strncmp(const char *string1, const char *string2, + fssh_size_t length); + +extern char *fssh_strchr(const char *string, int character); +extern char *fssh_strrchr(const char *string, int character); +extern char *fssh_strstr(const char *string, const char *searchString); + +extern char *fssh_strchrnul(const char *string, int character); + // this is a GNU extension + +extern char *fssh_strpbrk(const char *string, const char *set); +extern char *fssh_strtok(char *string, const char *set); +extern char *fssh_strtok_r(char *string, const char *set, + char **savePointer); +extern fssh_size_t fssh_strspn(const char *string, const char *set); +extern fssh_size_t fssh_strcspn(const char *string, const char *set); + +extern int fssh_strcoll(const char *string1, const char *string2); +extern fssh_size_t fssh_strxfrm(char *string1, const char *string2, + fssh_size_t length); + +extern char *fssh_strerror(int errorCode); +extern int fssh_strerror_r(int errorCode, char *buffer, + fssh_size_t bufferSize); + +/* non-standard string functions */ +extern int fssh_strcasecmp(const char *string1, const char *string2); +extern int fssh_strncasecmp(const char *string1, const char *string2, + fssh_size_t length); + +extern char *fssh_strcasestr(const char *string, const char *searchString); + +extern char *fssh_strdup(const char *string); +extern char *fssh_stpcpy(char *dest, const char *source); +extern const char *fssh_strtcopy(char *dest, const char *source); + +extern fssh_size_t fssh_strlcat(char *dest, const char *source, + fssh_size_t length); +extern fssh_size_t fssh_strlcpy(char *dest, const char *source, + fssh_size_t length); + +extern fssh_size_t fssh_strnlen(const char *string, fssh_size_t count); + +extern int fssh_ffs(int i); +extern char *fssh_index(const char *s, int c); +extern char *fssh_rindex(char const *s, int c); + + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_STRING_H */ diff --git a/headers/private/fs_shell/fssh_time.h b/headers/private/fs_shell/fssh_time.h new file mode 100644 index 0000000000..3e1479b1c9 --- /dev/null +++ b/headers/private/fs_shell/fssh_time.h @@ -0,0 +1,86 @@ +/* + * Copyright 2005-2007, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_TIME_H_ +#define _FSSH_TIME_H_ + + +#include "fssh_defs.h" + + +typedef long fssh_clock_t; +typedef long fssh_time_t; +typedef long fssh_suseconds_t; +typedef unsigned long fssh_useconds_t; + +#define FSSH_CLOCKS_PER_SEC 1000 +#define FSSH_CLK_TCK FSSH_CLOCKS_PER_SEC + +#define FSSH_MAX_TIMESTR 70 + /* maximum length of a string returned by asctime(), and ctime() */ + +struct fssh_timespec { + fssh_time_t tv_sec; /* seconds */ + long tv_nsec; /* and nanoseconds */ +}; + +struct fssh_itimerspec { + struct fssh_timespec it_interval; + struct fssh_timespec it_value; +}; + +struct fssh_tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; /* day of month (1 to 31) */ + int tm_mon; /* months since January (0 to 11) */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday (0 to 6, Sunday = 0, ...) */ + int tm_yday; /* days since January 1 (0 to 365) */ + int tm_isdst; /* daylight savings time (0 == no, >0 == yes, <0 == has to be calculated */ + int tm_gmtoff; /* timezone offset to GMT */ + char *tm_zone; /* timezone name */ +}; + + +/* special timezone support */ +extern char *fssh_tzname[2]; +extern int fssh_daylight; +extern long fssh_timezone; + + +#ifdef __cplusplus +extern "C" { +#endif + +extern fssh_clock_t fssh_clock(void); +extern double fssh_difftime(fssh_time_t time1, fssh_time_t time2); +extern fssh_time_t fssh_mktime(struct fssh_tm *tm); +extern fssh_time_t fssh_time(fssh_time_t *timer); +extern char *fssh_asctime(const struct fssh_tm *tm); +extern char *fssh_asctime_r(const struct fssh_tm *timep, + char *buffer); +extern char *fssh_ctime(const fssh_time_t *timer); +extern char *fssh_ctime_r(const fssh_time_t *timer, char *buffer); +extern struct fssh_tm *fssh_gmtime(const fssh_time_t *timer); +extern struct fssh_tm *fssh_gmtime_r(const fssh_time_t *timer, + struct fssh_tm *tm); +extern struct fssh_tm *fssh_localtime(const fssh_time_t *timer); +extern struct fssh_tm *fssh_localtime_r(const fssh_time_t *timer, + struct fssh_tm *tm); +extern fssh_size_t fssh_strftime(char *buffer, fssh_size_t maxSize, + const char *format, const struct fssh_tm *tm); +extern char *fssh_strptime(const char *buf, const char *format, + struct fssh_tm *tm); + +/* special timezone support */ +extern void fssh_tzset(void); +extern int fssh_stime(const fssh_time_t *t); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_TIME_H_ */ diff --git a/headers/private/fs_shell/fssh_type_constants.h b/headers/private/fs_shell/fssh_type_constants.h new file mode 100644 index 0000000000..6d93de93a4 --- /dev/null +++ b/headers/private/fs_shell/fssh_type_constants.h @@ -0,0 +1,78 @@ +/* + * Copyright 2005-2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Author: + * Erik Jaesler (erik@cgsoftware.com) + */ +#ifndef _FSSH_TYPE_CONSTANTS_H +#define _FSSH_TYPE_CONSTANTS_H + + +#include "fssh_defs.h" + + +enum { + FSSH_B_ANY_TYPE = 'ANYT', + FSSH_B_ATOM_TYPE = 'ATOM', + FSSH_B_ATOMREF_TYPE = 'ATMR', + FSSH_B_BOOL_TYPE = 'BOOL', + FSSH_B_CHAR_TYPE = 'CHAR', + FSSH_B_COLOR_8_BIT_TYPE = 'CLRB', + FSSH_B_DOUBLE_TYPE = 'DBLE', + FSSH_B_FLOAT_TYPE = 'FLOT', + FSSH_B_GRAYSCALE_8_BIT_TYPE = 'GRYB', + FSSH_B_INT16_TYPE = 'SHRT', + FSSH_B_INT32_TYPE = 'LONG', + FSSH_B_INT64_TYPE = 'LLNG', + FSSH_B_INT8_TYPE = 'BYTE', + FSSH_B_LARGE_ICON_TYPE = 'ICON', + FSSH_B_MEDIA_PARAMETER_GROUP_TYPE = 'BMCG', + FSSH_B_MEDIA_PARAMETER_TYPE = 'BMCT', + FSSH_B_MEDIA_PARAMETER_WEB_TYPE = 'BMCW', + FSSH_B_MESSAGE_TYPE = 'MSGG', + FSSH_B_MESSENGER_TYPE = 'MSNG', + FSSH_B_MIME_TYPE = 'MIME', + FSSH_B_MINI_ICON_TYPE = 'MICN', + FSSH_B_MONOCHROME_1_BIT_TYPE = 'MNOB', + FSSH_B_OBJECT_TYPE = 'OPTR', + FSSH_B_OFF_T_TYPE = 'OFFT', + FSSH_B_PATTERN_TYPE = 'PATN', + FSSH_B_POINTER_TYPE = 'PNTR', + FSSH_B_POINT_TYPE = 'BPNT', + FSSH_B_PROPERTY_INFO_TYPE = 'SCTD', + FSSH_B_RAW_TYPE = 'RAWT', + FSSH_B_RECT_TYPE = 'RECT', + FSSH_B_REF_TYPE = 'RREF', + FSSH_B_RGB_32_BIT_TYPE = 'RGBB', + FSSH_B_RGB_COLOR_TYPE = 'RGBC', + FSSH_B_SIZE_T_TYPE = 'SIZT', + FSSH_B_SSIZE_T_TYPE = 'SSZT', + FSSH_B_STRING_TYPE = 'CSTR', + FSSH_B_TIME_TYPE = 'TIME', + FSSH_B_UINT16_TYPE = 'USHT', + FSSH_B_UINT32_TYPE = 'ULNG', + FSSH_B_UINT64_TYPE = 'ULLG', + FSSH_B_UINT8_TYPE = 'UBYT', + FSSH_B_VECTOR_ICON_TYPE = 'VICN', + + // deprecated, do not use + FSSH_B_ASCII_TYPE = 'TEXT' // use B_STRING_TYPE instead +}; + +//----- System-wide MIME types for handling URL's ------------------------------ + +extern const char *FSSH_B_URL_HTTP; // application/x-vnd.Be.URL.http +extern const char *FSSH_B_URL_HTTPS; // application/x-vnd.Be.URL.https +extern const char *FSSH_B_URL_FTP; // application/x-vnd.Be.URL.ftp +extern const char *FSSH_B_URL_GOPHER; // application/x-vnd.Be.URL.gopher +extern const char *FSSH_B_URL_MAILTO; // application/x-vnd.Be.URL.mailto +extern const char *FSSH_B_URL_NEWS; // application/x-vnd.Be.URL.news +extern const char *FSSH_B_URL_NNTP; // application/x-vnd.Be.URL.nntp +extern const char *FSSH_B_URL_TELNET; // application/x-vnd.Be.URL.telnet +extern const char *FSSH_B_URL_RLOGIN; // application/x-vnd.Be.URL.rlogin +extern const char *FSSH_B_URL_TN3270; // application/x-vnd.Be.URL.tn3270 +extern const char *FSSH_B_URL_WAIS; // application/x-vnd.Be.URL.wais +extern const char *FSSH_B_URL_FILE; // application/x-vnd.Be.URL.file + +#endif // _FSSH_TYPE_CONSTANTS_H diff --git a/headers/private/fs_shell/fssh_types.h b/headers/private/fs_shell/fssh_types.h new file mode 100644 index 0000000000..8f1c31add4 --- /dev/null +++ b/headers/private/fs_shell/fssh_types.h @@ -0,0 +1,33 @@ +#ifndef _FSSH_TYPES_H +#define _FSSH_TYPES_H + +#include + +typedef uint32_t fssh_ulong; +typedef volatile int32_t vint32_t; + +typedef uint32_t fssh_addr_t; + +typedef int32_t fssh_dev_t; +typedef int64_t fssh_ino_t; + +typedef uint32_t fssh_size_t; +typedef int32_t fssh_ssize_t; +typedef int64_t fssh_off_t; + +typedef int64_t fssh_bigtime_t; + +typedef int32_t fssh_status_t; +typedef uint32_t fssh_type_code; + +typedef uint32_t fssh_mode_t; +typedef uint32_t fssh_nlink_t; +typedef uint32_t fssh_uid_t; +typedef uint32_t fssh_gid_t; +typedef int32_t fssh_pid_t; + +#ifndef NULL +#define NULL (0) +#endif + +#endif // _FSSH_TYPES_H diff --git a/headers/private/fs_shell/fssh_uio.h b/headers/private/fs_shell/fssh_uio.h new file mode 100644 index 0000000000..dc15a9d58c --- /dev/null +++ b/headers/private/fs_shell/fssh_uio.h @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2007, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_SYS_UIO_H +#define _FSSH_SYS_UIO_H + + +#include "fssh_types.h" + + +typedef struct fssh_iovec { + void *iov_base; + fssh_size_t iov_len; +} fssh_iovec; + + +#ifdef __cplusplus +extern "C" { +#endif + +fssh_ssize_t fssh_readv(int fd, const struct fssh_iovec *vector, + fssh_size_t count); +fssh_ssize_t fssh_readv_pos(int fd, fssh_off_t pos, const struct + fssh_iovec *vec, fssh_size_t count); +fssh_ssize_t fssh_writev(int fd, const struct fssh_iovec *vector, + fssh_size_t count); +fssh_ssize_t fssh_writev_pos(int fd, fssh_off_t pos, + const struct fssh_iovec *vec, fssh_size_t count); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_SYS_UIO_H */ diff --git a/headers/private/fs_shell/fssh_unistd.h b/headers/private/fs_shell/fssh_unistd.h new file mode 100644 index 0000000000..e6ec586f58 --- /dev/null +++ b/headers/private/fs_shell/fssh_unistd.h @@ -0,0 +1,87 @@ +/* + * Copyright 2004-2007, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_UNISTD_H +#define _FSSH_UNISTD_H + + +#include "fssh_defs.h" + + +/* access modes */ +#define FSSH_R_OK 4 +#define FSSH_W_OK 2 +#define FSSH_X_OK 1 +#define FSSH_F_OK 0 + +/* standard file descriptors */ +#define FSSH_STDIN_FILENO 0 +#define FSSH_STDOUT_FILENO 1 +#define FSSH_STDERR_FILENO 2 + +/* lseek() constants */ +#ifndef FSSH_SEEK_SET +# define FSSH_SEEK_SET 0 +#endif +#ifndef FSSH_SEEK_CUR +# define FSSH_SEEK_CUR 1 +#endif +#ifndef FSSH_SEEK_END +# define FSSH_SEEK_END 2 +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* file functions */ +extern int fssh_access(const char *path, int accessMode); + +extern int fssh_chdir(const char *path); +extern int fssh_fchdir(int fd); +extern char *fssh_getcwd(char *buffer, fssh_size_t size); + +extern int fssh_dup(int fd); +extern int fssh_dup2(int fd1, int fd2); +extern int fssh_close(int fd); +extern int fssh_link(const char *name, const char *new_name); +extern int fssh_unlink(const char *name); +extern int fssh_rmdir(const char *path); + +extern fssh_ssize_t fssh_readlink(const char *path, char *buffer, + fssh_size_t bufferSize); +extern int fssh_symlink(const char *from, const char *to); + +extern int fssh_ftruncate(int fd, fssh_off_t newSize); +extern int fssh_truncate(const char *path, fssh_off_t newSize); +extern int fssh_ioctl(int fd, unsigned long op, ...); + +extern fssh_ssize_t fssh_read(int fd, void *buffer, fssh_size_t count); +extern fssh_ssize_t fssh_read_pos(int fd, fssh_off_t pos, void *buffer, + fssh_size_t count); +extern fssh_ssize_t fssh_pread(int fd, void *buffer, fssh_size_t count, + fssh_off_t pos); +extern fssh_ssize_t fssh_write(int fd, const void *buffer, fssh_size_t count); +extern fssh_ssize_t fssh_write_pos(int fd, fssh_off_t pos, const void *buffer, + fssh_size_t count); +extern fssh_ssize_t fssh_pwrite(int fd, const void *buffer, fssh_size_t count, + fssh_off_t pos); +extern fssh_off_t fssh_lseek(int fd, fssh_off_t offset, int whence); + +extern int fssh_sync(void); +extern int fssh_fsync(int fd); + +/* access permissions */ +extern fssh_gid_t fssh_getegid(void); +extern fssh_uid_t fssh_geteuid(void); +extern fssh_gid_t fssh_getgid(void); +extern int fssh_getgroups(int groupSize, fssh_gid_t groupList[]); +extern fssh_uid_t fssh_getuid(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _FSSH_UNISTD_H */ diff --git a/src/tools/Jamfile b/src/tools/Jamfile index 0c966c0281..52a92dec42 100644 --- a/src/tools/Jamfile +++ b/src/tools/Jamfile @@ -63,6 +63,7 @@ SubInclude HAIKU_TOP src tools copy_to_bfs_image ; SubInclude HAIKU_TOP src tools cppunit ; SubInclude HAIKU_TOP src tools docbook ; SubInclude HAIKU_TOP src tools elfsymbolpatcher ; +SubInclude HAIKU_TOP src tools fs_shell ; SubInclude HAIKU_TOP src tools gensyscalls ; SubInclude HAIKU_TOP src tools keymap ; SubInclude HAIKU_TOP src tools makebootable ; diff --git a/src/tools/fs_shell/Jamfile b/src/tools/fs_shell/Jamfile new file mode 100644 index 0000000000..f4d84734d3 --- /dev/null +++ b/src/tools/fs_shell/Jamfile @@ -0,0 +1,38 @@ +SubDir HAIKU_TOP src tools fs_shell ; + +UseHeaders [ FDirName $(HAIKU_TOP) headers build ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) headers build os ] : true ; +#UseHeaders [ FDirName $(HAIKU_TOP) headers build os app ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) headers build os kernel ] : true ; +#UseHeaders [ FDirName $(HAIKU_TOP) headers build os interface ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) headers build os storage ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) headers build os support ] : true ; + +UsePrivateHeaders fs_shell ; + +BuildPlatformStaticLibrary fs_shell.a : + atomic.cpp + block_cache.cpp + errno.cpp + fcntl.cpp + fd.cpp + file_cache.cpp + kernel_export.cpp + KPath.cpp + hash.cpp + list.cpp + lock.cpp + node_monitor.cpp + sem.cpp + stat.cpp + stat_util.cpp + stdio.cpp + string.cpp + thread.cpp + time.cpp + uio.cpp + unistd.cpp + vfs.cpp + + fssh.cpp +; diff --git a/src/tools/fs_shell/KPath.cpp b/src/tools/fs_shell/KPath.cpp new file mode 100644 index 0000000000..4a0ba15fc2 --- /dev/null +++ b/src/tools/fs_shell/KPath.cpp @@ -0,0 +1,298 @@ +/* + * Copyright 2004-2007, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ + +/** A simple class wrapping a path. Has a fixed-sized buffer. */ + +#include "KPath.h" + +#include + +#include "fssh_string.h" +#include "vfs.h" + + +// debugging +#define TRACE(x) ; +//#define TRACE(x) dprintf x + + +KPath::KPath(fssh_size_t bufferSize) + : + fBuffer(NULL), + fBufferSize(0), + fPathLength(0), + fLocked(false) +{ + SetTo(NULL, bufferSize); +} + + +KPath::KPath(const char* path, bool normalize, fssh_size_t bufferSize) + : + fBuffer(NULL), + fBufferSize(0), + fPathLength(0), + fLocked(false) +{ + SetTo(path, normalize, bufferSize); +} + + +KPath::KPath(const KPath& other) + : + fBuffer(NULL), + fBufferSize(0), + fPathLength(0), + fLocked(false) +{ + *this = other; +} + + +KPath::~KPath() +{ + free(fBuffer); +} + + +fssh_status_t +KPath::SetTo(const char* path, bool normalize, fssh_size_t bufferSize) +{ + if (bufferSize == 0) + bufferSize = FSSH_B_PATH_NAME_LENGTH; + + // free the previous buffer, if the buffer size differs + if (fBuffer && fBufferSize != bufferSize) { + free(fBuffer); + fBuffer = NULL; + fBufferSize = 0; + } + fPathLength = 0; + fLocked = false; + + // allocate buffer + if (!fBuffer) + fBuffer = (char*)malloc(bufferSize); + if (!fBuffer) + return FSSH_B_NO_MEMORY; + if (fBuffer) { + fBufferSize = bufferSize; + fBuffer[0] = '\0'; + } + return SetPath(path, normalize); +} + + +fssh_status_t +KPath::InitCheck() const +{ + return fBuffer ? FSSH_B_OK : FSSH_B_NO_MEMORY; +} + + +fssh_status_t +KPath::SetPath(const char *path, bool normalize) +{ + if (!fBuffer) + return FSSH_B_NO_INIT; + + if (path) { + if (normalize) { + // normalize path + fssh_status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, + true); + if (error != FSSH_B_OK) { + SetPath(NULL); + return error; + } + fPathLength = fssh_strlen(fBuffer); + } else { + // don't normalize path + fssh_size_t length = fssh_strlen(path); + if (length >= fBufferSize) + return FSSH_B_BUFFER_OVERFLOW; + + fssh_memcpy(fBuffer, path, length + 1); + fPathLength = length; + _ChopTrailingSlashes(); + } + } else { + fBuffer[0] = '\0'; + fPathLength = 0; + } + return FSSH_B_OK; +} + + +const char* +KPath::Path() const +{ + return fBuffer; +} + + +char * +KPath::LockBuffer() +{ + if (!fBuffer || fLocked) + return NULL; + + fLocked = true; + return fBuffer; +} + + +void +KPath::UnlockBuffer() +{ + if (!fLocked) { + TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n")); + return; + } + fLocked = false; + fPathLength = fssh_strnlen(fBuffer, fBufferSize); + if (fPathLength == fBufferSize) { + TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); + fPathLength--; + fBuffer[fPathLength] = '\0'; + } + _ChopTrailingSlashes(); +} + + +const char * +KPath::Leaf() const +{ + if (!fBuffer) + return NULL; + + // only "/" has trailing slashes -- then we have to return the complete + // buffer, as we have to do in case there are no slashes at all + if (fPathLength != 1 || fBuffer[0] != '/') { + for (int32_t i = fPathLength - 1; i >= 0; i--) { + if (fBuffer[i] == '/') + return fBuffer + i + 1; + } + } + return fBuffer; +} + + +fssh_status_t +KPath::ReplaceLeaf(const char *newLeaf) +{ + const char *leaf = Leaf(); + if (!leaf) + return FSSH_B_NO_INIT; + + int32_t leafIndex = leaf - fBuffer; + // chop off the current leaf (don't replace "/", though) + if (leafIndex != 0 || fBuffer[leafIndex - 1]) { + fBuffer[leafIndex] = '\0'; + fPathLength = leafIndex; + _ChopTrailingSlashes(); + } + + // if a leaf was given, append it + if (newLeaf) + return Append(newLeaf); + return FSSH_B_OK; +} + + +fssh_status_t +KPath::Append(const char *component, bool isComponent) +{ + // check initialization and parameter + if (!fBuffer) + return FSSH_B_NO_INIT; + if (!component) + return FSSH_B_BAD_VALUE; + if (fPathLength == 0) + return SetPath(component); + + // get component length + fssh_size_t componentLength = fssh_strlen(component); + if (componentLength < 1) + return FSSH_B_OK; + + // if our current path is empty, we just copy the supplied one + // compute the result path len + bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/' + && component[0] != '/'; + fssh_size_t resultPathLength = fPathLength + componentLength + (insertSlash ? 1 : 0); + if (resultPathLength >= fBufferSize) + return FSSH_B_BUFFER_OVERFLOW; + + // compose the result path + if (insertSlash) + fBuffer[fPathLength++] = '/'; + fssh_memcpy(fBuffer + fPathLength, component, componentLength + 1); + fPathLength = resultPathLength; + return FSSH_B_OK; +} + + +KPath& +KPath::operator=(const KPath& other) +{ + SetTo(other.fBuffer, other.fBufferSize); + return *this; +} + + +KPath& +KPath::operator=(const char* path) +{ + SetTo(path); + return *this; +} + + +bool +KPath::operator==(const KPath& other) const +{ + if (!fBuffer) + return !other.fBuffer; + + return (other.fBuffer + && fPathLength == other.fPathLength + && fssh_strcmp(fBuffer, other.fBuffer) == 0); +} + + +bool +KPath::operator==(const char* path) const +{ + if (!fBuffer) + return (!path); + + return path && !fssh_strcmp(fBuffer, path); +} + + +bool +KPath::operator!=(const KPath& other) const +{ + return !(*this == other); +} + + +bool +KPath::operator!=(const char* path) const +{ + return !(*this == path); +} + + +void +KPath::_ChopTrailingSlashes() +{ + if (fBuffer) { + while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') + fBuffer[--fPathLength] = '\0'; + } +} + diff --git a/src/tools/fs_shell/atomic.cpp b/src/tools/fs_shell/atomic.cpp new file mode 100644 index 0000000000..ba6a0589ef --- /dev/null +++ b/src/tools/fs_shell/atomic.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include "fssh_atomic.h" + + +int32_t +fssh_atomic_set(vint32_t *value, int32_t newValue) +{ + return atomic_set((vint32*)value, newValue); +} + + +int32_t +fssh_atomic_test_and_set(vint32_t *value, int32_t newValue, int32_t testAgainst) +{ + return atomic_test_and_set((vint32*)value, newValue, testAgainst); +} + + +int32_t +fssh_atomic_add(vint32_t *value, int32_t addValue) +{ + return atomic_add((vint32*)value, addValue); +} + + +int32_t +fssh_atomic_and(vint32_t *value, int32_t andValue) +{ + return atomic_and((vint32*)value, andValue); +} + + +int32_t +fssh_atomic_or(vint32_t *value, int32_t orValue) +{ + return atomic_or((vint32*)value, orValue); +} + + +int32_t +fssh_atomic_get(vint32_t *value) +{ + return atomic_get((vint32*)value); +} diff --git a/src/tools/fs_shell/block_cache.cpp b/src/tools/fs_shell/block_cache.cpp new file mode 100644 index 0000000000..2d24852d5e --- /dev/null +++ b/src/tools/fs_shell/block_cache.cpp @@ -0,0 +1,1219 @@ +/* + * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include "block_cache_priv.h" + +#include + +#include + +#include "fssh_atomic.h" +#include "fssh_errno.h" +#include "fssh_fs_cache.h" +#include "fssh_kernel_export.h" +#include "fssh_string.h" +#include "fssh_unistd.h" +#include "hash.h" +#include "vfs.h" + +// TODO: this is a naive but growing implementation to test the API: +// 1) block reading/writing is not at all optimized for speed, it will +// just read and write single blocks. +// 2) the locking could be improved; getting a block should not need to +// wait for blocks to be written +// 3) dirty blocks are only written back if asked for +// TODO: the retrieval/copy of the original data could be delayed until the +// new data must be written, ie. in low memory situations. + +//#define TRACE_BLOCK_CACHE +#ifdef TRACE_BLOCK_CACHE +# define TRACE(x) fssh_dprintf x +#else +# define TRACE(x) ; +#endif + +using std::nothrow; + +// This macro is used for fatal situations that are acceptable in a running +// system, like out of memory situations - should only panic for debugging. +#define FATAL(x) fssh_panic x + + +namespace FSShell { + +static const int32_t kMaxBlockCount = 1024; + + +struct cache_transaction { + cache_transaction(); + + cache_transaction *next; + int32_t id; + int32_t num_blocks; + int32_t sub_num_blocks; + cached_block *first_block; + block_list blocks; + fssh_transaction_notification_hook notification_hook; + void *notification_data; + bool open; + bool has_sub_transaction; +}; + +static fssh_status_t write_cached_block(block_cache *cache, cached_block *block, + bool deleteTransaction = true); + + +// #pragma mark - private transaction + + +cache_transaction::cache_transaction() +{ + num_blocks = 0; + sub_num_blocks = 0; + first_block = NULL; + notification_hook = NULL; + notification_data = NULL; + open = true; +} + + +static int +transaction_compare(void *_transaction, const void *_id) +{ + cache_transaction *transaction = (cache_transaction *)_transaction; + const int32_t *id = (const int32_t *)_id; + + return transaction->id - *id; +} + + +static uint32_t +transaction_hash(void *_transaction, const void *_id, uint32_t range) +{ + cache_transaction *transaction = (cache_transaction *)_transaction; + const int32_t *id = (const int32_t *)_id; + + if (transaction != NULL) + return transaction->id % range; + + return (uint32_t)*id % range; +} + + +static void +delete_transaction(block_cache *cache, cache_transaction *transaction) +{ + if (cache->last_transaction == transaction) + cache->last_transaction = NULL; + + delete transaction; +} + + +static cache_transaction * +lookup_transaction(block_cache *cache, int32_t id) +{ + return (cache_transaction *)hash_lookup(cache->transaction_hash, &id); +} + + +// #pragma mark - cached_block + + +/* static */ +int +cached_block::Compare(void *_cacheEntry, const void *_block) +{ + cached_block *cacheEntry = (cached_block *)_cacheEntry; + const fssh_off_t *block = (const fssh_off_t *)_block; + + return cacheEntry->block_number - *block; +} + + + +/* static */ +uint32_t +cached_block::Hash(void *_cacheEntry, const void *_block, uint32_t range) +{ + cached_block *cacheEntry = (cached_block *)_cacheEntry; + const fssh_off_t *block = (const fssh_off_t *)_block; + + if (cacheEntry != NULL) + return cacheEntry->block_number % range; + + return (uint64_t)*block % range; +} + + +// #pragma mark - block_cache + + +block_cache::block_cache(int _fd, fssh_off_t numBlocks, fssh_size_t blockSize, + bool readOnly) + : + hash(NULL), + fd(_fd), + max_blocks(numBlocks), + block_size(blockSize), + allocated_block_count(0), + next_transaction_id(1), + last_transaction(NULL), + transaction_hash(NULL), + read_only(readOnly) +{ + hash = hash_init(32, 0, &cached_block::Compare, &cached_block::Hash); + if (hash == NULL) + return; + + transaction_hash = hash_init(16, 0, &transaction_compare, + &FSShell::transaction_hash); + if (transaction_hash == NULL) + return; + + if (benaphore_init(&lock, "block cache") < FSSH_B_OK) + return; +} + + +block_cache::~block_cache() +{ + benaphore_destroy(&lock); + + hash_uninit(transaction_hash); + hash_uninit(hash); +} + + +fssh_status_t +block_cache::InitCheck() +{ + if (lock.sem < FSSH_B_OK) + return lock.sem; + + if (hash == NULL || transaction_hash == NULL) + return FSSH_B_NO_MEMORY; + + return FSSH_B_OK; +} + + +void +block_cache::Free(void *address) +{ + if (address == NULL) + return; + + free(address); +} + + +void * +block_cache::Allocate() +{ + return malloc(block_size); +} + + +void +block_cache::FreeBlock(cached_block *block) +{ + Free(block->current_data); + block->current_data = NULL; + + if (block->original_data != NULL || block->parent_data != NULL) { + fssh_panic("block_cache::FreeBlock(): %p, %p\n", block->original_data, + block->parent_data); + } + +#ifdef DEBUG_CHANGED + Free(block->compare); +#endif + + delete block; +} + + +/*! Allocates a new block for \a blockNumber, ready for use */ +cached_block * +block_cache::NewBlock(fssh_off_t blockNumber) +{ + cached_block *block = new(nothrow) cached_block; + if (block == NULL) { + FATAL(("could not allocate block!\n")); + return NULL; + } + + // if we hit the limit of blocks to cache¸ try to free one or more + if (allocated_block_count >= kMaxBlockCount) { + RemoveUnusedBlocks(LONG_MAX, + allocated_block_count - kMaxBlockCount + 1); + } + + block->current_data = Allocate(); + if (!block->current_data) { + FATAL(("could not allocate block data!\n")); + delete block; + return NULL; + } + + block->block_number = blockNumber; + block->ref_count = 0; + block->accessed = 0; + block->transaction_next = NULL; + block->transaction = block->previous_transaction = NULL; + block->original_data = NULL; + block->parent_data = NULL; + block->is_dirty = false; + block->unused = false; +#ifdef DEBUG_CHANGED + block->compare = NULL; +#endif + + allocated_block_count++; + + return block; +} + + +void +block_cache::RemoveUnusedBlocks(int32_t maxAccessed, int32_t count) +{ + TRACE(("block_cache: remove up to %ld unused blocks\n", count)); + + for (block_list::Iterator it = unused_blocks.GetIterator(); + cached_block *block = it.Next();) { + + if (maxAccessed < block->accessed) + continue; + + TRACE((" remove block %Ld, accessed %ld times\n", + block->block_number, block->accessed)); + + // this can only happen if no transactions are used + if (block->is_dirty) + write_cached_block(this, block, false); + + // remove block from lists + it.Remove(); + hash_remove(hash, block); + + FreeBlock(block); + + if (--count <= 0) + break; + } +} + + +// #pragma mark - private block functions + + +static void +put_cached_block(block_cache *cache, cached_block *block) +{ +#ifdef DEBUG_CHANGED + if (!block->is_dirty && block->compare != NULL + && memcmp(block->current_data, block->compare, cache->block_size)) { + fssh_dprintf("new block:\n"); + fssh_dump_block((const char *)block->current_data, 256, " "); + fssh_dprintf("unchanged block:\n"); + fssh_dump_block((const char *)block->compare, 256, " "); + write_cached_block(cache, block); + fssh_panic("block_cache: supposed to be clean block was changed!\n"); + + cache->Free(block->compare); + block->compare = NULL; + } +#endif + + if (--block->ref_count == 0 + && block->transaction == NULL + && block->previous_transaction == NULL) { + // put this block in the list of unused blocks + block->unused = true; + cache->unused_blocks.Add(block); + } + + if (cache->allocated_block_count > kMaxBlockCount) { + cache->RemoveUnusedBlocks(LONG_MAX, + cache->allocated_block_count - kMaxBlockCount); + } +} + + +static void +put_cached_block(block_cache *cache, fssh_off_t blockNumber) +{ + if (blockNumber < 0 || blockNumber >= cache->max_blocks) { + fssh_panic("put_cached_block: invalid block number %lld (max %lld)", + blockNumber, cache->max_blocks - 1); + } + + cached_block *block = (cached_block *)hash_lookup(cache->hash, &blockNumber); + if (block != NULL) + put_cached_block(cache, block); +} + + +/*! + Retrieves the block \a blockNumber from the hash table, if it's already + there, or reads it from the disk. + + \param _allocated tells you wether or not a new block has been allocated + to satisfy your request. + \param readBlock if \c false, the block will not be read in case it was + not already in the cache. The block you retrieve may contain random + data. +*/ +static cached_block * +get_cached_block(block_cache *cache, fssh_off_t blockNumber, bool *_allocated, + bool readBlock = true) +{ + if (blockNumber < 0 || blockNumber >= cache->max_blocks) { + fssh_panic("get_cached_block: invalid block number %lld (max %lld)", + blockNumber, cache->max_blocks - 1); + return NULL; + } + + cached_block *block = (cached_block *)hash_lookup(cache->hash, + &blockNumber); + *_allocated = false; + + if (block == NULL) { + // read block into cache + block = cache->NewBlock(blockNumber); + if (block == NULL) + return NULL; + + hash_insert(cache->hash, block); + *_allocated = true; + } else { + // TODO: currently, the data is always mapped in +/* + if (block->ref_count == 0 && block->current_data != NULL) { + // see if the old block can be resurrected + block->current_data = cache->allocator->Acquire(block->current_data); + } + + if (block->current_data == NULL) { + // there is no block yet, but we need one + block->current_data = cache->allocator->Get(); + if (block->current_data == NULL) + return NULL; + + *_allocated = true; + } +*/ + } + + if (*_allocated && readBlock) { + int32_t blockSize = cache->block_size; + + if (fssh_read_pos(cache->fd, blockNumber * blockSize, block->current_data, + blockSize) < blockSize) { + hash_remove(cache->hash, block); + cache->FreeBlock(block); + FATAL(("could not read block %Ld\n", blockNumber)); + return NULL; + } + } + + if (block->unused) { + //TRACE(("remove block %Ld from unused\n", blockNumber)); + block->unused = false; + cache->unused_blocks.Remove(block); + } + + block->ref_count++; + block->accessed++; + + return block; +} + + +/*! + Returns the writable block data for the requested blockNumber. + If \a cleared is true, the block is not read from disk; an empty block + is returned. + + This is the only method to insert a block into a transaction. It makes + sure that the previous block contents are preserved in that case. +*/ +static void * +get_writable_cached_block(block_cache *cache, fssh_off_t blockNumber, fssh_off_t base, + fssh_off_t length, int32_t transactionID, bool cleared) +{ + TRACE(("get_writable_cached_block(blockNumber = %Ld, transaction = %ld)\n", + blockNumber, transactionID)); + + if (blockNumber < 0 || blockNumber >= cache->max_blocks) { + fssh_panic("get_writable_cached_block: invalid block number %lld (max %lld)", + blockNumber, cache->max_blocks - 1); + } + + bool allocated; + cached_block *block = get_cached_block(cache, blockNumber, &allocated, + !cleared); + if (block == NULL) + return NULL; + + // if there is no transaction support, we just return the current block + if (transactionID == -1) { + if (cleared) + fssh_memset(block->current_data, 0, cache->block_size); + + block->is_dirty = true; + // mark the block as dirty + + return block->current_data; + } + + if (block->transaction != NULL && block->transaction->id != transactionID) { + // ToDo: we have to wait here until the other transaction is done. + // Maybe we should even panic, since we can't prevent any deadlocks. + fssh_panic("get_writable_cached_block(): asked to get busy writable block (transaction %d)\n", (int)block->transaction->id); + put_cached_block(cache, block); + return NULL; + } + if (block->transaction == NULL && transactionID != -1) { + // get new transaction + cache_transaction *transaction = lookup_transaction(cache, transactionID); + if (transaction == NULL) { + fssh_panic("get_writable_cached_block(): invalid transaction %d!\n", + (int)transactionID); + put_cached_block(cache, block); + return NULL; + } + if (!transaction->open) { + fssh_panic("get_writable_cached_block(): transaction already done!\n"); + put_cached_block(cache, block); + return NULL; + } + + block->transaction = transaction; + + // attach the block to the transaction block list + block->transaction_next = transaction->first_block; + transaction->first_block = block; + transaction->num_blocks++; + } + + if (!(allocated && cleared) && block->original_data == NULL) { + // we already have data, so we need to preserve it + block->original_data = cache->Allocate(); + if (block->original_data == NULL) { + FATAL(("could not allocate original_data\n")); + put_cached_block(cache, block); + return NULL; + } + + fssh_memcpy(block->original_data, block->current_data, cache->block_size); + } + if (block->parent_data == block->current_data) { + // remember any previous contents for the parent transaction + block->parent_data = cache->Allocate(); + if (block->parent_data == NULL) { + // TODO: maybe we should just continue the current transaction in this case... + FATAL(("could not allocate parent\n")); + put_cached_block(cache, block); + return NULL; + } + + fssh_memcpy(block->parent_data, block->current_data, cache->block_size); + block->transaction->sub_num_blocks++; + } + + if (cleared) + fssh_memset(block->current_data, 0, cache->block_size); + + block->is_dirty = true; + + return block->current_data; +} + + +static fssh_status_t +write_cached_block(block_cache *cache, cached_block *block, + bool deleteTransaction) +{ + cache_transaction *previous = block->previous_transaction; + int32_t blockSize = cache->block_size; + + void *data = previous && block->original_data + ? block->original_data : block->current_data; + // we first need to write back changes from previous transactions + + TRACE(("write_cached_block(block %Ld)\n", block->block_number)); + + fssh_ssize_t written = fssh_write_pos(cache->fd, block->block_number * blockSize, + data, blockSize); + + if (written < blockSize) { + FATAL(("could not write back block %Ld (%s)\n", block->block_number, + fssh_strerror(fssh_get_errno()))); + return FSSH_B_IO_ERROR; + } + + if (data == block->current_data) + block->is_dirty = false; + + if (previous != NULL) { + previous->blocks.Remove(block); + block->previous_transaction = NULL; + + // Has the previous transation been finished with that write? + if (--previous->num_blocks == 0) { + TRACE(("cache transaction %ld finished!\n", previous->id)); + + if (previous->notification_hook != NULL) { + previous->notification_hook(previous->id, + previous->notification_data); + } + + if (deleteTransaction) { + hash_remove(cache->transaction_hash, previous); + delete_transaction(cache, previous); + } + } + } + + return FSSH_B_OK; +} + + +fssh_status_t +block_cache_init() +{ + return FSSH_B_OK; +} + + +} // namespace FSShell + + +// #pragma mark - public transaction API + + +using namespace FSShell; + + +int32_t +fssh_cache_start_transaction(void *_cache) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + if (cache->last_transaction && cache->last_transaction->open) { + fssh_panic("last transaction (%d) still open!\n", + (int)cache->last_transaction->id); + } + + cache_transaction *transaction = new(nothrow) cache_transaction; + if (transaction == NULL) + return FSSH_B_NO_MEMORY; + + transaction->id = fssh_atomic_add(&cache->next_transaction_id, 1); + cache->last_transaction = transaction; + + TRACE(("cache_start_transaction(): id %ld started\n", transaction->id)); + + hash_insert(cache->transaction_hash, transaction); + + return transaction->id; +} + + +fssh_status_t +fssh_cache_sync_transaction(void *_cache, int32_t id) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + fssh_status_t status = FSSH_B_ENTRY_NOT_FOUND; + + hash_iterator iterator; + hash_open(cache->transaction_hash, &iterator); + + cache_transaction *transaction; + while ((transaction = (cache_transaction *)hash_next( + cache->transaction_hash, &iterator)) != NULL) { + // close all earlier transactions which haven't been closed yet + + if (transaction->id <= id && !transaction->open) { + // write back all of their remaining dirty blocks + while (transaction->num_blocks > 0) { + status = write_cached_block(cache, transaction->blocks.Head(), + false); + if (status != FSSH_B_OK) + return status; + } + + hash_remove_current(cache->transaction_hash, &iterator); + delete_transaction(cache, transaction); + } + } + + hash_close(cache->transaction_hash, &iterator, false); + return FSSH_B_OK; +} + + +fssh_status_t +fssh_cache_end_transaction(void *_cache, int32_t id, + fssh_transaction_notification_hook hook, void *data) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + TRACE(("cache_end_transaction(id = %ld)\n", id)); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) { + fssh_panic("cache_end_transaction(): invalid transaction ID\n"); + return FSSH_B_BAD_VALUE; + } + + transaction->notification_hook = hook; + transaction->notification_data = data; + + // iterate through all blocks and free the unchanged original contents + + cached_block *block = transaction->first_block, *next; + for (; block != NULL; block = next) { + next = block->transaction_next; + + if (block->previous_transaction != NULL) { + // need to write back pending changes + write_cached_block(cache, block); + } + + if (block->original_data != NULL) { + cache->Free(block->original_data); + block->original_data = NULL; + } + if (transaction->has_sub_transaction) { + if (block->parent_data != block->current_data) + cache->Free(block->parent_data); + block->parent_data = NULL; + } + + // move the block to the previous transaction list + transaction->blocks.Add(block); + + block->previous_transaction = transaction; + block->transaction_next = NULL; + block->transaction = NULL; + } + + transaction->open = false; + + return FSSH_B_OK; +} + + +fssh_status_t +fssh_cache_abort_transaction(void *_cache, int32_t id) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + TRACE(("cache_abort_transaction(id = %ld)\n", id)); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) { + fssh_panic("cache_abort_transaction(): invalid transaction ID\n"); + return FSSH_B_BAD_VALUE; + } + + // iterate through all blocks and restore their original contents + + cached_block *block = transaction->first_block, *next; + for (; block != NULL; block = next) { + next = block->transaction_next; + + if (block->original_data != NULL) { + TRACE(("cache_abort_transaction(id = %ld): restored contents of block %Ld\n", + transaction->id, block->block_number)); + fssh_memcpy(block->current_data, block->original_data, cache->block_size); + cache->Free(block->original_data); + block->original_data = NULL; + } + if (transaction->has_sub_transaction) { + if (block->parent_data != block->current_data) + cache->Free(block->parent_data); + block->parent_data = NULL; + } + + block->transaction_next = NULL; + block->transaction = NULL; + } + + hash_remove(cache->transaction_hash, transaction); + delete_transaction(cache, transaction); + return FSSH_B_OK; +} + + +/*! + Acknowledges the current parent transaction, and starts a new transaction + from its sub transaction. + The new transaction also gets a new transaction ID. +*/ +int32_t +fssh_cache_detach_sub_transaction(void *_cache, int32_t id, + fssh_transaction_notification_hook hook, void *data) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + TRACE(("cache_detach_sub_transaction(id = %ld)\n", id)); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) { + fssh_panic("cache_detach_sub_transaction(): invalid transaction ID\n"); + return FSSH_B_BAD_VALUE; + } + if (!transaction->has_sub_transaction) + return FSSH_B_BAD_VALUE; + + // create a new transaction for the sub transaction + cache_transaction *newTransaction = new(nothrow) cache_transaction; + if (transaction == NULL) + return FSSH_B_NO_MEMORY; + + newTransaction->id = fssh_atomic_add(&cache->next_transaction_id, 1); + + transaction->notification_hook = hook; + transaction->notification_data = data; + + // iterate through all blocks and free the unchanged original contents + + cached_block *block = transaction->first_block, *next, *last = NULL; + for (; block != NULL; block = next) { + next = block->transaction_next; + + if (block->previous_transaction != NULL) { + // need to write back pending changes + write_cached_block(cache, block); + } + + if (block->original_data != NULL && block->parent_data != NULL + && block->parent_data != block->current_data) { + // free the original data if the parent data of the transaction + // will be made current - but keep them otherwise + cache->Free(block->original_data); + block->original_data = NULL; + } + if (block->parent_data != NULL + && block->parent_data != block->current_data) { + // we need to move this block over to the new transaction + block->original_data = block->parent_data; + if (last == NULL) + newTransaction->first_block = block; + else + last->transaction_next = block; + + last = block; + } + block->parent_data = NULL; + + // move the block to the previous transaction list + transaction->blocks.Add(block); + + block->previous_transaction = transaction; + block->transaction_next = NULL; + block->transaction = newTransaction; + } + + transaction->open = false; + + hash_insert(cache->transaction_hash, newTransaction); + cache->last_transaction = newTransaction; + + return FSSH_B_OK; +} + + +fssh_status_t +fssh_cache_abort_sub_transaction(void *_cache, int32_t id) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + TRACE(("cache_abort_sub_transaction(id = %ld)\n", id)); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) { + fssh_panic("cache_abort_sub_transaction(): invalid transaction ID\n"); + return FSSH_B_BAD_VALUE; + } + if (!transaction->has_sub_transaction) + return FSSH_B_BAD_VALUE; + + // revert all changes back to the version of the parent + + cached_block *block = transaction->first_block, *next; + for (; block != NULL; block = next) { + next = block->transaction_next; + + if (block->parent_data == NULL) { + if (block->original_data != NULL) { + // the parent transaction didn't change the block, but the sub + // transaction did - we need to revert from the original data + fssh_memcpy(block->current_data, block->original_data, + cache->block_size); + } + } else if (block->parent_data != block->current_data) { + // the block has been changed and must be restored + TRACE(("cache_abort_sub_transaction(id = %ld): restored contents of block %Ld\n", + transaction->id, block->block_number)); + fssh_memcpy(block->current_data, block->parent_data, cache->block_size); + cache->Free(block->parent_data); + } + + block->parent_data = NULL; + } + + // all subsequent changes will go into the main transaction + transaction->has_sub_transaction = false; + return FSSH_B_OK; +} + + +fssh_status_t +fssh_cache_start_sub_transaction(void *_cache, int32_t id) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + TRACE(("cache_start_sub_transaction(id = %ld)\n", id)); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) { + fssh_panic("cache_start_sub_transaction(): invalid transaction ID %d\n", (int)id); + return FSSH_B_BAD_VALUE; + } + + // move all changed blocks up to the parent + + cached_block *block = transaction->first_block, *next; + for (; block != NULL; block = next) { + next = block->transaction_next; + + if (transaction->has_sub_transaction + && block->parent_data != NULL + && block->parent_data != block->current_data) { + // there already is an older sub transaction - we acknowledge + // its changes and move its blocks up to the parent + cache->Free(block->parent_data); + } + + // we "allocate" the parent data lazily, that means, we don't copy + // the data (and allocate memory for it) until we need to + block->parent_data = block->current_data; + } + + // all subsequent changes will go into the sub transaction + transaction->has_sub_transaction = true; + transaction->sub_num_blocks = 0; + + return FSSH_B_OK; +} + + +fssh_status_t +fssh_cache_next_block_in_transaction(void *_cache, int32_t id, uint32_t *_cookie, + fssh_off_t *_blockNumber, void **_data, void **_unchangedData) +{ + cached_block *block = (cached_block *)*_cookie; + block_cache *cache = (block_cache *)_cache; + + BenaphoreLocker locker(&cache->lock); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) + return FSSH_B_BAD_VALUE; + + if (block == NULL) + block = transaction->first_block; + else + block = block->transaction_next; + + if (block == NULL) + return FSSH_B_ENTRY_NOT_FOUND; + + if (_blockNumber) + *_blockNumber = block->block_number; + if (_data) + *_data = block->current_data; + if (_unchangedData) + *_unchangedData = block->original_data; + + *_cookie = (uint32_t)block; + return FSSH_B_OK; +} + + +int32_t +fssh_cache_blocks_in_transaction(void *_cache, int32_t id) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) + return FSSH_B_BAD_VALUE; + + return transaction->num_blocks; +} + + +int32_t +fssh_cache_blocks_in_sub_transaction(void *_cache, int32_t id) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + cache_transaction *transaction = lookup_transaction(cache, id); + if (transaction == NULL) + return FSSH_B_BAD_VALUE; + + return transaction->sub_num_blocks; +} + + +// #pragma mark - public block cache API +// public interface + + +void +fssh_block_cache_delete(void *_cache, bool allowWrites) +{ + block_cache *cache = (block_cache *)_cache; + + if (allowWrites) + fssh_block_cache_sync(cache); + + BenaphoreLocker locker(&cache->lock); + + // free all blocks + + uint32_t cookie = 0; + cached_block *block; + while ((block = (cached_block *)hash_remove_first(cache->hash, + &cookie)) != NULL) { + cache->FreeBlock(block); + } + + // free all transactions (they will all be aborted) + + cookie = 0; + cache_transaction *transaction; + while ((transaction = (cache_transaction *)hash_remove_first( + cache->transaction_hash, &cookie)) != NULL) { + delete transaction; + } + + delete cache; +} + + +void * +fssh_block_cache_create(int fd, fssh_off_t numBlocks, fssh_size_t blockSize, bool readOnly) +{ + block_cache *cache = new(nothrow) block_cache(fd, numBlocks, blockSize, + readOnly); + if (cache == NULL) + return NULL; + + if (cache->InitCheck() != FSSH_B_OK) { + delete cache; + return NULL; + } + + return cache; +} + + +fssh_status_t +fssh_block_cache_sync(void *_cache) +{ + block_cache *cache = (block_cache *)_cache; + + // we will sync all dirty blocks to disk that have a completed + // transaction or no transaction only + + BenaphoreLocker locker(&cache->lock); + hash_iterator iterator; + hash_open(cache->hash, &iterator); + + cached_block *block; + while ((block = (cached_block *)hash_next(cache->hash, &iterator)) != NULL) { + if (block->previous_transaction != NULL + || (block->transaction == NULL && block->is_dirty)) { + fssh_status_t status = write_cached_block(cache, block); + if (status != FSSH_B_OK) + return status; + } + } + + hash_close(cache->hash, &iterator, false); + return FSSH_B_OK; +} + + +fssh_status_t +fssh_block_cache_sync_etc(void *_cache, fssh_off_t blockNumber, + fssh_size_t numBlocks) +{ + block_cache *cache = (block_cache *)_cache; + + // we will sync all dirty blocks to disk that have a completed + // transaction or no transaction only + + if (blockNumber < 0 || blockNumber >= cache->max_blocks) { + fssh_panic("block_cache_sync_etc: invalid block number %Ld (max %Ld)", + blockNumber, cache->max_blocks - 1); + return FSSH_B_BAD_VALUE; + } + + BenaphoreLocker locker(&cache->lock); + + for (; numBlocks > 0; numBlocks--, blockNumber++) { + cached_block *block = (cached_block *)hash_lookup(cache->hash, + &blockNumber); + if (block == NULL) + continue; + if (block->previous_transaction != NULL + || (block->transaction == NULL && block->is_dirty)) { + fssh_status_t status = write_cached_block(cache, block); + if (status != FSSH_B_OK) + return status; + } + } + + return FSSH_B_OK; +} + + +fssh_status_t +fssh_block_cache_make_writable(void *_cache, fssh_off_t blockNumber, int32_t transaction) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + if (cache->read_only) + fssh_panic("tried to make block writable on a read-only cache!"); + + // ToDo: this can be done better! + void *block = get_writable_cached_block(cache, blockNumber, + blockNumber, 1, transaction, false); + if (block != NULL) { + put_cached_block((block_cache *)_cache, blockNumber); + return FSSH_B_OK; + } + + return FSSH_B_ERROR; +} + + +void * +fssh_block_cache_get_writable_etc(void *_cache, fssh_off_t blockNumber, fssh_off_t base, + fssh_off_t length, int32_t transaction) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + TRACE(("block_cache_get_writable_etc(block = %Ld, transaction = %ld)\n", + blockNumber, transaction)); + if (cache->read_only) + fssh_panic("tried to get writable block on a read-only cache!"); + + return get_writable_cached_block(cache, blockNumber, base, length, + transaction, false); +} + + +void * +fssh_block_cache_get_writable(void *_cache, fssh_off_t blockNumber, int32_t transaction) +{ + return fssh_block_cache_get_writable_etc(_cache, blockNumber, + blockNumber, 1, transaction); +} + + +void * +fssh_block_cache_get_empty(void *_cache, fssh_off_t blockNumber, int32_t transaction) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + TRACE(("block_cache_get_empty(block = %Ld, transaction = %ld)\n", + blockNumber, transaction)); + if (cache->read_only) + fssh_panic("tried to get empty writable block on a read-only cache!"); + + return get_writable_cached_block((block_cache *)_cache, blockNumber, + blockNumber, 1, transaction, true); +} + + +const void * +fssh_block_cache_get_etc(void *_cache, fssh_off_t blockNumber, fssh_off_t base, fssh_off_t length) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + bool allocated; + + cached_block *block = get_cached_block(cache, blockNumber, &allocated); + if (block == NULL) + return NULL; + +#ifdef DEBUG_CHANGED + if (block->compare == NULL) + block->compare = cache->Allocate(); + if (block->compare != NULL) + memcpy(block->compare, block->current_data, cache->block_size); +#endif + return block->current_data; +} + + +const void * +fssh_block_cache_get(void *_cache, fssh_off_t blockNumber) +{ + return fssh_block_cache_get_etc(_cache, blockNumber, blockNumber, 1); +} + + +/*! + Changes the internal status of a writable block to \a dirty. This can be + helpful in case you realize you don't need to change that block anymore + for whatever reason. + + Note, you must only use this function on blocks that were acquired + writable! +*/ +fssh_status_t +fssh_block_cache_set_dirty(void *_cache, fssh_off_t blockNumber, bool dirty, + int32_t transaction) +{ + // TODO: not yet implemented + if (dirty) + fssh_panic("block_cache_set_dirty(): not yet implemented that way!\n"); + + return FSSH_B_OK; +} + + +void +fssh_block_cache_put(void *_cache, fssh_off_t blockNumber) +{ + block_cache *cache = (block_cache *)_cache; + BenaphoreLocker locker(&cache->lock); + + put_cached_block(cache, blockNumber); +} + diff --git a/src/tools/fs_shell/block_cache_priv.h b/src/tools/fs_shell/block_cache_priv.h new file mode 100644 index 0000000000..b8a780776c --- /dev/null +++ b/src/tools/fs_shell/block_cache_priv.h @@ -0,0 +1,91 @@ +/* + * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_BLOCK_CACHE_PRIVATE_H +#define _FSSH_BLOCK_CACHE_PRIVATE_H + +#include "DoublyLinkedList.h" +#include "lock.h" + + +namespace FSShell { + +struct hash_table; +struct vm_page; + + +//#define DEBUG_CHANGED +#undef DEBUG_CHANGED + + +struct cache_transaction; +struct cached_block; +struct block_cache; +typedef DoublyLinkedListLink block_link; + + +struct cached_block { + cached_block *next; // next in hash + cached_block *transaction_next; + block_link link; + fssh_off_t block_number; + void *current_data; + void *original_data; + void *parent_data; +#ifdef DEBUG_CHANGED + void *compare; +#endif + int32_t ref_count; + int32_t accessed; + bool busy : 1; + bool is_writing : 1; + bool is_dirty : 1; + bool unused : 1; + bool unmapped : 1; + cache_transaction *transaction; + cache_transaction *previous_transaction; + + static int Compare(void *_cacheEntry, const void *_block); + static uint32_t Hash(void *_cacheEntry, const void *_block, uint32_t range); +}; + +typedef DoublyLinkedList > block_list; + +struct block_cache { + hash_table *hash; + benaphore lock; + int fd; + fssh_off_t max_blocks; + fssh_size_t block_size; + int32_t allocated_block_count; + int32_t next_transaction_id; + cache_transaction *last_transaction; + hash_table *transaction_hash; + + block_list unmapped_blocks; + block_list unused_blocks; + + bool read_only; + + block_cache(int fd, fssh_off_t numBlocks, fssh_size_t blockSize, bool readOnly); + ~block_cache(); + + fssh_status_t InitCheck(); + + void RemoveUnusedBlocks(int32_t maxAccessed = LONG_MAX, int32_t count = LONG_MAX); + void FreeBlock(cached_block *block); + cached_block *NewBlock(fssh_off_t blockNumber); + void Free(void *address); + void *Allocate(); + + static void LowMemoryHandler(void *data, int32_t level); +}; + + +} // namespace FSShell + + +#endif /* _FSSH_BLOCK_CACHE_PRIVATE_H */ diff --git a/src/tools/fs_shell/errno.cpp b/src/tools/fs_shell/errno.cpp new file mode 100644 index 0000000000..835da29799 --- /dev/null +++ b/src/tools/fs_shell/errno.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "fssh_errno.h" + +#include + + +int * +_fssh_errnop(void) +{ + return &errno; +} + + +int +fssh_get_errno(void) +{ + return errno; +} + + +void +fssh_set_errno(int error) +{ + errno = error; +} diff --git a/src/tools/fs_shell/fcntl.cpp b/src/tools/fs_shell/fcntl.cpp new file mode 100644 index 0000000000..c41944276d --- /dev/null +++ b/src/tools/fs_shell/fcntl.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "fssh_fcntl.h" + +#include +#include + +#include "stat_util.h" + + +using namespace FSShell; + + +int +fssh_open(const char *pathname, int oflags, ...) +{ + va_list args; + va_start(args, oflags); + + // get the mode, if O_CREAT was specified + fssh_mode_t mode = 0; + if (oflags & FSSH_O_CREAT) + mode = va_arg(args, fssh_mode_t); + + va_end(args); + +// TODO: That's not perfect yet: We should use open() on BeOS compatible +// platforms and _kern_open() otherwise. + return open(pathname, to_platform_open_mode(oflags), + to_platform_mode(mode)); +} diff --git a/src/tools/fs_shell/fd.cpp b/src/tools/fs_shell/fd.cpp new file mode 100644 index 0000000000..e91b9f4952 --- /dev/null +++ b/src/tools/fs_shell/fd.cpp @@ -0,0 +1,736 @@ +/* Operations on file descriptors + * + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + +#include "fd.h" + +#include + +#include "fssh_fcntl.h" +#include "fssh_kernel_export.h" +#include "fssh_kernel_priv.h" +#include "fssh_string.h" +#include "fssh_uio.h" + + +//#define TRACE_FD +#ifdef TRACE_FD +# define TRACE(x) dprintf x +#else +# define TRACE(x) +#endif + + +namespace FSShell { + + +io_context gKernelIOContext; + + +/*** General fd routines ***/ + + +#ifdef DEBUG +void dump_fd(int fd, struct file_descriptor *descriptor); + +void +dump_fd(int fd,struct file_descriptor *descriptor) +{ + dprintf("fd[%d] = %p: type = %ld, ref_count = %ld, ops = %p, u.vnode = %p, u.mount = %p, cookie = %p, open_mode = %lx, pos = %Ld\n", + fd, descriptor, descriptor->type, descriptor->ref_count, descriptor->ops, + descriptor->u.vnode, descriptor->u.mount, descriptor->cookie, descriptor->open_mode, descriptor->pos); +} +#endif + + +/** Allocates and initializes a new file_descriptor */ + +struct file_descriptor * +alloc_fd(void) +{ + struct file_descriptor *descriptor; + + descriptor = (file_descriptor*)malloc(sizeof(struct file_descriptor)); + if (descriptor == NULL) + return NULL; + + descriptor->u.vnode = NULL; + descriptor->cookie = NULL; + descriptor->ref_count = 1; + descriptor->open_count = 0; + descriptor->open_mode = 0; + descriptor->pos = 0; + + return descriptor; +} + + +bool +fd_close_on_exec(struct io_context *context, int fd) +{ + return CHECK_BIT(context->fds_close_on_exec[fd / 8], fd & 7) ? true : false; +} + + +void +fd_set_close_on_exec(struct io_context *context, int fd, bool closeFD) +{ + if (closeFD) + context->fds_close_on_exec[fd / 8] |= (1 << (fd & 7)); + else + context->fds_close_on_exec[fd / 8] &= ~(1 << (fd & 7)); +} + + +/** Searches a free slot in the FD table of the provided I/O context, and inserts + * the specified descriptor into it. + */ + +int +new_fd_etc(struct io_context *context, struct file_descriptor *descriptor, + int firstIndex) +{ + int fd = -1; + uint32_t i; + + mutex_lock(&context->io_mutex); + + for (i = firstIndex; i < context->table_size; i++) { + if (!context->fds[i]) { + fd = i; + break; + } + } + if (fd < 0) { + fd = FSSH_B_NO_MORE_FDS; + goto err; + } + + context->fds[fd] = descriptor; + context->num_used_fds++; + fssh_atomic_add(&descriptor->open_count, 1); + +err: + mutex_unlock(&context->io_mutex); + + return fd; +} + + +int +new_fd(struct io_context *context, struct file_descriptor *descriptor) +{ + return new_fd_etc(context, descriptor, 0); +} + + +/** Reduces the descriptor's reference counter, and frees all resources + * when it's no longer used. + */ + +void +put_fd(struct file_descriptor *descriptor) +{ + int32_t previous = fssh_atomic_add(&descriptor->ref_count, -1); + + TRACE(("put_fd(descriptor = %p [ref = %ld, cookie = %p])\n", + descriptor, descriptor->ref_count, descriptor->cookie)); + + // free the descriptor if we don't need it anymore + if (previous == 1) { + // free the underlying object + if (descriptor->ops != NULL && descriptor->ops->fd_free != NULL) + descriptor->ops->fd_free(descriptor); + + free(descriptor); + } else if ((descriptor->open_mode & FSSH_O_DISCONNECTED) != 0 + && previous - 1 == descriptor->open_count + && descriptor->ops != NULL) { + // the descriptor has been disconnected - it cannot + // be accessed anymore, let's close it (no one is + // currently accessing this descriptor) + + if (descriptor->ops->fd_close) + descriptor->ops->fd_close(descriptor); + if (descriptor->ops->fd_free) + descriptor->ops->fd_free(descriptor); + + // prevent this descriptor from being closed/freed again + descriptor->open_count = -1; + descriptor->ref_count = -1; + descriptor->ops = NULL; + descriptor->u.vnode = NULL; + + // the file descriptor is kept intact, so that it's not + // reused until someone explicetly closes it + } +} + + +/** Decrements the open counter of the file descriptor and invokes + * its close hook when appropriate. + */ + +void +close_fd(struct file_descriptor *descriptor) +{ + if (fssh_atomic_add(&descriptor->open_count, -1) == 1) { + vfs_unlock_vnode_if_locked(descriptor); + + if (descriptor->ops != NULL && descriptor->ops->fd_close != NULL) + descriptor->ops->fd_close(descriptor); + } +} + + +/** This descriptor's underlying object will be closed and freed + * as soon as possible (in one of the next calls to put_fd() - + * get_fd() will no longer succeed on this descriptor). + * This is useful if the underlying object is gone, for instance + * when a (mounted) volume got removed unexpectedly. + */ + +void +disconnect_fd(struct file_descriptor *descriptor) +{ + descriptor->open_mode |= FSSH_O_DISCONNECTED; +} + + +void +inc_fd_ref_count(struct file_descriptor *descriptor) +{ + fssh_atomic_add(&descriptor->ref_count, 1); +} + + +struct file_descriptor * +get_fd(struct io_context *context, int fd) +{ + struct file_descriptor *descriptor = NULL; + + if (fd < 0) + return NULL; + + mutex_lock(&context->io_mutex); + + if ((uint32_t)fd < context->table_size) + descriptor = context->fds[fd]; + + if (descriptor != NULL) { + // Disconnected descriptors cannot be accessed anymore + if (descriptor->open_mode & FSSH_O_DISCONNECTED) + descriptor = NULL; + else + inc_fd_ref_count(descriptor); + } + + mutex_unlock(&context->io_mutex); + + return descriptor; +} + + +/** Removes the file descriptor from the specified slot. + */ + +static struct file_descriptor * +remove_fd(struct io_context *context, int fd) +{ + struct file_descriptor *descriptor = NULL; + + if (fd < 0) + return NULL; + + mutex_lock(&context->io_mutex); + + if ((uint32_t)fd < context->table_size) + descriptor = context->fds[fd]; + + if (descriptor) { + // fd is valid + context->fds[fd] = NULL; + fd_set_close_on_exec(context, fd, false); + context->num_used_fds--; + + if (descriptor->open_mode & FSSH_O_DISCONNECTED) + descriptor = NULL; + } + + mutex_unlock(&context->io_mutex); + + return descriptor; +} + + +static int +dup_fd(int fd, bool kernel) +{ + struct io_context *context = get_current_io_context(kernel); + struct file_descriptor *descriptor; + int status; + + TRACE(("dup_fd: fd = %d\n", fd)); + + // Try to get the fd structure + descriptor = get_fd(context, fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + // now put the fd in place + status = new_fd(context, descriptor); + if (status < 0) + put_fd(descriptor); + else { + mutex_lock(&context->io_mutex); + fd_set_close_on_exec(context, status, false); + mutex_unlock(&context->io_mutex); + } + + return status; +} + + +/** POSIX says this should be the same as: + * close(newfd); + * fcntl(oldfd, F_DUPFD, newfd); + * + * We do dup2() directly to be thread-safe. + */ + +static int +dup2_fd(int oldfd, int newfd, bool kernel) +{ + struct file_descriptor *evicted = NULL; + struct io_context *context; + + TRACE(("dup2_fd: ofd = %d, nfd = %d\n", oldfd, newfd)); + + // quick check + if (oldfd < 0 || newfd < 0) + return FSSH_B_FILE_ERROR; + + // Get current I/O context and lock it + context = get_current_io_context(kernel); + mutex_lock(&context->io_mutex); + + // Check if the fds are valid (mutex must be locked because + // the table size could be changed) + if ((uint32_t)oldfd >= context->table_size + || (uint32_t)newfd >= context->table_size + || context->fds[oldfd] == NULL) { + mutex_unlock(&context->io_mutex); + return FSSH_B_FILE_ERROR; + } + + // Check for identity, note that it cannot be made above + // because we always want to return an error on invalid + // handles + if (oldfd != newfd) { + // Now do the work + evicted = context->fds[newfd]; + fssh_atomic_add(&context->fds[oldfd]->ref_count, 1); + fssh_atomic_add(&context->fds[oldfd]->open_count, 1); + context->fds[newfd] = context->fds[oldfd]; + + if (evicted == NULL) + context->num_used_fds++; + } + + fd_set_close_on_exec(context, newfd, false); + + mutex_unlock(&context->io_mutex); + + // Say bye bye to the evicted fd + if (evicted) { + close_fd(evicted); + put_fd(evicted); + } + + return newfd; +} + + +fssh_status_t +select_fd(int fd, uint8_t event, uint32_t ref, struct select_sync *sync, bool kernel) +{ +// struct file_descriptor *descriptor; +// fssh_status_t status; +// +// TRACE(("select_fd(fd = %d, event = %u, ref = %lu, selectsync = %p)\n", fd, event, ref, sync)); +// +// descriptor = get_fd(get_current_io_context(kernel), fd); +// if (descriptor == NULL) +// return FSSH_B_FILE_ERROR; +// +// if (descriptor->ops->fd_select) { +// status = descriptor->ops->fd_select(descriptor, event, ref, sync); +// } else { +// // if the I/O subsystem doesn't support select(), we will +// // immediately notify the select call +// status = notify_select_event((void *)sync, ref, event); +// } +// +// put_fd(descriptor); +// return status; + + return FSSH_B_BAD_VALUE; +} + + +fssh_status_t +deselect_fd(int fd, uint8_t event, struct select_sync *sync, bool kernel) +{ +// struct file_descriptor *descriptor; +// fssh_status_t status; +// +// TRACE(("deselect_fd(fd = %d, event = %u, selectsync = %p)\n", fd, event, sync)); +// +// descriptor = get_fd(get_current_io_context(kernel), fd); +// if (descriptor == NULL) +// return FSSH_B_FILE_ERROR; +// +// if (descriptor->ops->fd_deselect) +// status = descriptor->ops->fd_deselect(descriptor, event, sync); +// else +// status = FSSH_B_OK; +// +// put_fd(descriptor); +// return status; + + return FSSH_B_BAD_VALUE; +} + + +/** This function checks if the specified fd is valid in the current + * context. It can be used for a quick check; the fd is not locked + * so it could become invalid immediately after this check. + */ + +bool +fd_is_valid(int fd, bool kernel) +{ + struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd); + if (descriptor == NULL) + return false; + + put_fd(descriptor); + return true; +} + + +struct vnode * +fd_vnode(struct file_descriptor *descriptor) +{ + switch (descriptor->type) { + case FDTYPE_FILE: + case FDTYPE_DIR: + case FDTYPE_ATTR_DIR: + case FDTYPE_ATTR: + return descriptor->u.vnode; + } + + return NULL; +} + + +static fssh_status_t +common_close(int fd, bool kernel) +{ + struct io_context *io = get_current_io_context(kernel); + struct file_descriptor *descriptor = remove_fd(io, fd); + + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + +#ifdef TRACE_FD + if (!kernel) + TRACE(("_user_close(descriptor = %p)\n", descriptor)); +#endif + + close_fd(descriptor); + put_fd(descriptor); + // the reference associated with the slot + + return FSSH_B_OK; +} + +} // namespace FSShell + + + +// #pragma mark - +// Kernel calls + + +using namespace FSShell; + + +fssh_ssize_t +_kern_read(int fd, fssh_off_t pos, void *buffer, fssh_size_t length) +{ + struct file_descriptor *descriptor; + fssh_ssize_t bytesRead; + + descriptor = get_fd(get_current_io_context(true), fd); + if (!descriptor) + return FSSH_B_FILE_ERROR; + if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_WRONLY) { + put_fd(descriptor); + return FSSH_B_FILE_ERROR; + } + + if (pos == -1) + pos = descriptor->pos; + + if (descriptor->ops->fd_read) { + bytesRead = descriptor->ops->fd_read(descriptor, pos, buffer, &length); + if (bytesRead >= FSSH_B_OK) { + if (length > SSIZE_MAX) + bytesRead = SSIZE_MAX; + else + bytesRead = (fssh_ssize_t)length; + + descriptor->pos = pos + length; + } + } else + bytesRead = FSSH_B_BAD_VALUE; + + put_fd(descriptor); + return bytesRead; +} + + +fssh_ssize_t +_kern_readv(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count) +{ + struct file_descriptor *descriptor; + fssh_ssize_t bytesRead = 0; + fssh_status_t status; + uint32_t i; + + descriptor = get_fd(get_current_io_context(true), fd); + if (!descriptor) + return FSSH_B_FILE_ERROR; + if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_WRONLY) { + put_fd(descriptor); + return FSSH_B_FILE_ERROR; + } + + if (pos == -1) + pos = descriptor->pos; + + if (descriptor->ops->fd_read) { + for (i = 0; i < count; i++) { + fssh_size_t length = vecs[i].iov_len; + status = descriptor->ops->fd_read(descriptor, pos, vecs[i].iov_base, &length); + if (status < FSSH_B_OK) { + bytesRead = status; + break; + } + + if ((uint32_t)bytesRead + length > SSIZE_MAX) + bytesRead = SSIZE_MAX; + else + bytesRead += (fssh_ssize_t)length; + + pos += vecs[i].iov_len; + } + } else + bytesRead = FSSH_B_BAD_VALUE; + + descriptor->pos = pos; + put_fd(descriptor); + return bytesRead; +} + + +fssh_ssize_t +_kern_write(int fd, fssh_off_t pos, const void *buffer, fssh_size_t length) +{ + struct file_descriptor *descriptor; + fssh_ssize_t bytesWritten; + + descriptor = get_fd(get_current_io_context(true), fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_RDONLY) { + put_fd(descriptor); + return FSSH_B_FILE_ERROR; + } + + if (pos == -1) + pos = descriptor->pos; + + if (descriptor->ops->fd_write) { + bytesWritten = descriptor->ops->fd_write(descriptor, pos, buffer, &length); + if (bytesWritten >= FSSH_B_OK) { + if (length > SSIZE_MAX) + bytesWritten = SSIZE_MAX; + else + bytesWritten = (fssh_ssize_t)length; + + descriptor->pos = pos + length; + } + } else + bytesWritten = FSSH_B_BAD_VALUE; + + put_fd(descriptor); + return bytesWritten; +} + + +fssh_ssize_t +_kern_writev(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count) +{ + struct file_descriptor *descriptor; + fssh_ssize_t bytesWritten = 0; + fssh_status_t status; + uint32_t i; + + descriptor = get_fd(get_current_io_context(true), fd); + if (!descriptor) + return FSSH_B_FILE_ERROR; + if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_RDONLY) { + put_fd(descriptor); + return FSSH_B_FILE_ERROR; + } + + if (pos == -1) + pos = descriptor->pos; + + if (descriptor->ops->fd_write) { + for (i = 0; i < count; i++) { + fssh_size_t length = vecs[i].iov_len; + status = descriptor->ops->fd_write(descriptor, pos, vecs[i].iov_base, &length); + if (status < FSSH_B_OK) { + bytesWritten = status; + break; + } + + if ((uint32_t)bytesWritten + length > SSIZE_MAX) + bytesWritten = SSIZE_MAX; + else + bytesWritten += (fssh_ssize_t)length; + + pos += vecs[i].iov_len; + } + } else + bytesWritten = FSSH_B_BAD_VALUE; + + descriptor->pos = pos; + put_fd(descriptor); + return bytesWritten; +} + + +fssh_off_t +_kern_seek(int fd, fssh_off_t pos, int seekType) +{ + struct file_descriptor *descriptor; + + descriptor = get_fd(get_current_io_context(true), fd); + if (!descriptor) + return FSSH_B_FILE_ERROR; + + if (descriptor->ops->fd_seek) + pos = descriptor->ops->fd_seek(descriptor, pos, seekType); + else + pos = FSSH_ESPIPE; + + put_fd(descriptor); + return pos; +} + + +fssh_status_t +_kern_ioctl(int fd, uint32_t op, void *buffer, fssh_size_t length) +{ + struct file_descriptor *descriptor; + int status; + + TRACE(("sys_ioctl: fd %d\n", fd)); + + descriptor = get_fd(get_current_io_context(true), fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + if (descriptor->ops->fd_ioctl) + status = descriptor->ops->fd_ioctl(descriptor, op, buffer, length); + else + status = FSSH_EOPNOTSUPP; + + put_fd(descriptor); + return status; +} + + +fssh_ssize_t +_kern_read_dir(int fd, struct fssh_dirent *buffer, fssh_size_t bufferSize, uint32_t maxCount) +{ + struct file_descriptor *descriptor; + fssh_ssize_t retval; + + TRACE(("sys_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = %lu)\n",fd, buffer, bufferSize, maxCount)); + + descriptor = get_fd(get_current_io_context(true), fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + if (descriptor->ops->fd_read_dir) { + uint32_t count = maxCount; + retval = descriptor->ops->fd_read_dir(descriptor, buffer, bufferSize, &count); + if (retval >= 0) + retval = count; + } else + retval = FSSH_EOPNOTSUPP; + + put_fd(descriptor); + return retval; +} + + +fssh_status_t +_kern_rewind_dir(int fd) +{ + struct file_descriptor *descriptor; + fssh_status_t status; + + TRACE(("sys_rewind_dir(fd = %d)\n",fd)); + + descriptor = get_fd(get_current_io_context(true), fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + if (descriptor->ops->fd_rewind_dir) + status = descriptor->ops->fd_rewind_dir(descriptor); + else + status = FSSH_EOPNOTSUPP; + + put_fd(descriptor); + return status; +} + + +fssh_status_t +_kern_close(int fd) +{ + return common_close(fd, true); +} + + +int +_kern_dup(int fd) +{ + return dup_fd(fd, true); +} + + +int +_kern_dup2(int ofd, int nfd) +{ + return dup2_fd(ofd, nfd, true); +} + diff --git a/src/tools/fs_shell/fd.h b/src/tools/fs_shell/fd.h new file mode 100644 index 0000000000..eec22f55b3 --- /dev/null +++ b/src/tools/fs_shell/fd.h @@ -0,0 +1,117 @@ +/* + * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_FD_H +#define _FSSH_FD_H + + +#include "vfs.h" + + +namespace FSShell { + + +struct file_descriptor; +struct select_sync; +struct vnode; + +extern io_context gKernelIOContext; + + +struct fd_ops { + fssh_status_t (*fd_read)(struct file_descriptor *, fssh_off_t pos, + void *buffer, fssh_size_t *length); + fssh_status_t (*fd_write)(struct file_descriptor *, fssh_off_t pos, + const void *buffer, fssh_size_t *length); + fssh_off_t (*fd_seek)(struct file_descriptor *, fssh_off_t pos, + int seekType); + fssh_status_t (*fd_ioctl)(struct file_descriptor *, uint32_t op, + void *buffer, fssh_size_t length); + fssh_status_t (*fd_select)(struct file_descriptor *, uint8_t event, + uint32_t ref, struct select_sync *sync); + fssh_status_t (*fd_deselect)(struct file_descriptor *, uint8_t event, + struct select_sync *sync); + fssh_status_t (*fd_read_dir)(struct file_descriptor *, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_count); + fssh_status_t (*fd_rewind_dir)(struct file_descriptor *); + fssh_status_t (*fd_read_stat)(struct file_descriptor *, + struct fssh_stat *); + fssh_status_t (*fd_write_stat)(struct file_descriptor *, + const struct fssh_stat *, int statMask); + fssh_status_t (*fd_close)(struct file_descriptor *); + void (*fd_free)(struct file_descriptor *); +}; + +struct file_descriptor { + int32_t type; /* descriptor type */ + int32_t ref_count; + int32_t open_count; + struct fd_ops* ops; + union { + struct vnode* vnode; + struct fs_mount* mount; + } u; + void* cookie; + int32_t open_mode; + fssh_off_t pos; +}; + + +/* Types of file descriptors we can create */ + +enum fd_types { + FDTYPE_FILE = 1, + FDTYPE_ATTR, + FDTYPE_DIR, + FDTYPE_ATTR_DIR, + FDTYPE_INDEX, + FDTYPE_INDEX_DIR, + FDTYPE_QUERY, + FDTYPE_SOCKET +}; + +// additional open mode - kernel special +#define FSSH_O_DISCONNECTED 0x80000000 + +/* Prototypes */ + +extern file_descriptor* alloc_fd(void); +extern int new_fd_etc(struct io_context *, + struct file_descriptor *, int firstIndex); +extern int new_fd(struct io_context *, struct file_descriptor *); +extern file_descriptor* get_fd(struct io_context *, int); +extern void close_fd(struct file_descriptor *descriptor); +extern void put_fd(struct file_descriptor *descriptor); +extern void disconnect_fd(struct file_descriptor *descriptor); +extern void inc_fd_ref_count(struct file_descriptor *descriptor); +extern fssh_status_t select_fd(int fd, uint8_t event, uint32_t ref, + struct select_sync *sync, bool kernel); +extern fssh_status_t deselect_fd(int fd, uint8_t event, + struct select_sync *sync, bool kernel); +extern bool fd_is_valid(int fd, bool kernel); +extern vnode* fd_vnode(struct file_descriptor *descriptor); + +extern bool fd_close_on_exec(struct io_context *context, int fd); +extern void fd_set_close_on_exec(struct io_context *context, int fd, + bool closeFD); + +static io_context* get_current_io_context(bool kernel); + +/* The prototypes of the (sys|user)_ functions are currently defined in vfs.h */ + + +/* Inlines */ + +static inline struct io_context * +get_current_io_context(bool /*kernel*/) +{ + return &gKernelIOContext; +} + + +} // namespace FSShell + + +#endif /* _FSSH_FD_H */ diff --git a/src/tools/fs_shell/file_cache.cpp b/src/tools/fs_shell/file_cache.cpp new file mode 100644 index 0000000000..4f1460ca50 --- /dev/null +++ b/src/tools/fs_shell/file_cache.cpp @@ -0,0 +1,1872 @@ +/* + * Copyright 2004-2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler + * Ingo Weinhold + */ + +#include "fssh_fs_cache.h" + +#include + +#include + +#include "DoublyLinkedList.h" +#include "fssh_kernel_export.h" +#include "fssh_stdio.h" +#include "fssh_string.h" +#include "fssh_uio.h" +#include "fssh_unistd.h" +#include "hash.h" +#include "lock.h" +#include "vfs.h" + + +#undef TRACE +//#define TRACE_FILE_CACHE +#ifdef TRACE_FILE_CACHE +# define TRACE(x) fssh_dprintf x +#else +# define TRACE(x) ; +#endif + +using std::nothrow; + + +// This is a hacked version of the kernel file cache implementation. The main +// part of the implementation didn't change that much -- some code not needed +// in userland has been removed, file_cache_ref, vm_cache_ref and vm_cache +// have been unified, and the complete interface to the VM (vm_*()) and the +// VFS (vfs_*()) has been re-implemented to suit our needs. +// +// The PagePool class is a data structure used for managing the pages (vm_page) +// allocated and assigned to caches (file_cache_ref). It has a list for free +// pages, i.e. those that are not assigned to any cache and can be reused at +// once. A second list contains those pages that belong to a cache, but are +// not in use by any of the functions. These pages have a reference count of +// 0. They will be stolen from the owning cache when a usable page is needed, +// there are no free pages anymore, and the limit of pages that shall be used +// at maximum has already been reached. +// +// The only purpose of the page reference count (vm_page::ref_count) is to +// indicate whether the page is in use (i.e. known to one or more of the cache +// functions currently being executed). vm_page_get_page(), +// vm_page_allocate_page(), and vm_cache_lookup_page acquire a reference to +// the page, vm_page_put_page() releases a reference. + +// vm_page::state indicates in what state +// a page currently is. PAGE_STATE_FREE is only encountered for pages not +// belonging to a cache (being in page pool's free pages list, or just having +// been removed or not yet added). PAGE_STATE_ACTIVE/MODIFIED indicate that +// the page contains valid file data, in the latter case not yet written to +// disk. PAGE_STATE_BUSY means that the page is currently being manipulated by +// an operation, usually meaning that page data are being read from/written to +// disk. The required behavior when encountering a busy page, is to put the +// page, release the page pool and cache locks, wait a short time, and +// retry again later. +// +// Changing the page state requires having a reference to the page, +// holding the lock of the owning cache (if any) and the lock of the page pool +// (in case the page is not newly allocated). +// +// A page is in up to three lists. The free or unused list in the page pool +// (guarded by the page pool lock), the pages list of the cache the page +// belongs to, and the modified pages list of the cache (both cache lists +// are guarded by both the page pool and the cache lock). The modified pages +// list only contains PAGE_STATE_MODIFIED or PAGE_STATE_BUSY pages. + + +// maximum number of iovecs per request +#define MAX_IO_VECS 64 // 256 kB +#define MAX_FILE_IO_VECS 32 +#define MAX_TEMP_IO_VECS 8 + +#define CACHED_FILE_EXTENTS 2 + // must be smaller than MAX_FILE_IO_VECS + // ToDo: find out how much of these are typically used + +#define user_memcpy(a, b, c) fssh_memcpy(a, b, c) + +#define PAGE_ALIGN(x) (((x) + (FSSH_B_PAGE_SIZE - 1)) & ~(FSSH_B_PAGE_SIZE - 1)) +#define ASSERT(x) + +namespace FSShell { + +enum { + PAGE_STATE_ACTIVE = 0, + PAGE_STATE_BUSY, + PAGE_STATE_MODIFIED, + PAGE_STATE_FREE +}; + +enum { + PHYSICAL_PAGE_NO_WAIT = 0, + PHYSICAL_PAGE_CAN_WAIT, +}; + +struct vm_page; +struct file_cache_ref; + +typedef DoublyLinkedListLink page_link; + +// vm page +struct vm_page { + vm_page* next; + page_link link_cache; + page_link link_cache_modified; + page_link link_pool; + file_cache_ref* cache; + fssh_off_t offset; + void* data; + uint8_t state; + uint32_t ref_count; + + vm_page() + : next(NULL), + cache(NULL), + offset(0), + data(malloc(FSSH_B_PAGE_SIZE)), + state(PAGE_STATE_FREE), + ref_count(1) + { + } + + ~vm_page() + { + free(data); + } + + fssh_addr_t Address() const { return (fssh_addr_t)data; } + + static int Compare(void *_cacheEntry, const void *_key); + static uint32_t Hash(void *_cacheEntry, const void *_key, uint32_t range); +}; + +struct file_extent { + fssh_off_t offset; + fssh_file_io_vec disk; +}; + +struct file_map { + file_map(); + ~file_map(); + + file_extent *operator[](uint32_t index); + file_extent *ExtentAt(uint32_t index); + fssh_status_t Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, fssh_off_t &lastOffset); + void Free(); + + union { + file_extent direct[CACHED_FILE_EXTENTS]; + file_extent *array; + }; + fssh_size_t count; +}; + + +static vm_page *vm_page_allocate_page(int state); +static void vm_page_get_page(vm_page* page); +static fssh_status_t vm_page_put_page(vm_page* page); +static fssh_status_t vm_page_set_state(vm_page *page, int state); + +static void vm_cache_insert_page(file_cache_ref *cacheRef, vm_page *page, + fssh_off_t offset); +static void vm_cache_remove_page(file_cache_ref *cacheRef, vm_page *page); +static vm_page *vm_cache_lookup_page(file_cache_ref *cacheRef, fssh_off_t page); +static fssh_status_t vm_cache_resize(file_cache_ref *cacheRef, fssh_off_t newSize); +static fssh_status_t vm_cache_write_modified(file_cache_ref *ref, bool fsReenter); + +static fssh_status_t vfs_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes, bool fsReenter); +static fssh_status_t vfs_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes, bool fsReenter); + +static fssh_status_t pages_io(file_cache_ref *ref, fssh_off_t offset, const fssh_iovec *vecs, + fssh_size_t count, fssh_size_t *_numBytes, bool doWrite); + + +typedef DoublyLinkedList > cache_modified_page_list; + +typedef DoublyLinkedList > cache_page_list; + +struct file_cache_ref { + mutex lock; + fssh_mount_id mountID; + fssh_vnode_id nodeID; + void* node; + int deviceFD; + fssh_off_t virtual_size; + + cache_page_list pages; + cache_modified_page_list modifiedPages; + + file_map map; +}; + +struct page_hash_key { + page_hash_key() {} + page_hash_key(int fd, fssh_vnode_id id, fssh_off_t offset) + : deviceFD(fd), + nodeID(id), + offset(offset) + { + } + + int deviceFD; + fssh_vnode_id nodeID; + fssh_off_t offset; +}; + + +typedef DoublyLinkedList > pool_page_list; + +struct PagePool { + + PagePool() + : pageHash(NULL), + unusedPages(), + freePages(), + allocatedPages(0) + { + } + + ~PagePool() + { + } + + fssh_status_t Init() + { + fssh_status_t error = recursive_lock_init(&lock, "page pool"); + if (error != FSSH_B_OK) { + fssh_panic("PagePool: Failed to init lock\n"); + return error; + } + + pageHash = hash_init(256, 0, &vm_page::Compare, &vm_page::Hash); + if (pageHash == NULL) { + fssh_panic("PagePool: Failed to create page hash\n"); + return FSSH_B_NO_MEMORY; + } + + return FSSH_B_OK; + } + + bool Lock() { return (recursive_lock_lock(&lock) == FSSH_B_OK); } + void Unlock() { recursive_lock_unlock(&lock); } + + recursive_lock lock; + hash_table* pageHash; + pool_page_list unusedPages; + pool_page_list freePages; + uint32_t allocatedPages; +}; + +struct PagePutter { + PagePutter(vm_page* page) + : fPage(page) + { + } + + ~PagePutter() + { + if (fPage) + vm_page_put_page(fPage); + } + +private: + vm_page* fPage; +}; + +static PagePool sPagePool; +static const uint32_t kMaxAllocatedPages = 1024; + + +int +vm_page::Compare(void *_cacheEntry, const void *_key) +{ + vm_page *page = (vm_page*)_cacheEntry; + const page_hash_key *key = (const page_hash_key *)_key; + + // device FD + if (page->cache->deviceFD != key->deviceFD) + return page->cache->deviceFD - key->deviceFD; + + // node ID + if (page->cache->nodeID < key->nodeID) + return -1; + if (page->cache->nodeID > key->nodeID) + return 1; + + // offset + if (page->offset < key->offset) + return -1; + if (page->offset > key->offset) + return 1; + + return 0; +} + +uint32_t +vm_page::Hash(void *_cacheEntry, const void *_key, uint32_t range) +{ + vm_page *page = (vm_page*)_cacheEntry; + const page_hash_key *key = (const page_hash_key *)_key; + + int fd = (page ? page->cache->deviceFD : key->deviceFD); + fssh_vnode_id id = (page ? page->cache->nodeID : key->nodeID); + fssh_off_t offset = (page ? page->offset : key->offset); + + uint32_t value = fd; + value = value * 17 + id; + value = value * 17 + offset; + + return value % range; +} + + +vm_page * +vm_page_allocate_page(int state) +{ + AutoLocker poolLocker(sPagePool); + + // is a queued free page available? + vm_page* page = sPagePool.freePages.RemoveHead(); + if (page) { + page->ref_count++; + return page; + } + + // no free page + +#if 0 +// TODO: Nice idea in principle, but causes a serious locking problem. +// vm_page_allocate_page() is always invoked with some cached locked at the +// same time. Thus locking a cache here to steal a page can cause a deadlock. + // If the limit for allocated pages has been reached, we try to steal an + // unused page. + if (sPagePool.allocatedPages >= kMaxAllocatedPages + && !sPagePool.unusedPages.IsEmpty()) { + // we grab the first non-busy page + for (pool_page_list::Iterator it(&sPagePool.unusedPages); + vm_page* currentPage = it.Next();) { + if (currentPage->state != PAGE_STATE_BUSY) { + it.Remove(); + page = currentPage; + page->ref_count++; + break; + } + } + + // If we've found a suitable page, we need to mark it busy, write it + // if it was modified, and finally remove it from its cache. + if (page != NULL) { + bool modified = (page->state == PAGE_STATE_MODIFIED); + + // mark the page busy + page->state = PAGE_STATE_BUSY; + + // We don't need the pool lock anymore. + poolLocker.Unlock(); + + file_cache_ref* cache = page->cache; + + // If the page is modified, we write it to the disk. + if (modified) { + // find out, how much to write, and remove the page from the + // cache's modified pages list + MutexLocker cacheLocker(cache->lock); + fssh_size_t bytes = fssh_min_c(FSSH_B_PAGE_SIZE, + cache->virtual_size - page->offset); + cache->modifiedPages.Remove(page); + cacheLocker.Unlock(); + + // if we need to write anything, do it now + if (bytes > 0) { + fssh_iovec vecs[1]; + vecs[0].iov_base = page->data; + vecs[0].iov_len = bytes; + fssh_status_t error = pages_io(cache, page->offset, vecs, 1, + &bytes, true); + if (error != FSSH_B_OK) { + // failed to write the page: bad, but we can't do + // much about it + fssh_dprintf("vm_page_allocate_page(): Failed to write " + "page %p (cache %p, offset: %lld).\n", page, + cache, page->offset); + } + } + } + + // remove the page from the cache + MutexLocker cacheLocker(cache->lock); + vm_cache_remove_page(cache, page); + + // now it's ours + page->state = PAGE_STATE_FREE; + + return page; + } + } +#endif // 0 + + // no page yet -- allocate a new one + + page = new(nothrow) vm_page; + if (!page || !page->data) { + delete page; + return NULL; + } + + sPagePool.allocatedPages++; + + return page; +} + + +static void +vm_page_get_page(vm_page* page) +{ + if (page) { + AutoLocker _(sPagePool); + + // increase ref count + page->ref_count++; + + // if the page was unused before, remove it from the unused pages list + if (page->ref_count == 1) + sPagePool.unusedPages.Remove(page); + } +} + + +static fssh_status_t +vm_page_put_page(vm_page* page) +{ + if (!page) + return FSSH_B_BAD_VALUE; + + AutoLocker _(sPagePool); + + if (page->ref_count <= 0) { + fssh_panic("vm_page_put_page(): page %p already unreferenced!\n", page); + return FSSH_B_BAD_VALUE; + } + + // decrease ref count + page->ref_count--; + + if (page->ref_count > 0) + return FSSH_B_OK; + + // the page is unreference now: add it to the unused or free pages list + + if (page->state == PAGE_STATE_FREE) { + // page is free + // if we've maxed out the allowed allocated page, free this one, + // otherwise add it to the free list + if (sPagePool.allocatedPages > kMaxAllocatedPages) { + delete page; + sPagePool.allocatedPages--; + } else + sPagePool.freePages.Add(page); + } else { + // page is is not free; add to unused pages list + sPagePool.unusedPages.Add(page); + } + + return FSSH_B_OK; +} + + +fssh_status_t +vm_page_set_state(vm_page *page, int state) +{ + AutoLocker _(sPagePool); + + if (page->ref_count <= 0) { + fssh_panic("vm_page_set_state(): page %p is already unreferenced!\n", + page); + return FSSH_B_BAD_VALUE; + } + + if (state == page->state) + return FSSH_B_OK; + + // If it was modified before, remove the page from the cache's modified + // pages list. + if (page->state == PAGE_STATE_MODIFIED && page->cache) + page->cache->modifiedPages.Remove(page); + + switch (state) { + case PAGE_STATE_ACTIVE: + case PAGE_STATE_BUSY: + case PAGE_STATE_FREE: + page->state = state; + break; + + case PAGE_STATE_MODIFIED: + { + page->state = state; + + // add page to the modified list of the cache + if (!page->cache) { + fssh_panic("vm_page_set_state(): setting page state of page %p " + "to PAGE_STATE_MODIFIED, but page is not in a cache\n", + page); + return FSSH_B_BAD_VALUE; + } + + page->cache->modifiedPages.Add(page); + + break; + } + + default: + fssh_panic("vm_page_set_state(): invalid page state: %d\n", state); + return FSSH_B_BAD_VALUE; + } + + return FSSH_B_OK; +} + + +// cache must be locked +// +void +vm_cache_insert_page(file_cache_ref *cache, vm_page *page, fssh_off_t offset) +{ + AutoLocker _(sPagePool); + + if (page->cache != NULL) { + fssh_panic("vm_cache_insert_page(%p, %p): page already in cache %p\n", + cache, page, page->cache); + return; + } + + page->cache = cache; + page->offset = offset; + + // insert page into hash + fssh_status_t error = hash_insert(sPagePool.pageHash, page); + if (error != FSSH_B_OK) { + fssh_panic("vm_cache_insert_page(): Failed to insert page %p into hash!\n", + page); + page->cache = NULL; + page->offset = 0; + return; + } + + // add page to cache page list + cache->pages.Add(page); +} + + +// cache must be locked +// +void +vm_cache_remove_page(file_cache_ref *cache, vm_page *page) +{ + if (cache != page->cache) { + fssh_panic("vm_cache_remove_page(%p, %p): page is in cache %p\n", + cache, page, page->cache); + return; + } + + AutoLocker _(sPagePool); + + if (page->state == PAGE_STATE_MODIFIED) + cache->modifiedPages.Remove(page); + + cache->pages.Remove(page); + + hash_remove(sPagePool.pageHash, page); + + page->cache = NULL; + page->offset = 0; +} + + +vm_page * +vm_cache_lookup_page(file_cache_ref *cache, fssh_off_t offset) +{ + if (!cache) + return NULL; + + AutoLocker _(sPagePool); + + page_hash_key key(cache->deviceFD, cache->nodeID, offset); + + vm_page* page = (vm_page*)hash_lookup(sPagePool.pageHash, &key); + + if (page) + vm_page_get_page(page); + + return page; +} + + +// cache must be locked +// +fssh_status_t +vm_cache_resize(file_cache_ref *cache, fssh_off_t newSize) +{ + fssh_off_t oldAlignedSize = PAGE_ALIGN(cache->virtual_size); + fssh_off_t newAlignedSize = PAGE_ALIGN(newSize); + + cache->virtual_size = newSize; + + // if the aligned cache size increased or remained the same, we're done + if (newAlignedSize >= oldAlignedSize) + return FSSH_B_OK; + + // the file shrinks, so we need to get rid of excess pages + + // Hold the page pool lock virtually all the time from now on. + AutoLocker poolLocker(sPagePool); + + // For sake of efficiency we sort the cache's list of pages so that all + // pages that need to be removed are at the beginning of the list. + vm_page* page = cache->pages.Head(); + if (newAlignedSize > 0) { + while (page) { + vm_page* nextPage = cache->pages.GetNext(page); + + if (page->offset >= newAlignedSize) { + // move to the beginning of the list + cache->pages.Remove(page); + cache->pages.Add(page, false); + } + + page = nextPage; + } + } + + // now we remove and free the excess pages one by one + while (true) { + // get the first page in the list to remove + // (since we sorted the list, this is usually very cheap) + for (cache_page_list::Iterator it(&cache->pages); (page = it.Next());) { + if (page->offset >= newAlignedSize) + break; + } + + // no more pages? -- then we're done + if (!page) + return FSSH_B_OK; + + if (page->state == PAGE_STATE_BUSY) { + // the page is busy -- wait a while and try again + poolLocker.Unlock(); + mutex_unlock(&cache->lock); + fssh_snooze(20000); + mutex_lock(&cache->lock); + poolLocker.Lock(); + } else { + // the page isn't busy -- get rid of it + vm_page_get_page(page); + vm_cache_remove_page(cache, page); + vm_page_set_state(page, PAGE_STATE_FREE); + vm_page_put_page(page); + } + } + + return FSSH_B_OK; +} + + +fssh_status_t +vm_cache_write_modified(file_cache_ref *cache, bool fsReenter) +{ + // TODO: Write more than one page at a time. To avoid deadlocks, when a + // busy page is encountered the previously collected pages need to be + // written. Otherwise as many pages as our on-stack array can contain + // can be processed at once. + MutexLocker locker(cache->lock); + while (true) { + // get the next modified page and mark it busy + vm_page* page = NULL; + + while (true) { + // get the first modified page + AutoLocker poolLocker(sPagePool); + page = cache->modifiedPages.Head(); + + if (!page) + return FSSH_B_OK; + + // if not marked busy, remove it and mark it busy + if (page->state != PAGE_STATE_BUSY) { + cache->modifiedPages.Remove(page); + vm_page_get_page(page); + page->state = PAGE_STATE_BUSY; + break; + } + + // page is busy -- wait a while + vm_page_put_page(page); + poolLocker.Unlock(); + locker.Unlock(); + fssh_snooze(20000); + locker.Lock(); + } + + locker.Unlock(); + + // write the page + fssh_size_t bytes = fssh_min_c(FSSH_B_PAGE_SIZE, cache->virtual_size - page->offset); + fssh_iovec vecs[1]; + vecs[0].iov_base = page->data; + vecs[0].iov_len = bytes; + fssh_status_t error = pages_io(cache, page->offset, vecs, 1, &bytes, true); + if (error != FSSH_B_OK) + return error; + + locker.Lock(); + + vm_page_set_state(page, PAGE_STATE_ACTIVE); + vm_page_put_page(page); + } + + return FSSH_B_OK; +} + + +fssh_status_t +vfs_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count, + fssh_size_t *_numBytes, bool fsReenter) +{ + // check how much the iovecs allow us to read + fssh_size_t toRead = 0; + for (fssh_size_t i = 0; i < count; i++) + toRead += vecs[i].iov_len; + + fssh_iovec* newVecs = NULL; + if (*_numBytes < toRead) { + // We're supposed to read less than specified by the vecs. Since + // readv_pos() doesn't support this, we need to clone the vecs. + newVecs = new(nothrow) fssh_iovec[count]; + if (!newVecs) + return FSSH_B_NO_MEMORY; + + fssh_size_t newCount = 0; + for (fssh_size_t i = 0; i < count && toRead > 0; i++) { + fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead); + newVecs[i].iov_base = vecs[i].iov_base; + newVecs[i].iov_len = vecLen; + toRead -= vecLen; + newCount++; + } + + vecs = newVecs; + count = newCount; + } + + fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count); + delete[] newVecs; + if (bytesRead < 0) + return bytesRead; + + *_numBytes = bytesRead; + return FSSH_B_OK; +} + + +fssh_status_t +vfs_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count, + fssh_size_t *_numBytes, bool fsReenter) +{ + // check how much the iovecs allow us to write + fssh_size_t toWrite = 0; + for (fssh_size_t i = 0; i < count; i++) + toWrite += vecs[i].iov_len; + + fssh_iovec* newVecs = NULL; + if (*_numBytes < toWrite) { + // We're supposed to write less than specified by the vecs. Since + // writev_pos() doesn't support this, we need to clone the vecs. + newVecs = new(nothrow) fssh_iovec[count]; + if (!newVecs) + return FSSH_B_NO_MEMORY; + + fssh_size_t newCount = 0; + for (fssh_size_t i = 0; i < count && toWrite > 0; i++) { + fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite); + newVecs[i].iov_base = vecs[i].iov_base; + newVecs[i].iov_len = vecLen; + toWrite -= vecLen; + newCount++; + } + + vecs = newVecs; + count = newCount; + } + + fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count); + delete[] newVecs; + if (bytesWritten < 0) + return bytesWritten; + + *_numBytes = bytesWritten; + return FSSH_B_OK; +} + + +fssh_status_t +file_cache_init() +{ + fssh_status_t error = sPagePool.Init(); + if (error != FSSH_B_OK) + return error; + + return FSSH_B_OK; +} + + +// #pragma mark - + + +file_map::file_map() +{ + array = NULL; + count = 0; +} + + +file_map::~file_map() +{ + Free(); +} + + +file_extent * +file_map::operator[](uint32_t index) +{ + return ExtentAt(index); +} + + +file_extent * +file_map::ExtentAt(uint32_t index) +{ + if (index >= count) + return NULL; + + if (count > CACHED_FILE_EXTENTS) + return &array[index]; + + return &direct[index]; +} + + +fssh_status_t +file_map::Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, fssh_off_t &lastOffset) +{ + TRACE(("file_map::Add(vecCount = %ld)\n", vecCount)); + + fssh_off_t offset = 0; + + if (vecCount <= CACHED_FILE_EXTENTS && count == 0) { + // just use the reserved area in the file_cache_ref structure + } else { + // TODO: once we can invalidate only parts of the file map, + // we might need to copy the previously cached file extends + // from the direct range + file_extent *newMap = (file_extent *)realloc(array, + (count + vecCount) * sizeof(file_extent)); + if (newMap == NULL) + return FSSH_B_NO_MEMORY; + + array = newMap; + + if (count != 0) { + file_extent *extent = ExtentAt(count - 1); + offset = extent->offset + extent->disk.length; + } + } + + int32_t start = count; + count += vecCount; + + for (uint32_t i = 0; i < vecCount; i++) { + file_extent *extent = ExtentAt(start + i); + + extent->offset = offset; + extent->disk = vecs[i]; + + offset += extent->disk.length; + } + +#ifdef TRACE_FILE_CACHE + for (uint32_t i = 0; i < count; i++) { + file_extent *extent = ExtentAt(i); + fssh_dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n", + i, extent->offset, extent->disk.offset, extent->disk.length); + } +#endif + + lastOffset = offset; + return FSSH_B_OK; +} + + +void +file_map::Free() +{ + if (count > CACHED_FILE_EXTENTS) + free(array); + + array = NULL; + count = 0; +} + + +// #pragma mark - + + +static void +add_to_iovec(fssh_iovec *vecs, int32_t &index, int32_t max, fssh_addr_t address, fssh_size_t size) +{ + if (index > 0 && (fssh_addr_t)vecs[index - 1].iov_base + vecs[index - 1].iov_len == address) { + // the iovec can be combined with the previous one + vecs[index - 1].iov_len += size; + return; + } + + if (index == max) + fssh_panic("no more space for iovecs!"); + + // we need to start a new iovec + vecs[index].iov_base = (void *)address; + vecs[index].iov_len = size; + index++; +} + + +static file_extent * +find_file_extent(file_cache_ref *ref, fssh_off_t offset, uint32_t *_index) +{ + // TODO: do binary search + + for (uint32_t index = 0; index < ref->map.count; index++) { + file_extent *extent = ref->map[index]; + + if (extent->offset <= offset + && extent->offset + extent->disk.length > offset) { + if (_index) + *_index = index; + return extent; + } + } + + return NULL; +} + + +static fssh_status_t +get_file_map(file_cache_ref *ref, fssh_off_t offset, fssh_size_t size, + fssh_file_io_vec *vecs, fssh_size_t *_count) +{ + fssh_size_t maxVecs = *_count; + fssh_status_t status = FSSH_B_OK; + + if (ref->map.count == 0) { + // we don't yet have the map of this file, so let's grab it + // (ordered by offset, so that we can do a binary search on them) + + mutex_lock(&ref->lock); + + // the file map could have been requested in the mean time + if (ref->map.count == 0) { + fssh_size_t vecCount = maxVecs; + fssh_off_t mapOffset = 0; + + while (true) { + status = vfs_get_file_map(ref->node, mapOffset, ~0UL, vecs, + &vecCount); + if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW) { + mutex_unlock(&ref->lock); + return status; + } + + fssh_status_t addStatus = ref->map.Add(vecs, vecCount, mapOffset); + if (addStatus != FSSH_B_OK) { + // only clobber the status in case of failure + status = addStatus; + } + + if (status != FSSH_B_BUFFER_OVERFLOW) + break; + + // when we are here, the map has been stored in the array, and + // the array size was still too small to cover the whole file + vecCount = maxVecs; + } + } + + mutex_unlock(&ref->lock); + } + + if (status != FSSH_B_OK) { + // We must invalidate the (part of the) map we already + // have, as we cannot know if it's complete or not + ref->map.Free(); + return status; + } + + // We now have cached the map of this file, we now need to + // translate it for the requested access. + + uint32_t index; + file_extent *fileExtent = find_file_extent(ref, offset, &index); + if (fileExtent == NULL) { + // access outside file bounds? But that's not our problem + *_count = 0; + return FSSH_B_OK; + } + + offset -= fileExtent->offset; + vecs[0].offset = fileExtent->disk.offset + offset; + vecs[0].length = fileExtent->disk.length - offset; + + if (vecs[0].length >= size || index >= ref->map.count - 1) { + *_count = 1; + return FSSH_B_OK; + } + + // copy the rest of the vecs + + size -= vecs[0].length; + + for (index = 1; index < ref->map.count;) { + fileExtent++; + + vecs[index] = fileExtent->disk; + index++; + + if (size <= fileExtent->disk.length) + break; + + if (index >= maxVecs) { + *_count = index; + return FSSH_B_BUFFER_OVERFLOW; + } + + size -= fileExtent->disk.length; + } + + *_count = index; + return FSSH_B_OK; +} + + +/*! + Does the dirty work of translating the request into actual disk offsets + and reads to or writes from the supplied iovecs as specified by \a doWrite. +*/ +static fssh_status_t +pages_io(file_cache_ref *ref, fssh_off_t offset, const fssh_iovec *vecs, fssh_size_t count, + fssh_size_t *_numBytes, bool doWrite) +{ + TRACE(("pages_io: ref = %p, offset = %Ld, size = %lu, vecCount = %lu, %s\n", + ref, offset, *_numBytes, count, doWrite ? "write" : "read")); + + // translate the iovecs into direct device accesses + fssh_file_io_vec fileVecs[MAX_FILE_IO_VECS]; + fssh_size_t fileVecCount = MAX_FILE_IO_VECS; + fssh_size_t numBytes = *_numBytes; + + fssh_status_t status = get_file_map(ref, offset, numBytes, fileVecs, + &fileVecCount); + if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW) { + TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n", + offset, numBytes, fssh_strerror(status))); + return status; + } + + bool bufferOverflow = status == FSSH_B_BUFFER_OVERFLOW; + +#ifdef TRACE_FILE_CACHE + fssh_dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount, offset, + numBytes, bufferOverflow ? " (array too small)" : ""); + for (fssh_size_t i = 0; i < fileVecCount; i++) { + fssh_dprintf(" [%lu] offset = %Ld, size = %Ld\n", + i, fileVecs[i].offset, fileVecs[i].length); + } +#endif + + if (fileVecCount == 0) { + // There are no file vecs at this offset, so we're obviously trying + // to access the file outside of its bounds + TRACE(("pages_io: access outside of vnode %p at offset %Ld\n", + ref->vnode, offset)); + return FSSH_B_BAD_VALUE; + } + + uint32_t fileVecIndex; + fssh_size_t size; + + if (!doWrite) { + // now directly read the data from the device + // the first file_io_vec can be read directly + + size = fileVecs[0].length; + if (size > numBytes) + size = numBytes; + + status = vfs_read_pages(ref->deviceFD, fileVecs[0].offset, vecs, + count, &size, false); + if (status < FSSH_B_OK) + return status; + + // TODO: this is a work-around for buggy device drivers! + // When our own drivers honour the length, we can: + // a) also use this direct I/O for writes (otherwise, it would + // overwrite precious data) + // b) panic if the term below is true (at least for writes) + if (size > fileVecs[0].length) { + //fssh_dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device); + size = fileVecs[0].length; + } + + ASSERT(size <= fileVecs[0].length); + + // If the file portion was contiguous, we're already done now + if (size == numBytes) + return FSSH_B_OK; + + // if we reached the end of the file, we can return as well + if (size != fileVecs[0].length) { + *_numBytes = size; + return FSSH_B_OK; + } + + fileVecIndex = 1; + } else { + fileVecIndex = 0; + size = 0; + } + + // Too bad, let's process the rest of the file_io_vecs + + fssh_size_t totalSize = size; + + // first, find out where we have to continue in our iovecs + uint32_t i = 0; + for (; i < count; i++) { + if (size < vecs[i].iov_len) + break; + + size -= vecs[i].iov_len; + } + + fssh_size_t vecOffset = size; + fssh_size_t bytesLeft = numBytes - size; + + while (true) { + for (; fileVecIndex < fileVecCount; fileVecIndex++) { + fssh_file_io_vec &fileVec = fileVecs[fileVecIndex]; + fssh_off_t fileOffset = fileVec.offset; + fssh_off_t fileLeft = fssh_min_c(fileVec.length, bytesLeft); + + TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft)); + + // process the complete fileVec + while (fileLeft > 0) { + fssh_iovec tempVecs[MAX_TEMP_IO_VECS]; + uint32_t tempCount = 0; + + // size tracks how much of what is left of the current fileVec + // (fileLeft) has been assigned to tempVecs + size = 0; + + // assign what is left of the current fileVec to the tempVecs + for (size = 0; size < fileLeft && i < count + && tempCount < MAX_TEMP_IO_VECS;) { + // try to satisfy one iovec per iteration (or as much as + // possible) + + // bytes left of the current iovec + fssh_size_t vecLeft = vecs[i].iov_len - vecOffset; + if (vecLeft == 0) { + vecOffset = 0; + i++; + continue; + } + + TRACE(("fill vec %ld, offset = %lu, size = %lu\n", + i, vecOffset, size)); + + // actually available bytes + fssh_size_t tempVecSize = fssh_min_c(vecLeft, fileLeft - size); + + tempVecs[tempCount].iov_base + = (void *)((fssh_addr_t)vecs[i].iov_base + vecOffset); + tempVecs[tempCount].iov_len = tempVecSize; + tempCount++; + + size += tempVecSize; + vecOffset += tempVecSize; + } + + fssh_size_t bytes = size; + if (doWrite) { + status = vfs_write_pages(ref->deviceFD, fileOffset, + tempVecs, tempCount, &bytes, false); + } else { + status = vfs_read_pages(ref->deviceFD, fileOffset, + tempVecs, tempCount, &bytes, false); + } + if (status < FSSH_B_OK) + return status; + + totalSize += bytes; + bytesLeft -= size; + fileOffset += size; + fileLeft -= size; + //fssh_dprintf("-> file left = %Lu\n", fileLeft); + + if (size != bytes || i >= count) { + // there are no more bytes or iovecs, let's bail out + *_numBytes = totalSize; + return FSSH_B_OK; + } + } + } + + if (bufferOverflow) { + status = get_file_map(ref, offset + totalSize, bytesLeft, fileVecs, + &fileVecCount); + if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW) { + TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n", + offset, numBytes, fssh_strerror(status))); + return status; + } + + bufferOverflow = status == FSSH_B_BUFFER_OVERFLOW; + fileVecIndex = 0; + +#ifdef TRACE_FILE_CACHE + fssh_dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount, + offset + totalSize, numBytes, + bufferOverflow ? " (array too small)" : ""); + for (fssh_size_t i = 0; i < fileVecCount; i++) { + fssh_dprintf(" [%lu] offset = %Ld, size = %Ld\n", + i, fileVecs[i].offset, fileVecs[i].length); + } +#endif + } else + break; + } + + *_numBytes = totalSize; + return FSSH_B_OK; +} + + +/*! + This function is called by read_into_cache() (and from there only) - it + can only handle a certain amount of bytes, and read_into_cache() makes + sure that it matches that criterion. +*/ +static inline fssh_status_t +read_chunk_into_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t numBytes, + int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize) +{ + TRACE(("read_chunk(offset = %Ld, size = %lu, pageOffset = %ld, buffer = %#lx, bufferSize = %lu\n", + offset, size, pageOffset, buffer, bufferSize)); + + fssh_iovec vecs[MAX_IO_VECS]; + int32_t vecCount = 0; + + vm_page *pages[MAX_IO_VECS]; + int32_t pageIndex = 0; + + // allocate pages for the cache and mark them busy + for (fssh_size_t pos = 0; pos < numBytes; pos += FSSH_B_PAGE_SIZE) { + vm_page *page = pages[pageIndex++] = vm_page_allocate_page(PAGE_STATE_FREE); + if (page == NULL) + fssh_panic("no more pages!"); + + page->state = PAGE_STATE_BUSY; + + vm_cache_insert_page(ref, page, offset + pos); + + fssh_addr_t virtualAddress = page->Address(); + + add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, FSSH_B_PAGE_SIZE); + // ToDo: check if the array is large enough! + } + + mutex_unlock(&ref->lock); + + // read file into reserved pages + fssh_status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes, false); + if (status < FSSH_B_OK) { + // reading failed, free allocated pages + + fssh_dprintf("file_cache: read pages failed: %s\n", fssh_strerror(status)); + + mutex_lock(&ref->lock); + + for (int32_t i = 0; i < pageIndex; i++) { + vm_cache_remove_page(ref, pages[i]); + vm_page_set_state(pages[i], PAGE_STATE_FREE); + vm_page_put_page(pages[i]); + } + + return status; + } + + // copy the pages and unmap them again + + for (int32_t i = 0; i < vecCount; i++) { + fssh_addr_t base = (fssh_addr_t)vecs[i].iov_base; + fssh_size_t size = vecs[i].iov_len; + + // copy to user buffer if necessary + if (bufferSize != 0) { + fssh_size_t bytes = fssh_min_c(bufferSize, size - pageOffset); + + user_memcpy((void *)buffer, (void *)(base + pageOffset), bytes); + buffer += bytes; + bufferSize -= bytes; + pageOffset = 0; + } + } + + mutex_lock(&ref->lock); + + // make the pages accessible in the cache + for (int32_t i = pageIndex; i-- > 0;) { + vm_page_set_state(pages[i], PAGE_STATE_ACTIVE); + vm_page_put_page(pages[i]); + } + + return FSSH_B_OK; +} + + +/*! + This function reads \a size bytes directly from the file into the cache. + If \a bufferSize does not equal zero, \a bufferSize bytes from the data + read in are also copied to the provided \a buffer. + This function always allocates all pages; it is the responsibility of the + calling function to only ask for yet uncached ranges. + The cache_ref lock must be hold when calling this function. +*/ +static fssh_status_t +read_into_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t size, fssh_addr_t buffer, fssh_size_t bufferSize) +{ + TRACE(("read_from_cache: ref = %p, offset = %Ld, size = %lu, buffer = %p, bufferSize = %lu\n", + ref, offset, size, (void *)buffer, bufferSize)); + + // do we have to read in anything at all? + if (size == 0) + return FSSH_B_OK; + + // make sure "offset" is page aligned - but also remember the page offset + int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1); + size = PAGE_ALIGN(size + pageOffset); + offset -= pageOffset; + + while (true) { + fssh_size_t chunkSize = size; + if (chunkSize > (MAX_IO_VECS * FSSH_B_PAGE_SIZE)) + chunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE; + + fssh_status_t status = read_chunk_into_cache(ref, offset, chunkSize, pageOffset, + buffer, bufferSize); + if (status != FSSH_B_OK) + return status; + + if ((size -= chunkSize) == 0) + return FSSH_B_OK; + + if (chunkSize >= bufferSize) { + bufferSize = 0; + buffer = 0; + } else { + bufferSize -= chunkSize - pageOffset; + buffer += chunkSize - pageOffset; + } + + offset += chunkSize; + pageOffset = 0; + } + + return FSSH_B_OK; +} + + +/** Like read_chunk_into_cache() but writes data into the cache */ + +static inline fssh_status_t +write_chunk_to_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t numBytes, + int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize) +{ + fssh_iovec vecs[MAX_IO_VECS]; + int32_t vecCount = 0; + vm_page *pages[MAX_IO_VECS]; + int32_t pageIndex = 0; + fssh_status_t status = FSSH_B_OK; + + // ToDo: this should be settable somewhere + bool writeThrough = false; + + // allocate pages for the cache and mark them busy + for (fssh_size_t pos = 0; pos < numBytes; pos += FSSH_B_PAGE_SIZE) { + // ToDo: if space is becoming tight, and this cache is already grown + // big - shouldn't we better steal the pages directly in that case? + // (a working set like approach for the file cache) + vm_page *page = pages[pageIndex++] = vm_page_allocate_page(PAGE_STATE_FREE); + page->state = PAGE_STATE_BUSY; + + vm_cache_insert_page(ref, page, offset + pos); + + fssh_addr_t virtualAddress = page->Address(); + + add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, FSSH_B_PAGE_SIZE); + // ToDo: check if the array is large enough! + } + + mutex_unlock(&ref->lock); + + // copy contents (and read in partially written pages first) + + if (pageOffset != 0) { + // This is only a partial write, so we have to read the rest of the page + // from the file to have consistent data in the cache + fssh_iovec readVec = { vecs[0].iov_base, FSSH_B_PAGE_SIZE }; + fssh_size_t bytesRead = FSSH_B_PAGE_SIZE; + + status = pages_io(ref, offset, &readVec, 1, &bytesRead, false); + // ToDo: handle errors for real! + if (status < FSSH_B_OK) + fssh_panic("1. pages_io() failed: %s!\n", fssh_strerror(status)); + } + + fssh_addr_t lastPageOffset = (pageOffset + bufferSize) & (FSSH_B_PAGE_SIZE - 1); + if (lastPageOffset != 0) { + // get the last page in the I/O vectors + fssh_addr_t last = (fssh_addr_t)vecs[vecCount - 1].iov_base + + vecs[vecCount - 1].iov_len - FSSH_B_PAGE_SIZE; + + if (offset + pageOffset + bufferSize == ref->virtual_size) { + // the space in the page after this write action needs to be cleaned + fssh_memset((void *)(last + lastPageOffset), 0, FSSH_B_PAGE_SIZE - lastPageOffset); + } else if (vecCount > 1) { + // the end of this write does not happen on a page boundary, so we + // need to fetch the last page before we can update it + fssh_iovec readVec = { (void *)last, FSSH_B_PAGE_SIZE }; + fssh_size_t bytesRead = FSSH_B_PAGE_SIZE; + + status = pages_io(ref, offset + numBytes - FSSH_B_PAGE_SIZE, &readVec, 1, + &bytesRead, false); + // ToDo: handle errors for real! + if (status < FSSH_B_OK) + fssh_panic("pages_io() failed: %s!\n", fssh_strerror(status)); + } + } + + for (int32_t i = 0; i < vecCount; i++) { + fssh_addr_t base = (fssh_addr_t)vecs[i].iov_base; + fssh_size_t bytes = fssh_min_c(bufferSize, fssh_size_t(vecs[i].iov_len - pageOffset)); + + // copy data from user buffer + user_memcpy((void *)(base + pageOffset), (void *)buffer, bytes); + + bufferSize -= bytes; + if (bufferSize == 0) + break; + + buffer += bytes; + pageOffset = 0; + } + + if (writeThrough) { + // write cached pages back to the file if we were asked to do that + fssh_status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes, true); + if (status < FSSH_B_OK) { + // ToDo: remove allocated pages, ...? + fssh_panic("file_cache: remove allocated pages! write pages failed: %s\n", + fssh_strerror(status)); + } + } + + mutex_lock(&ref->lock); + + // unmap the pages again + + // make the pages accessible in the cache + for (int32_t i = pageIndex; i-- > 0;) { + if (writeThrough) + vm_page_set_state(pages[i], PAGE_STATE_ACTIVE); + else + vm_page_set_state(pages[i], PAGE_STATE_MODIFIED); + vm_page_put_page(pages[i]); + } + + return status; +} + + +/** Like read_into_cache() but writes data into the cache. To preserve data consistency, + * it might also read pages into the cache, though, if only a partial page gets written. + * The cache_ref lock must be hold when calling this function. + */ + +static fssh_status_t +write_to_cache(file_cache_ref *ref, fssh_off_t offset, fssh_size_t size, fssh_addr_t buffer, fssh_size_t bufferSize) +{ + TRACE(("write_to_cache: ref = %p, offset = %Ld, size = %lu, buffer = %p, bufferSize = %lu\n", + ref, offset, size, (void *)buffer, bufferSize)); + + // make sure "offset" is page aligned - but also remember the page offset + int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1); + size = PAGE_ALIGN(size + pageOffset); + offset -= pageOffset; + + while (true) { + fssh_size_t chunkSize = size; + if (chunkSize > (MAX_IO_VECS * FSSH_B_PAGE_SIZE)) + chunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE; + + fssh_status_t status = write_chunk_to_cache(ref, offset, chunkSize, pageOffset, buffer, bufferSize); + if (status != FSSH_B_OK) + return status; + + if ((size -= chunkSize) == 0) + return FSSH_B_OK; + + if (chunkSize >= bufferSize) { + bufferSize = 0; + buffer = 0; + } else { + bufferSize -= chunkSize - pageOffset; + buffer += chunkSize - pageOffset; + } + + offset += chunkSize; + pageOffset = 0; + } + + return FSSH_B_OK; +} + + +static fssh_status_t +satisfy_cache_io(file_cache_ref *ref, fssh_off_t offset, fssh_addr_t buffer, fssh_addr_t lastBuffer, + bool doWrite) +{ + fssh_size_t requestSize = buffer - lastBuffer; + + if (doWrite) + return write_to_cache(ref, offset, requestSize, lastBuffer, requestSize); + + return read_into_cache(ref, offset, requestSize, lastBuffer, requestSize); +} + + +static fssh_status_t +cache_io(void *_cacheRef, fssh_off_t offset, fssh_addr_t buffer, fssh_size_t *_size, bool doWrite) +{ + if (_cacheRef == NULL) + fssh_panic("cache_io() called with NULL ref!\n"); + + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + fssh_off_t fileSize = ref->virtual_size; + + TRACE(("cache_io(ref = %p, offset = %Ld, buffer = %p, size = %lu, %s)\n", + ref, offset, (void *)buffer, *_size, doWrite ? "write" : "read")); + + // out of bounds access? + if (offset >= fileSize || offset < 0) { + *_size = 0; + return FSSH_B_OK; + } + + int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1); + fssh_size_t size = *_size; + offset -= pageOffset; + + if (offset + pageOffset + size > fileSize) { + // adapt size to be within the file's offsets + size = fileSize - pageOffset - offset; + *_size = size; + } + + // "offset" and "lastOffset" are always aligned to FSSH_B_PAGE_SIZE, + // the "last*" variables always point to the end of the last + // satisfied request part + + fssh_size_t bytesLeft = size, lastLeft = size; + int32_t lastPageOffset = pageOffset; + fssh_addr_t lastBuffer = buffer; + fssh_off_t lastOffset = offset; + + mutex_lock(&ref->lock); + + for (; bytesLeft > 0; offset += FSSH_B_PAGE_SIZE) { + // check if this page is already in memory + restart: + vm_page *page = vm_cache_lookup_page(ref, offset); + PagePutter pagePutter(page); + + if (page != NULL) { + // The page is busy - since we need to unlock the cache sometime + // in the near future, we need to satisfy the request of the pages + // we didn't get yet (to make sure no one else interferes in the + // mean time). + fssh_status_t status = FSSH_B_OK; + + if (lastBuffer != buffer) { + status = satisfy_cache_io(ref, lastOffset + lastPageOffset, + buffer, lastBuffer, doWrite); + if (status == FSSH_B_OK) { + lastBuffer = buffer; + lastLeft = bytesLeft; + lastOffset = offset; + lastPageOffset = 0; + pageOffset = 0; + } + } + + if (status != FSSH_B_OK) { + mutex_unlock(&ref->lock); + return status; + } + + if (page->state == PAGE_STATE_BUSY) { + mutex_unlock(&ref->lock); + // ToDo: don't wait forever! + fssh_snooze(20000); + mutex_lock(&ref->lock); + goto restart; + } + } + + fssh_size_t bytesInPage = fssh_min_c(fssh_size_t(FSSH_B_PAGE_SIZE - pageOffset), bytesLeft); + fssh_addr_t virtualAddress; + + TRACE(("lookup page from offset %Ld: %p, size = %lu, pageOffset = %lu\n", offset, page, bytesLeft, pageOffset)); + if (page != NULL) { + virtualAddress = page->Address(); + + // and copy the contents of the page already in memory + if (doWrite) { + user_memcpy((void *)(virtualAddress + pageOffset), (void *)buffer, bytesInPage); + + // make sure the page is in the modified list + if (page->state != PAGE_STATE_MODIFIED) + vm_page_set_state(page, PAGE_STATE_MODIFIED); + } else + user_memcpy((void *)buffer, (void *)(virtualAddress + pageOffset), bytesInPage); + + if (bytesLeft <= bytesInPage) { + // we've read the last page, so we're done! + mutex_unlock(&ref->lock); + return FSSH_B_OK; + } + + // prepare a potential gap request + lastBuffer = buffer + bytesInPage; + lastLeft = bytesLeft - bytesInPage; + lastOffset = offset + FSSH_B_PAGE_SIZE; + lastPageOffset = 0; + } + + if (bytesLeft <= bytesInPage) + break; + + buffer += bytesInPage; + bytesLeft -= bytesInPage; + pageOffset = 0; + } + + // fill the last remaining bytes of the request (either write or read) + + fssh_status_t status; + if (doWrite) + status = write_to_cache(ref, lastOffset + lastPageOffset, lastLeft, lastBuffer, lastLeft); + else + status = read_into_cache(ref, lastOffset + lastPageOffset, lastLeft, lastBuffer, lastLeft); + + mutex_unlock(&ref->lock); + return status; +} + + +} // namespace FSShell + + +// #pragma mark - public FS API + + +using namespace FSShell; + + +void * +fssh_file_cache_create(fssh_mount_id mountID, fssh_vnode_id vnodeID, + fssh_off_t size, int fd) +{ + TRACE(("file_cache_create(mountID = %ld, vnodeID = %Ld, size = %Ld, fd = %d)\n", mountID, vnodeID, size, fd)); + + file_cache_ref *ref = new(nothrow) file_cache_ref; + if (ref == NULL) + return NULL; + + ref->mountID = mountID; + ref->nodeID = vnodeID; + ref->deviceFD = fd; + ref->virtual_size = size; + + // get vnode + fssh_status_t error = vfs_get_vnode(mountID, vnodeID, &ref->node); + if (error != FSSH_B_OK) { + fssh_dprintf("file_cache_create(): Failed get vnode %d:%lld: %s\n", + mountID, vnodeID, fssh_strerror(error)); + delete ref; + return NULL; + } + + // create lock + char buffer[32]; + fssh_snprintf(buffer, sizeof(buffer), "file cache %d:%lld", (int)mountID, vnodeID); + error = mutex_init(&ref->lock, buffer); + if (error != FSSH_B_OK) { + fssh_dprintf("file_cache_create(): Failed to init mutex: %s\n", + fssh_strerror(error)); + vfs_put_vnode(ref->node); + delete ref; + return NULL; + } + + return ref; +} + + +void +fssh_file_cache_delete(void *_cacheRef) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + if (ref == NULL) + return; + + TRACE(("file_cache_delete(ref = %p)\n", ref)); + + // write all modified pages and resize the cache to 0 to free all pages + // it contains + vm_cache_write_modified(ref, false); + + mutex_lock(&ref->lock); + vm_cache_resize(ref, 0); + + mutex_destroy(&ref->lock); + + vfs_put_vnode(ref->node); + + delete ref; +} + + +fssh_status_t +fssh_file_cache_set_size(void *_cacheRef, fssh_off_t size) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + TRACE(("file_cache_set_size(ref = %p, size = %Ld)\n", ref, size)); + + if (ref == NULL) + return FSSH_B_OK; + + fssh_file_cache_invalidate_file_map(_cacheRef, 0, size); + // ToDo: make this better (we would only need to extend or shrink the map) + + mutex_lock(&ref->lock); + fssh_status_t status = vm_cache_resize(ref, size); + mutex_unlock(&ref->lock); + + return status; +} + + +fssh_status_t +fssh_file_cache_sync(void *_cacheRef) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + if (ref == NULL) + return FSSH_B_BAD_VALUE; + + return vm_cache_write_modified(ref, true); +} + + +fssh_status_t +fssh_file_cache_read_pages(void *_cacheRef, fssh_off_t offset, const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + return pages_io(ref, offset, vecs, count, _numBytes, false); +} + + +fssh_status_t +fssh_file_cache_write_pages(void *_cacheRef, fssh_off_t offset, const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + fssh_status_t status = pages_io(ref, offset, vecs, count, _numBytes, true); + TRACE(("file_cache_write_pages(ref = %p, offset = %Ld, vecs = %p, count = %lu, bytes = %lu) = %ld\n", + ref, offset, vecs, count, *_numBytes, status)); + + return status; +} + + +fssh_status_t +fssh_file_cache_read(void *_cacheRef, fssh_off_t offset, void *bufferBase, fssh_size_t *_size) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %lu)\n", + ref, offset, bufferBase, *_size)); + + return cache_io(ref, offset, (fssh_addr_t)bufferBase, _size, false); +} + + +fssh_status_t +fssh_file_cache_write(void *_cacheRef, fssh_off_t offset, const void *buffer, fssh_size_t *_size) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + fssh_status_t status = cache_io(ref, offset, (fssh_addr_t)const_cast(buffer), _size, true); + TRACE(("file_cache_write(ref = %p, offset = %Ld, buffer = %p, size = %lu) = %ld\n", + ref, offset, buffer, *_size, status)); + + return status; +} + + +fssh_status_t +fssh_file_cache_invalidate_file_map(void *_cacheRef, fssh_off_t offset, fssh_off_t size) +{ + file_cache_ref *ref = (file_cache_ref *)_cacheRef; + + // ToDo: honour offset/size parameters + + TRACE(("file_cache_invalidate_file_map(offset = %Ld, size = %Ld)\n", offset, size)); + mutex_lock(&ref->lock); + ref->map.Free(); + mutex_unlock(&ref->lock); + return FSSH_B_OK; +} diff --git a/src/tools/fs_shell/fssh.cpp b/src/tools/fs_shell/fssh.cpp new file mode 100644 index 0000000000..485c8b7670 --- /dev/null +++ b/src/tools/fs_shell/fssh.cpp @@ -0,0 +1,11 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + + +int +main() +{ + return 0; +} diff --git a/src/tools/fs_shell/hash.cpp b/src/tools/fs_shell/hash.cpp new file mode 100644 index 0000000000..818cb9868f --- /dev/null +++ b/src/tools/fs_shell/hash.cpp @@ -0,0 +1,310 @@ +/* Generic hash table +** +** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ + +#include "hash.h" + +#include + +#include "fssh_errors.h" +#include "fssh_kernel_export.h" + + +#undef TRACE +#define TRACE_HASH 0 +#if TRACE_HASH +# define TRACE(x) fssh_dprintf x +#else +# define TRACE(x) ; +#endif + +#undef ASSERT +#define ASSERT(x) + + +namespace FSShell { + + +// TODO: the hashtable is not expanded when necessary (no load factor, nothing) +// resizing should be optional, though, in case the hash is used at times +// that forbid resizing. + +struct hash_table { + struct hash_element **table; + int next_ptr_offset; + uint32_t table_size; + int num_elements; + int flags; + int (*compare_func)(void *e, const void *key); + uint32_t (*hash_func)(void *e, const void *key, uint32_t range); +}; + +// XXX gross hack +#define NEXT_ADDR(t, e) ((void *)(((unsigned long)(e)) + (t)->next_ptr_offset)) +#define NEXT(t, e) ((void *)(*(unsigned long *)NEXT_ADDR(t, e))) +#define PUT_IN_NEXT(t, e, val) (*(unsigned long *)NEXT_ADDR(t, e) = (long)(val)) + + +static inline void * +next_element(hash_table *table, void *element) +{ + // ToDo: should we use this instead of the NEXT() macro? + return (void *)(*(unsigned long *)NEXT_ADDR(table, element)); +} + + +struct hash_table * +hash_init(uint32_t table_size, int next_ptr_offset, + int compare_func(void *e, const void *key), + uint32_t hash_func(void *e, const void *key, uint32_t range)) +{ + struct hash_table *t; + unsigned int i; + + if (compare_func == NULL || hash_func == NULL) { + fssh_dprintf("hash_init() called with NULL function pointer\n"); + return NULL; + } + + t = (struct hash_table *)malloc(sizeof(struct hash_table)); + if (t == NULL) + return NULL; + + t->table = (struct hash_element **)malloc(sizeof(void *) * table_size); + if (t->table == NULL) { + free(t); + return NULL; + } + + for (i = 0; i < table_size; i++) + t->table[i] = NULL; + + t->table_size = table_size; + t->next_ptr_offset = next_ptr_offset; + t->flags = 0; + t->num_elements = 0; + t->compare_func = compare_func; + t->hash_func = hash_func; + + TRACE(("hash_init: created table %p, next_ptr_offset %d, compare_func %p, hash_func %p\n", + t, next_ptr_offset, compare_func, hash_func)); + + return t; +} + + +int +hash_uninit(struct hash_table *table) +{ + ASSERT(table->num_elements == 0); + + free(table->table); + free(table); + + return 0; +} + + +fssh_status_t +hash_insert(struct hash_table *table, void *element) +{ + uint32_t hash; + + ASSERT(table != NULL && element != NULL); + TRACE(("hash_insert: table 0x%x, element 0x%x\n", table, element)); + + hash = table->hash_func(element, NULL, table->table_size); + PUT_IN_NEXT(table, element, table->table[hash]); + table->table[hash] = (struct hash_element *)element; + table->num_elements++; + + // ToDo: resize hash table if it's grown too much! + + return FSSH_B_OK; +} + + +fssh_status_t +hash_remove(struct hash_table *table, void *_element) +{ + uint32_t hash = table->hash_func(_element, NULL, table->table_size); + void *element, *lastElement = NULL; + + for (element = table->table[hash]; element != NULL; + lastElement = element, element = NEXT(table, element)) { + if (element == _element) { + if (lastElement != NULL) { + // connect the previous entry with the next one + PUT_IN_NEXT(table, lastElement, NEXT(table, element)); + } else + table->table[hash] = (struct hash_element *)NEXT(table, element); + table->num_elements--; + + return FSSH_B_OK; + } + } + + return FSSH_B_ERROR; +} + + +void +hash_remove_current(struct hash_table *table, struct hash_iterator *iterator) +{ + uint32_t index = iterator->bucket; + void *element; + + if (iterator->current == NULL) + fssh_panic("hash_remove_current() called too early."); + + for (element = table->table[index]; index < table->table_size; index++) { + void *lastElement = NULL; + + while (element != NULL) { + if (element == iterator->current) { + iterator->current = lastElement; + + if (lastElement != NULL) { + // connect the previous entry with the next one + PUT_IN_NEXT(table, lastElement, NEXT(table, element)); + } else { + table->table[index] = (struct hash_element *)NEXT(table, + element); + } + + table->num_elements--; + return; + } + + element = NEXT(table, element); + } + } +} + + +void * +hash_remove_first(struct hash_table *table, uint32_t *_cookie) +{ + uint32_t index; + + for (index = _cookie ? *_cookie : 0; index < table->table_size; index++) { + void *element = table->table[index]; + if (element != NULL) { + // remove the first element we find + table->table[index] = (struct hash_element *)NEXT(table, element); + table->num_elements--; + if (_cookie) + *_cookie = index; + return element; + } + } + + return NULL; +} + + +void * +hash_find(struct hash_table *table, void *searchedElement) +{ + uint32_t hash = table->hash_func(searchedElement, NULL, table->table_size); + void *element; + + for (element = table->table[hash]; element != NULL; element = NEXT(table, element)) { + if (element == searchedElement) + return element; + } + + return NULL; +} + + +void * +hash_lookup(struct hash_table *table, const void *key) +{ + uint32_t hash = table->hash_func(NULL, key, table->table_size); + void *element; + + for (element = table->table[hash]; element != NULL; element = NEXT(table, element)) { + if (table->compare_func(element, key) == 0) + return element; + } + + return NULL; +} + + +struct hash_iterator * +hash_open(struct hash_table *table, struct hash_iterator *iterator) +{ + if (iterator == NULL) { + iterator = (struct hash_iterator *)malloc(sizeof(struct hash_iterator)); + if (iterator == NULL) + return NULL; + } + + hash_rewind(table, iterator); + + return iterator; +} + + +void +hash_close(struct hash_table *table, struct hash_iterator *iterator, bool freeIterator) +{ + if (freeIterator) + free(iterator); +} + + +void +hash_rewind(struct hash_table *table, struct hash_iterator *iterator) +{ + iterator->current = NULL; + iterator->bucket = -1; +} + + +void * +hash_next(struct hash_table *table, struct hash_iterator *iterator) +{ + uint32_t index; + +restart: + if (iterator->current == NULL) { + // get next bucket + for (index = (uint32_t)(iterator->bucket + 1); index < table->table_size; index++) { + if (table->table[index]) { + iterator->bucket = index; + iterator->current = table->table[index]; + break; + } + } + } else { + iterator->current = NEXT(table, iterator->current); + if (!iterator->current) + goto restart; + } + + return iterator->current; +} + + +uint32_t +hash_hash_string(const char *string) +{ + uint32_t hash = 0; + char c; + + // we assume hash to be at least 32 bits + while ((c = *string++) != 0) { + hash ^= hash >> 28; + hash <<= 4; + hash ^= c; + } + + return hash; +} + +} // namespace FSShell diff --git a/src/tools/fs_shell/hash.h b/src/tools/fs_shell/hash.h new file mode 100644 index 0000000000..76c241f2d6 --- /dev/null +++ b/src/tools/fs_shell/hash.h @@ -0,0 +1,51 @@ +/* +** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +#ifndef _FSSH_HASH_H +#define _FSSH_HASH_H + +#include "fssh_types.h" + + +namespace FSShell { + +// can be allocated on the stack +typedef struct hash_iterator { + void *current; + int bucket; +} hash_iterator; + +typedef struct hash_table hash_table; + +struct hash_table *hash_init(uint32_t table_size, int next_ptr_offset, + int compare_func(void *element, const void *key), + uint32_t hash_func(void *element, const void *key, uint32_t range)); +int hash_uninit(struct hash_table *table); +fssh_status_t hash_insert(struct hash_table *table, void *_element); +fssh_status_t hash_remove(struct hash_table *table, void *_element); +void hash_remove_current(struct hash_table *table, struct hash_iterator *iterator); +void *hash_remove_first(struct hash_table *table, uint32_t *_cookie); +void *hash_find(struct hash_table *table, void *e); +void *hash_lookup(struct hash_table *table, const void *key); +struct hash_iterator *hash_open(struct hash_table *table, struct hash_iterator *i); +void hash_close(struct hash_table *table, struct hash_iterator *i, bool free_iterator); +void *hash_next(struct hash_table *table, struct hash_iterator *i); +void hash_rewind(struct hash_table *table, struct hash_iterator *i); + +/* function pointers must look like this: + * + * uint32 hash_func(void *e, const void *key, uint32 range); + * hash function should calculate hash on either e or key, + * depending on which one is not NULL - they also need + * to make sure the returned value is within range. + * int compare_func(void *e, const void *key); + * compare function should compare the element with + * the key, returning 0 if equal, other if not + */ + +uint32_t hash_hash_string(const char *str); + +} // namespace FSShell + +#endif /* _FSSH_HASH_H */ diff --git a/src/tools/fs_shell/kernel_export.cpp b/src/tools/fs_shell/kernel_export.cpp new file mode 100644 index 0000000000..ee7be69413 --- /dev/null +++ b/src/tools/fs_shell/kernel_export.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "fssh_kernel_export.h" + +#include +#include +#include + +#include "fssh_errors.h" + + +fssh_thread_id +fssh_spawn_kernel_thread(fssh_thread_func function, const char *threadName, + int32_t priority, void *arg) +{ + return FSSH_B_ERROR; +} + + +void +fssh_dprintf(const char *format, ...) +{ + va_list args; + va_start(args, format); + + vprintf(format, args); + + va_end(args); +} + + +void +fssh_kprintf(const char *format, ...) +{ + va_list args; + va_start(args, format); + + vprintf(format, args); + + va_end(args); +} + + +void +fssh_dump_block(const char *buffer, int size, const char *prefix) +{ +} + + +void +fssh_panic(const char *format, ...) +{ + va_list args; + va_start(args, format); + + vfprintf(stderr, format, args); + exit(1); + + va_end(args); +} + +void +fssh_kernel_debugger(const char *message) +{ + fssh_panic("%s", message); +} + + +uint32_t +fssh_parse_expression(const char *string) +{ + return 0; +} + +int +fssh_add_debugger_command(char *name, fssh_debugger_command_hook hook, + char *help) +{ + return 0; +} + + +int +fssh_remove_debugger_command(char *name, fssh_debugger_command_hook hook) +{ + return 0; +} + diff --git a/src/tools/fs_shell/list.cpp b/src/tools/fs_shell/list.cpp new file mode 100644 index 0000000000..6e70e5d238 --- /dev/null +++ b/src/tools/fs_shell/list.cpp @@ -0,0 +1,253 @@ +/* + * Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "list.h" + + +#define GET_ITEM(list, item) ((void *)((uint8_t *)item - list->offset)) +#define GET_LINK(list, item) ((list_link *)((uint8_t *)item + list->offset)) + + +namespace FSShell { + + +/** Initializes the list with a specified offset to the link + * structure in the items that will be part of the list. + */ + +void +list_init_etc(struct list *list, int32_t offset) +{ + list->link.next = list->link.prev = &list->link; + list->offset = offset; +} + + +void +list_init(struct list *list) +{ + list_init_etc(list, 0); +} + + +/** Adds a link to the head of the list + */ + +void +list_add_link_to_head(struct list *list, void *_link) +{ + list_link *link = (list_link *)_link; + + link->next = list->link.next; + link->prev = &list->link; + + list->link.next->prev = link; + list->link.next = link; +} + + +/** Adds a link to the tail of the list + */ + +void +list_add_link_to_tail(struct list *list, void *_link) +{ + list_link *link = (list_link *)_link; + + link->next = &list->link; + link->prev = list->link.prev; + + list->link.prev->next = link; + list->link.prev = link; +} + + +/** Removes a link from the list it's currently in. + * Note: the link has to be in a list when you call this function. + */ + +void +list_remove_link(void *_link) +{ + list_link *link = (list_link *)_link; + + link->next->prev = link->prev; + link->prev->next = link->next; +} + + +static inline list_link * +get_next_link(struct list *list, list_link *link) +{ + if (link->next == &list->link) + return NULL; + + return link->next; +} + + +static inline list_link * +get_prev_link(struct list *list, list_link *link) +{ + if (link->prev == &list->link) + return NULL; + + return link->prev; +} + + +/** Gets the successor for the current item. If the passed + * item is NULL, it returns the first entry in the list, + * if there is one. + * Returns NULL if there aren't any more items in this list. + */ + +void * +list_get_next_item(struct list *list, void *item) +{ + list_link *link; + + if (item == NULL) + return list_is_empty(list) ? NULL : GET_ITEM(list, list->link.next); + + link = get_next_link(list, GET_LINK(list, item)); + return link != NULL ? GET_ITEM(list, link) : NULL; +} + + +/** Gets the predecessor for the current item. If the passed + * item is NULL, it returns the last entry in the list, + * if there is one. + * Returns NULL if there aren't any previous items in this list. + */ + +void * +list_get_prev_item(struct list *list, void *item) +{ + list_link *link; + + if (item == NULL) + return list_is_empty(list) ? NULL : GET_ITEM(list, list->link.prev); + + link = get_prev_link(list, GET_LINK(list, item)); + return link != NULL ? GET_ITEM(list, link) : NULL; +} + + +void * +list_get_last_item(struct list *list) +{ + return list_is_empty(list) ? NULL : GET_ITEM(list, list->link.prev); +} + + +/** Adds an item to the end of the list. + * Similar to list_add_link_to_tail() but works on the item, not the link. + */ + +void +list_add_item(struct list *list, void *item) +{ + list_add_link_to_tail(list, GET_LINK(list, item)); +} + + +/** Removes an item from the list. + * Similar to list_remove_link() but works on the item, not the link. + */ + +void +list_remove_item(struct list *list, void *item) +{ + list_remove_link(GET_LINK(list, item)); +} + + +/** Inserts an item before another item in the list. + * If you pass NULL as \a before item, the item is added at the end of + * the list. + */ + +void +list_insert_item_before(struct list *list, void *before, void *item) +{ + list_link *beforeLink; + list_link *link; + + if (before == NULL) { + list_add_item(list, item); + return; + } + + beforeLink = GET_LINK(list, before); + link = GET_LINK(list, item); + + link->prev = beforeLink->prev; + link->next = beforeLink; + + beforeLink->prev->next = link; + beforeLink->prev = link; +} + + +/** Removes the first item in the list and returns it. + * Returns NULL if the list is empty. + */ + +void * +list_remove_head_item(struct list *list) +{ + list_link *link; + + if (list_is_empty(list)) + return NULL; + + list_remove_link(link = list->link.next); + return GET_ITEM(list, link); +} + + +/** Removes the last item in the list and returns it. + * Returns NULL if the list is empty. + */ + +void * +list_remove_tail_item(struct list *list) +{ + list_link *link; + + if (list_is_empty(list)) + return NULL; + + list_remove_link(link = list->link.prev); + return GET_ITEM(list, link); +} + + +/** Moves the contents of the source list to the target list. + * The target list will be emptied before the items are moved; + * this is a very fast operation. + */ + +void +list_move_to_list(struct list *sourceList, struct list *targetList) +{ + if (list_is_empty(sourceList)) { + targetList->link.next = targetList->link.prev = &targetList->link; + return; + } + + *targetList = *sourceList; + + // correct link pointers to this list + targetList->link.next->prev = &targetList->link; + targetList->link.prev->next = &targetList->link; + + // empty source list + sourceList->link.next = sourceList->link.prev = &sourceList->link; +} + +} // namespace FSShell diff --git a/src/tools/fs_shell/list.h b/src/tools/fs_shell/list.h new file mode 100644 index 0000000000..c2a4b01304 --- /dev/null +++ b/src/tools/fs_shell/list.h @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_LIST_H +#define _FSSH_LIST_H + + +#include "fssh_types.h" + + +/* This header defines a doubly-linked list. It differentiates between a link + * and an item. + * A link is what is put into and removed from a list, an item is the whole + * object that contains the link. The item doesn't have to be begin with a + * link; the offset to the link structure is given to init_list_etc(), so that + * list_get_next/prev_item() will work correctly. + * Note, the offset value is only needed for the *_item() functions. If the + * offset is 0, list_link and item are identical - if you use init_list(), + * you don't have to care about the difference between a link and an item. + */ + + +namespace FSShell { + + +typedef struct list_link list_link; + +/* The object that is put into the list must begin with these + * fields, but it doesn't have to be this structure. + */ + +struct list_link { + list_link *next; + list_link *prev; +}; + +struct list { + list_link link; + int32_t offset; +}; + + +extern void list_init(struct list *list); +extern void list_init_etc(struct list *list, int32_t offset); +extern void list_add_link_to_head(struct list *list, void *_link); +extern void list_add_link_to_tail(struct list *list, void *_link); +extern void list_remove_link(void *_link); +extern void *list_get_next_item(struct list *list, void *item); +extern void *list_get_prev_item(struct list *list, void *item); +extern void *list_get_last_item(struct list *list); +extern void list_add_item(struct list *list, void *item); +extern void list_remove_item(struct list *list, void *item); +extern void list_insert_item_before(struct list *list, void *before, void *item); +extern void *list_remove_head_item(struct list *list); +extern void *list_remove_tail_item(struct list *list); +extern void list_move_to_list(struct list *sourceList, struct list *targetList); + +static inline bool +list_is_empty(struct list *list) +{ + return list->link.next == (list_link *)list; +} + +static inline void * +list_get_first_item(struct list *list) +{ + return list_get_next_item(list, NULL); +} + + +} // namespace FSShell + +#endif /* _FSSH_LIST_H */ diff --git a/src/tools/fs_shell/lock.cpp b/src/tools/fs_shell/lock.cpp new file mode 100644 index 0000000000..844dc0e9ca --- /dev/null +++ b/src/tools/fs_shell/lock.cpp @@ -0,0 +1,243 @@ +/* + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + +/* Mutex and recursive_lock code */ + +#include "lock.h" + +#include "fssh_kernel_export.h" + + +namespace FSShell { + + +int32_t +recursive_lock_get_recursion(recursive_lock *lock) +{ + if (lock->holder == fssh_find_thread(NULL)) + return lock->recursion; + + return -1; +} + + +fssh_status_t +recursive_lock_init(recursive_lock *lock, const char *name) +{ + if (lock == NULL) + return FSSH_B_BAD_VALUE; + + if (name == NULL) + name = "recursive lock"; + + lock->holder = -1; + lock->recursion = 0; + lock->sem = fssh_create_sem(1, name); + + if (lock->sem >= FSSH_B_OK) + return FSSH_B_OK; + + return lock->sem; +} + + +void +recursive_lock_destroy(recursive_lock *lock) +{ + if (lock == NULL) + return; + + fssh_delete_sem(lock->sem); + lock->sem = -1; +} + + +fssh_status_t +recursive_lock_lock(recursive_lock *lock) +{ + fssh_thread_id thread = fssh_find_thread(NULL); + + if (thread != lock->holder) { + fssh_status_t status = fssh_acquire_sem(lock->sem); + if (status < FSSH_B_OK) + return status; + + lock->holder = thread; + } + lock->recursion++; + return FSSH_B_OK; +} + + +void +recursive_lock_unlock(recursive_lock *lock) +{ + if (fssh_find_thread(NULL) != lock->holder) + fssh_panic("recursive_lock %p unlocked by non-holder thread!\n", lock); + + if (--lock->recursion == 0) { + lock->holder = -1; + fssh_release_sem(lock->sem); + } +} + + +// #pragma mark - + + +fssh_status_t +mutex_init(mutex *m, const char *name) +{ + if (m == NULL) + return FSSH_EINVAL; + + if (name == NULL) + name = "mutex_sem"; + + m->holder = -1; + + m->sem = fssh_create_sem(1, name); + if (m->sem >= FSSH_B_OK) + return FSSH_B_OK; + + return m->sem; +} + + +void +mutex_destroy(mutex *mutex) +{ + if (mutex == NULL) + return; + + if (mutex->sem >= 0) { + fssh_delete_sem(mutex->sem); + mutex->sem = -1; + } + mutex->holder = -1; +} + + +fssh_status_t +mutex_lock(mutex *mutex) +{ + fssh_thread_id me = fssh_find_thread(NULL); + fssh_status_t status; + + status = fssh_acquire_sem(mutex->sem); + if (status < FSSH_B_OK) + return status; + + if (me == mutex->holder) + fssh_panic("mutex_lock failure: mutex %p (sem = 0x%x) acquired twice by thread 0x%x\n", mutex, (int)mutex->sem, (int)me); + + mutex->holder = me; + return FSSH_B_OK; +} + + +void +mutex_unlock(mutex *mutex) +{ + fssh_thread_id me = fssh_find_thread(NULL); + + if (me != mutex->holder) { + fssh_panic("mutex_unlock failure: thread 0x%x is trying to release mutex %p (current holder 0x%x)\n", + (int)me, mutex, (int)mutex->holder); + } + + mutex->holder = -1; + fssh_release_sem(mutex->sem); +} + + +// #pragma mark - + + +fssh_status_t +benaphore_init(benaphore *ben, const char *name) +{ + if (ben == NULL || name == NULL) + return FSSH_B_BAD_VALUE; + + ben->count = 1; + ben->sem = fssh_create_sem(0, name); + if (ben->sem >= FSSH_B_OK) + return FSSH_B_OK; + + return ben->sem; +} + + +void +benaphore_destroy(benaphore *ben) +{ + fssh_delete_sem(ben->sem); + ben->sem = -1; +} + + +// #pragma mark - + + +fssh_status_t +rw_lock_init(rw_lock *lock, const char *name) +{ + if (lock == NULL) + return FSSH_B_BAD_VALUE; + + if (name == NULL) + name = "r/w lock"; + + lock->sem = fssh_create_sem(FSSH_RW_MAX_READERS, name); + if (lock->sem >= FSSH_B_OK) + return FSSH_B_OK; + + return lock->sem; +} + + +void +rw_lock_destroy(rw_lock *lock) +{ + if (lock == NULL) + return; + + fssh_delete_sem(lock->sem); +} + + +fssh_status_t +rw_lock_read_lock(rw_lock *lock) +{ + return fssh_acquire_sem(lock->sem); +} + + +fssh_status_t +rw_lock_read_unlock(rw_lock *lock) +{ + return fssh_release_sem(lock->sem); +} + + +fssh_status_t +rw_lock_write_lock(rw_lock *lock) +{ + return fssh_acquire_sem_etc(lock->sem, FSSH_RW_MAX_READERS, 0, 0); +} + + +fssh_status_t +rw_lock_write_unlock(rw_lock *lock) +{ + return fssh_release_sem_etc(lock->sem, FSSH_RW_MAX_READERS, 0); +} + + +} // namespace FSShell diff --git a/src/tools/fs_shell/lock.h b/src/tools/fs_shell/lock.h new file mode 100644 index 0000000000..e8ce4e2f4f --- /dev/null +++ b/src/tools/fs_shell/lock.h @@ -0,0 +1,160 @@ +/* + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ +#ifndef _FSSH_LOCK_H +#define _FSSH_LOCK_H + +#include "fssh_atomic.h" +#include "fssh_auto_locker.h" +#include "fssh_errors.h" +#include "fssh_os.h" + + +namespace FSShell { + + +typedef struct recursive_lock { + fssh_sem_id sem; + fssh_thread_id holder; + int recursion; +} recursive_lock; + +typedef struct mutex { + fssh_sem_id sem; + fssh_thread_id holder; +} mutex; + +typedef struct benaphore { + fssh_sem_id sem; + int32_t count; +} benaphore; + +// Note: this is currently a trivial r/w lock implementation +// it will be replaced with something better later - this +// or a similar API will be made publically available at this point. +typedef struct rw_lock { + fssh_sem_id sem; + int32_t count; + benaphore writeLock; +} rw_lock; + +#define FSSH_RW_MAX_READERS 1000000 + + +extern fssh_status_t recursive_lock_init(recursive_lock *lock, + const char *name); +extern void recursive_lock_destroy(recursive_lock *lock); +extern fssh_status_t recursive_lock_lock(recursive_lock *lock); +extern void recursive_lock_unlock(recursive_lock *lock); +extern int32_t recursive_lock_get_recursion(recursive_lock *lock); + +extern fssh_status_t mutex_init(mutex *m, const char *name); +extern void mutex_destroy(mutex *m); +extern fssh_status_t mutex_lock(mutex *m); +extern void mutex_unlock(mutex *m); + +extern fssh_status_t benaphore_init(benaphore *ben, + const char *name); +extern void benaphore_destroy(benaphore *ben); + +static inline fssh_status_t +benaphore_lock_etc(benaphore *ben, uint32_t flags, fssh_bigtime_t timeout) +{ + if (fssh_atomic_add(&ben->count, -1) <= 0) + return fssh_acquire_sem_etc(ben->sem, 1, flags, timeout); + + return FSSH_B_OK; +} + + +static inline fssh_status_t +benaphore_lock(benaphore *ben) +{ + if (fssh_atomic_add(&ben->count, -1) <= 0) + return fssh_acquire_sem(ben->sem); + + return FSSH_B_OK; +} + + +static inline fssh_status_t +benaphore_unlock(benaphore *ben) +{ + if (fssh_atomic_add(&ben->count, 1) < 0) + return fssh_release_sem(ben->sem); + + return FSSH_B_OK; +} + +extern fssh_status_t rw_lock_init(rw_lock *lock, const char *name); +extern void rw_lock_destroy(rw_lock *lock); +extern fssh_status_t rw_lock_read_lock(rw_lock *lock); +extern fssh_status_t rw_lock_read_unlock(rw_lock *lock); +extern fssh_status_t rw_lock_write_lock(rw_lock *lock); +extern fssh_status_t rw_lock_write_unlock(rw_lock *lock); + + +/* C++ Auto Locking */ + +// MutexLocking +class MutexLocking { +public: + inline bool Lock(mutex *lockable) + { + return mutex_lock(lockable) == FSSH_B_OK; + } + + inline void Unlock(mutex *lockable) + { + mutex_unlock(lockable); + } +}; + +// MutexLocker +typedef AutoLocker MutexLocker; + +// RecursiveLockLocking +class RecursiveLockLocking { +public: + inline bool Lock(recursive_lock *lockable) + { + return recursive_lock_lock(lockable) == FSSH_B_OK; + } + + inline void Unlock(recursive_lock *lockable) + { + recursive_lock_unlock(lockable); + } +}; + +// RecursiveLocker +typedef AutoLocker RecursiveLocker; + +// BenaphoreLocking +class BenaphoreLocking { +public: + inline bool Lock(benaphore *lockable) + { + return benaphore_lock(lockable) == FSSH_B_OK; + } + + inline void Unlock(benaphore *lockable) + { + benaphore_unlock(lockable); + } +}; + +// BenaphoreLocker +typedef AutoLocker BenaphoreLocker; + +} // namespace FSShell + +using FSShell::MutexLocker; +using FSShell::RecursiveLocker; +using FSShell::BenaphoreLocker; + +#endif /* _FSSH_LOCK_H */ diff --git a/src/tools/fs_shell/node_monitor.cpp b/src/tools/fs_shell/node_monitor.cpp new file mode 100644 index 0000000000..40a103b306 --- /dev/null +++ b/src/tools/fs_shell/node_monitor.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "fssh_errors.h" +#include "fssh_fs_interface.h" + + +fssh_status_t +fssh_notify_entry_created(fssh_mount_id device, fssh_vnode_id directory, + const char *name, fssh_vnode_id node) +{ + return FSSH_B_OK; +} + + +fssh_status_t +fssh_notify_entry_removed(fssh_mount_id device, fssh_vnode_id directory, + const char *name, fssh_vnode_id node) +{ + return FSSH_B_OK; +} + + +fssh_status_t +fssh_notify_entry_moved(fssh_mount_id device, fssh_vnode_id fromDirectory, + const char *fromName, fssh_vnode_id toDirectory, const char *toName, + fssh_vnode_id node) +{ + return FSSH_B_OK; +} + + +fssh_status_t +fssh_notify_stat_changed(fssh_mount_id device, fssh_vnode_id node, + uint32_t statFields) +{ + return FSSH_B_OK; +} + + +fssh_status_t +fssh_notify_attribute_changed(fssh_mount_id device, fssh_vnode_id node, + const char *attribute, int32_t cause) +{ + return FSSH_B_OK; +} + + +fssh_status_t +fssh_notify_query_entry_created(fssh_port_id port, int32_t token, + fssh_mount_id device, fssh_vnode_id directory, const char *name, + fssh_vnode_id node) +{ + return FSSH_B_OK; +} + + +fssh_status_t +fssh_notify_query_entry_removed(fssh_port_id port, int32_t token, + fssh_mount_id device, fssh_vnode_id directory, const char *name, + fssh_vnode_id node) +{ + return FSSH_B_OK; +} diff --git a/src/tools/fs_shell/sem.cpp b/src/tools/fs_shell/sem.cpp new file mode 100644 index 0000000000..de90cf2147 --- /dev/null +++ b/src/tools/fs_shell/sem.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include "fssh_errors.h" +#include "fssh_os.h" + + +static void +copy_sem_info(fssh_sem_info* info, const sem_info* systemInfo) +{ + info->sem = systemInfo->sem; + info->team = systemInfo->team; + strcpy(info->name, systemInfo->name); + info->count = systemInfo->count; + info->latest_holder = systemInfo->latest_holder; +} + + +// #pragma mark - + + +fssh_sem_id +fssh_create_sem(int32_t count, const char *name) +{ + return create_sem(count, name); +} + + +fssh_status_t +fssh_delete_sem(fssh_sem_id id) +{ + return delete_sem(id); +} + + +fssh_status_t +fssh_acquire_sem(fssh_sem_id id) +{ + return acquire_sem(id); +} + + +fssh_status_t +fssh_acquire_sem_etc(fssh_sem_id id, int32_t count, uint32_t flags, + fssh_bigtime_t timeout) +{ + return acquire_sem_etc(id, count, flags, timeout); +} + + +fssh_status_t +fssh_release_sem(fssh_sem_id id) +{ + return release_sem(id); +} + + +fssh_status_t +fssh_release_sem_etc(fssh_sem_id id, int32_t count, uint32_t flags) +{ + return release_sem_etc(id, count, flags); +} + + +fssh_status_t +fssh_get_sem_count(fssh_sem_id id, int32_t *threadCount) +{ + return get_sem_count(id, (int32*)threadCount); +} + + +fssh_status_t +fssh_set_sem_owner(fssh_sem_id id, fssh_team_id team) +{ + return set_sem_owner(id, team); +} + + +fssh_status_t +_fssh_get_sem_info(fssh_sem_id id, struct fssh_sem_info *info, + fssh_size_t infoSize) +{ + sem_info systemInfo; + status_t result = get_sem_info(id, &systemInfo); + if (result != B_OK) + return result; + + copy_sem_info(info, &systemInfo); + + return FSSH_B_OK; +} + + +fssh_status_t +_fssh_get_next_sem_info(fssh_team_id team, int32_t *cookie, + struct fssh_sem_info *info, fssh_size_t infoSize) +{ + sem_info systemInfo; + status_t result = get_next_sem_info(team, (int32*)cookie, &systemInfo); + if (result != B_OK) + return result; + + copy_sem_info(info, &systemInfo); + + return FSSH_B_OK; +} + diff --git a/src/tools/fs_shell/sem_unix.cpp b/src/tools/fs_shell/sem_unix.cpp new file mode 100644 index 0000000000..55c8615356 --- /dev/null +++ b/src/tools/fs_shell/sem_unix.cpp @@ -0,0 +1,162 @@ +#include "fssh_os.h" + +#include + +#include "fssh_errors.h" +#include "fssh_kernel_export.h" +#include "fssh_string.h" + + +static const int sSemaphoreCount = 1024; +static fssh_sem_info* sSemaphores[sSemaphoreCount]; + + +fssh_sem_id +fssh_create_sem(int32_t count, const char *name) +{ + // find a free slot + for (int i = 0; i < sSemaphoreCount; i++) { + if (!sSemaphores[i]) { + sSemaphores[i] = new fssh_sem_info; + sSemaphores[i]->sem = i; + sSemaphores[i]->team = 1; + fssh_strcpy(sSemaphores[i]->name, name); + sSemaphores[i]->count = count; + sSemaphores[i]->latest_holder = -1; + return i; + } + } + + return FSSH_B_NO_MORE_SEMS; +} + + +fssh_status_t +fssh_delete_sem(fssh_sem_id id) +{ + if (id < 0 || id >= sSemaphoreCount || !sSemaphores[id]) + return FSSH_B_BAD_SEM_ID; + + delete sSemaphores[id]; + sSemaphores[id] = NULL; + return 0; +} + + +fssh_status_t +fssh_acquire_sem(fssh_sem_id id) +{ + return fssh_acquire_sem_etc(id, 1, 0, 0); +} + + +fssh_status_t +fssh_acquire_sem_etc(fssh_sem_id id, int32_t count, uint32_t flags, + fssh_bigtime_t timeout) +{ + if (id < 0 || id >= sSemaphoreCount || !sSemaphores[id]) + return FSSH_B_BAD_SEM_ID; + if (count < 0) + return FSSH_B_BAD_VALUE; + + if (sSemaphores[id]->count >= count) { + sSemaphores[id]->count -= count; + return FSSH_B_OK; + } + + // can't acquire the sem with that count at the moment + if (!flags & (FSSH_B_RELATIVE_TIMEOUT | FSSH_B_ABSOLUTE_TIMEOUT)) { + fssh_panic("blocking on semaphore %d without timeout!\n", (int)id); + return FSSH_B_BAD_VALUE; + } + + // simulate timeout + if (flags & FSSH_B_RELATIVE_TIMEOUT) { + if (timeout == 0) + return FSSH_B_WOULD_BLOCK; + fssh_snooze(timeout); + } else + fssh_snooze_until(timeout, FSSH_B_SYSTEM_TIMEBASE); + + return FSSH_B_TIMED_OUT; +} + + +fssh_status_t +fssh_release_sem(fssh_sem_id id) +{ + return fssh_release_sem_etc(id, 1, 0); +} + + +fssh_status_t +fssh_release_sem_etc(fssh_sem_id id, int32_t count, uint32_t flags) +{ + if (id < 0 || id >= sSemaphoreCount || !sSemaphores[id]) + return FSSH_B_BAD_SEM_ID; + if (count < 0) + return FSSH_B_BAD_VALUE; + + sSemaphores[id]->count += count; + return FSSH_B_OK; +} + + +fssh_status_t +fssh_get_sem_count(fssh_sem_id id, int32_t *threadCount) +{ + if (id < 0 || id >= sSemaphoreCount || !sSemaphores[id]) + return FSSH_B_BAD_SEM_ID; + if (!threadCount) + return FSSH_B_BAD_VALUE; + + *threadCount = sSemaphores[id]->count; + return FSSH_B_OK; +} + + +fssh_status_t +fssh_set_sem_owner(fssh_sem_id id, fssh_team_id team) +{ + if (id < 0 || id >= sSemaphoreCount || !sSemaphores[id]) + return FSSH_B_BAD_SEM_ID; + if (team != 1) + return FSSH_B_BAD_VALUE; + + sSemaphores[id]->team = team; + return FSSH_B_OK; +} + + +fssh_status_t +_fssh_get_sem_info(fssh_sem_id id, struct fssh_sem_info *info, + fssh_size_t infoSize) +{ + if (id < 0 || id >= sSemaphoreCount || !sSemaphores[id]) + return FSSH_B_BAD_SEM_ID; + if (!info) + return FSSH_B_BAD_VALUE; + + fssh_memcpy(info, sSemaphores[id], sizeof(fssh_sem_info)); + + return FSSH_B_OK; +} + + +fssh_status_t +_fssh_get_next_sem_info(fssh_team_id team, int32_t *cookie, + struct fssh_sem_info *info, fssh_size_t infoSize) +{ + if (team != 0 || team != 1) + return FSSH_B_BAD_TEAM_ID; + + for (int i = *cookie; i < sSemaphoreCount; i++) { + if (sSemaphores[i]) { + *cookie = i + 1; + return _fssh_get_sem_info(i, info, infoSize); + } + } + + return FSSH_B_ENTRY_NOT_FOUND; +} + diff --git a/src/tools/fs_shell/stat.cpp b/src/tools/fs_shell/stat.cpp new file mode 100644 index 0000000000..dce6b6c7d6 --- /dev/null +++ b/src/tools/fs_shell/stat.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "fssh_stat.h" + +#include + +#include "stat_util.h" + + +using FSShell::from_platform_stat; + + +int +fssh_stat(const char *path, struct fssh_stat *fsshStat) +{ + struct stat st; + if (stat(path, &st) < 0) + return -1; + + from_platform_stat(&st, fsshStat); + + return 0; +} + + +int +fssh_fstat(int fd, struct fssh_stat *fsshStat) +{ + struct stat st; + if (fstat(fd, &st) < 0) + return -1; + + from_platform_stat(&st, fsshStat); + + return 0; +} + + +int +fssh_lstat(const char *path, struct fssh_stat *fsshStat) +{ + struct stat st; + if (lstat(path, &st) < 0) + return -1; + + from_platform_stat(&st, fsshStat); + + return 0; +} diff --git a/src/tools/fs_shell/stat_util.cpp b/src/tools/fs_shell/stat_util.cpp new file mode 100644 index 0000000000..02ede9d025 --- /dev/null +++ b/src/tools/fs_shell/stat_util.cpp @@ -0,0 +1,227 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "stat_util.h" + +#include +#include + +#include "fssh_fcntl.h" +#include "fssh_stat.h" + + +namespace FSShell { + +// Haiku only mode_t flags +#ifndef HAIKU_HOST_PLATFORM_HAIKU +# define S_LINK_SELF_HEALING 0 +# define S_LINK_AUTO_DELETE 0 +#endif + + +fssh_mode_t +from_platform_mode(mode_t mode) +{ + #define SET_ST_MODE_BIT(flag, fsshFlag) \ + if (mode & flag) \ + fsshMode |= fsshFlag; + + fssh_mode_t fsshMode = 0; + + // BeOS/Haiku only + #ifdef __BEOS__ + SET_ST_MODE_BIT(FSSH_S_ATTR_DIR, S_ATTR_DIR); + SET_ST_MODE_BIT(FSSH_S_ATTR, S_ATTR); + SET_ST_MODE_BIT(FSSH_S_INDEX_DIR, S_INDEX_DIR); + SET_ST_MODE_BIT(FSSH_S_INT_INDEX, S_INT_INDEX); + SET_ST_MODE_BIT(FSSH_S_UINT_INDEX, S_UINT_INDEX); + SET_ST_MODE_BIT(FSSH_S_LONG_LONG_INDEX, S_LONG_LONG_INDEX); + SET_ST_MODE_BIT(FSSH_S_ULONG_LONG_INDEX, S_ULONG_LONG_INDEX); + SET_ST_MODE_BIT(FSSH_S_FLOAT_INDEX, S_FLOAT_INDEX); + SET_ST_MODE_BIT(FSSH_S_DOUBLE_INDEX, S_DOUBLE_INDEX); + SET_ST_MODE_BIT(FSSH_S_ALLOW_DUPS, S_ALLOW_DUPS); + SET_ST_MODE_BIT(FSSH_S_LINK_SELF_HEALING, S_LINK_SELF_HEALING); + SET_ST_MODE_BIT(FSSH_S_LINK_AUTO_DELETE, S_LINK_AUTO_DELETE); + #endif + + switch (mode & S_IFMT) { + case S_IFLNK: + fsshMode |= FSSH_S_IFLNK; + break; + case S_IFREG: + fsshMode |= FSSH_S_IFREG; + break; + case S_IFBLK: + fsshMode |= FSSH_S_IFBLK; + break; + case S_IFDIR: + fsshMode |= FSSH_S_IFDIR; + break; + case S_IFIFO: + fsshMode |= FSSH_S_IFIFO; + break; + } + + SET_ST_MODE_BIT(FSSH_S_ISUID, S_ISUID); + SET_ST_MODE_BIT(FSSH_S_ISGID, S_ISGID); + SET_ST_MODE_BIT(FSSH_S_ISVTX, S_ISVTX); + SET_ST_MODE_BIT(FSSH_S_IRUSR, S_IRUSR); + SET_ST_MODE_BIT(FSSH_S_IWUSR, S_IWUSR); + SET_ST_MODE_BIT(FSSH_S_IXUSR, S_IXUSR); + SET_ST_MODE_BIT(FSSH_S_IRGRP, S_IRGRP); + SET_ST_MODE_BIT(FSSH_S_IWGRP, S_IWGRP); + SET_ST_MODE_BIT(FSSH_S_IXGRP, S_IXGRP); + SET_ST_MODE_BIT(FSSH_S_IROTH, S_IROTH); + SET_ST_MODE_BIT(FSSH_S_IWOTH, S_IWOTH); + SET_ST_MODE_BIT(FSSH_S_IXOTH, S_IXOTH); + + #undef SET_ST_MODE_BIT + + return fsshMode; +} + + +mode_t +to_platform_mode(fssh_mode_t fsshMode) +{ + #define SET_ST_MODE_BIT(flag, fsshFlag) \ + if (fsshMode & fsshFlag) \ + mode |= flag; + + mode_t mode = 0; + + // BeOS/Haiku only + #ifdef __BEOS__ + SET_ST_MODE_BIT(FSSH_S_ATTR_DIR, S_ATTR_DIR); + SET_ST_MODE_BIT(FSSH_S_ATTR, S_ATTR); + SET_ST_MODE_BIT(FSSH_S_INDEX_DIR, S_INDEX_DIR); + SET_ST_MODE_BIT(FSSH_S_INT_INDEX, S_INT_INDEX); + SET_ST_MODE_BIT(FSSH_S_UINT_INDEX, S_UINT_INDEX); + SET_ST_MODE_BIT(FSSH_S_LONG_LONG_INDEX, S_LONG_LONG_INDEX); + SET_ST_MODE_BIT(FSSH_S_ULONG_LONG_INDEX, S_ULONG_LONG_INDEX); + SET_ST_MODE_BIT(FSSH_S_FLOAT_INDEX, S_FLOAT_INDEX); + SET_ST_MODE_BIT(FSSH_S_DOUBLE_INDEX, S_DOUBLE_INDEX); + SET_ST_MODE_BIT(FSSH_S_ALLOW_DUPS, S_ALLOW_DUPS); + SET_ST_MODE_BIT(FSSH_S_LINK_SELF_HEALING, S_LINK_SELF_HEALING); + SET_ST_MODE_BIT(FSSH_S_LINK_AUTO_DELETE, S_LINK_AUTO_DELETE); + #endif + + switch (fsshMode & FSSH_S_IFMT) { + case FSSH_S_IFLNK: + mode |= S_IFLNK; + break; + case FSSH_S_IFREG: + mode |= S_IFREG; + break; + case FSSH_S_IFBLK: + mode |= S_IFBLK; + break; + case FSSH_S_IFDIR: + mode |= S_IFDIR; + break; + case FSSH_S_IFIFO: + mode |= S_IFIFO; + break; + } + + SET_ST_MODE_BIT(FSSH_S_ISUID, S_ISUID); + SET_ST_MODE_BIT(FSSH_S_ISGID, S_ISGID); + SET_ST_MODE_BIT(FSSH_S_ISVTX, S_ISVTX); + SET_ST_MODE_BIT(FSSH_S_IRUSR, S_IRUSR); + SET_ST_MODE_BIT(FSSH_S_IWUSR, S_IWUSR); + SET_ST_MODE_BIT(FSSH_S_IXUSR, S_IXUSR); + SET_ST_MODE_BIT(FSSH_S_IRGRP, S_IRGRP); + SET_ST_MODE_BIT(FSSH_S_IWGRP, S_IWGRP); + SET_ST_MODE_BIT(FSSH_S_IXGRP, S_IXGRP); + SET_ST_MODE_BIT(FSSH_S_IROTH, S_IROTH); + SET_ST_MODE_BIT(FSSH_S_IWOTH, S_IWOTH); + SET_ST_MODE_BIT(FSSH_S_IXOTH, S_IXOTH); + + #undef SET_ST_MODE_BIT + + return mode; +} + + +void +from_platform_stat(const struct stat *st, struct fssh_stat *fsshStat) +{ + fsshStat->fssh_st_dev = st->st_dev; + fsshStat->fssh_st_ino = st->st_ino; + fsshStat->fssh_st_mode = from_platform_mode(st->st_mode); + fsshStat->fssh_st_nlink = st->st_nlink; + fsshStat->fssh_st_uid = st->st_uid; + fsshStat->fssh_st_gid = st->st_gid; + fsshStat->fssh_st_size = st->st_size; + fsshStat->fssh_st_blksize = st->st_blksize; + fsshStat->fssh_st_atime = st->st_atime; + fsshStat->fssh_st_mtime = st->st_mtime; + fsshStat->fssh_st_ctime = st->st_ctime; + fsshStat->fssh_st_crtime = st->st_ctime; + fsshStat->fssh_st_type = 0; +} + + +void +to_platform_stat(const struct fssh_stat *fsshStat, struct stat *st) +{ + st->st_dev = fsshStat->fssh_st_dev; + st->st_ino = fsshStat->fssh_st_ino; + st->st_mode = to_platform_mode(fsshStat->fssh_st_mode); + st->st_nlink = fsshStat->fssh_st_nlink; + st->st_uid = fsshStat->fssh_st_uid; + st->st_gid = fsshStat->fssh_st_gid; + st->st_size = fsshStat->fssh_st_size; + st->st_blksize = fsshStat->fssh_st_blksize; + st->st_atime = fsshStat->fssh_st_atime; + st->st_mtime = fsshStat->fssh_st_mtime; + st->st_ctime = fsshStat->fssh_st_ctime; +// st->st_crtime = fsshStat->fssh_st_crtime; +// st->st_type = fsshStat->fssh_st_type; +} + + +int +to_platform_open_mode(int fsshMode) +{ + #define SET_OPEN_MODE_FLAG(flag, fsshFlag) \ + if (fsshMode & fsshFlag) \ + mode |= flag; + + int mode = 0; + + // the r/w mode + switch (fsshMode & FSSH_O_RWMASK) { + case FSSH_O_RDONLY: + mode |= O_RDONLY; + break; + case FSSH_O_WRONLY: + mode |= O_WRONLY; + break; + case FSSH_O_RDWR: + mode |= O_RDWR; + break; + } + + // the flags + //SET_OPEN_MODE_FLAG(O_CLOEXEC, FSSH_O_CLOEXEC) + SET_OPEN_MODE_FLAG(O_NONBLOCK, FSSH_O_NONBLOCK) + SET_OPEN_MODE_FLAG(O_EXCL, FSSH_O_EXCL) + SET_OPEN_MODE_FLAG(O_CREAT, FSSH_O_CREAT) + SET_OPEN_MODE_FLAG(O_TRUNC, FSSH_O_TRUNC) + SET_OPEN_MODE_FLAG(O_APPEND, FSSH_O_APPEND) + SET_OPEN_MODE_FLAG(O_NOCTTY, FSSH_O_NOCTTY) + SET_OPEN_MODE_FLAG(O_NOTRAVERSE, FSSH_O_NOTRAVERSE) + //SET_OPEN_MODE_FLAG(O_TEXT, FSSH_O_TEXT) + //SET_OPEN_MODE_FLAG(O_BINARY, FSSH_O_BINARY) + + #undef SET_OPEN_MODE_FLAG + + return mode; +} + +} // namespace FSShell diff --git a/src/tools/fs_shell/stat_util.h b/src/tools/fs_shell/stat_util.h new file mode 100644 index 0000000000..35347032e5 --- /dev/null +++ b/src/tools/fs_shell/stat_util.h @@ -0,0 +1,26 @@ +/* + * Copyright 2005-2007, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ +#ifndef _FSSH_STAT_UTIL_H +#define _FSSH_STAT_UTIL_H + +#include + +#include "fssh_defs.h" +#include "fssh_stat.h" + + +namespace FSShell { + +mode_t to_platform_mode(fssh_mode_t mode); + +void from_platform_stat(const struct stat *st, struct fssh_stat *fsshStat); +void to_platform_stat(const struct my_stat *fsshStat, struct stat *st); + +extern int to_platform_open_mode(int fsshMode); + +} // namespace FSShell + + +#endif // _FSSH_STAT_UTIL_H diff --git a/src/tools/fs_shell/stdio.cpp b/src/tools/fs_shell/stdio.cpp new file mode 100644 index 0000000000..3ae5718383 --- /dev/null +++ b/src/tools/fs_shell/stdio.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "fssh_stdio.h" + +#include +#include + + +int +fssh_sprintf(char *string, char const *format, ...) +{ + va_list args; + va_start(args, format); + + int result = vsprintf(string, format, args); + + va_end(args); + + return result; +} + + +int +fssh_snprintf(char *string, fssh_size_t size, char const *format, ...) +{ + va_list args; + va_start(args, format); + + int result = vsnprintf(string, size, format, args); + + va_end(args); + + return result; +} + + +int +fssh_vsprintf(char *string, char const *format, va_list ap) +{ + return vsprintf(string, format, ap); +} + + +int +fssh_vsnprintf(char *string, fssh_size_t size, char const *format, va_list ap) +{ + return vsnprintf(string, size, format, ap); +} diff --git a/src/tools/fs_shell/string.cpp b/src/tools/fs_shell/string.cpp new file mode 100644 index 0000000000..3ecb73c1d9 --- /dev/null +++ b/src/tools/fs_shell/string.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "fssh_string.h" + +#include + + +void* +fssh_memchr(const void *source, int value, fssh_size_t length) +{ + return memchr(source, value, length); +} + + +int +fssh_memcmp(const void *buffer1, const void *buffer2, fssh_size_t length) +{ + return memcmp(buffer1, buffer2, length); +} + + +void* +fssh_memcpy(void *dest, const void *source, fssh_size_t length) +{ + return memcpy(dest, source, length); +} + + +void* +fssh_memccpy(void *dest, const void *source, int stopByte, fssh_size_t length) +{ + return memccpy(dest, source, stopByte, length); +} + + +void* +fssh_memmove(void *dest, const void *source, fssh_size_t length) +{ + return memmove(dest, source, length); +} + + +void* +fssh_memset(void *dest, int value, fssh_size_t length) +{ + return memset(dest, value, length); +} + + +char* +fssh_strcpy(char *dest, const char *source) +{ + return strcpy(dest, source); +} + + +char* +fssh_strncpy(char *dest, const char *source, fssh_size_t length) +{ + return strncpy(dest, source, length); +} + + +char* +fssh_strcat(char *dest, const char *source) +{ + return strcat(dest, source); +} + + +char* +fssh_strncat(char *dest, const char *source, fssh_size_t length) +{ + return strncat(dest, source, length); +} + + +fssh_size_t +fssh_strlen(const char *string) +{ + return strlen(string); +} + + +int +fssh_strcmp(const char *string1, const char *string2) +{ + return strcmp(string1, string2); +} + + +int +fssh_strncmp(const char *string1, const char *string2, fssh_size_t length) +{ + return strncmp(string1, string2, length); +} + + +char* +fssh_strchr(const char *string, int character) +{ + return strchr(string, character); +} + + +char* +fssh_strrchr(const char *string, int character) +{ + return strrchr(string, character); +} + + +char* +fssh_strstr(const char *string, const char *searchString) +{ + return strstr(string, searchString); +} + + +#if 0 +char* +fssh_strchrnul(const char *string, int character) +{ +} +#endif // 0 + + +char* +fssh_strpbrk(const char *string, const char *set) +{ + return strpbrk(string, set); +} + + +char* +fssh_strtok(char *string, const char *set) +{ + return strtok(string, set); +} + + +char* +fssh_strtok_r(char *string, const char *set, char **savePointer) +{ + return strtok_r(string, set, savePointer); +} + + +fssh_size_t +fssh_strspn(const char *string, const char *set) +{ + return strspn(string, set); +} + + +fssh_size_t +fssh_strcspn(const char *string, const char *set) +{ + return strcspn(string, set); +} + + +int +fssh_strcoll(const char *string1, const char *string2) +{ + return strcoll(string1, string2); +} + + +fssh_size_t +fssh_strxfrm(char *string1, const char *string2, fssh_size_t length) +{ + return strxfrm(string1, string2, length); +} + + +char* +fssh_strerror(int errorCode) +{ + return strerror(errorCode); +} + + +#if 0 +int +fssh_strerror_r(int errorCode, char *buffer, fssh_size_t bufferSize) +{ + return strerror_r(errorCode, buffer, bufferSize); +} +#endif // 0 + + +int +fssh_strcasecmp(const char *string1, const char *string2) +{ + return strcasecmp(string1, string2); +} + + +int +fssh_strncasecmp(const char *string1, const char *string2, fssh_size_t length) +{ + return strncasecmp(string1, string2, length); +} + + +char* +fssh_strcasestr(const char *string, const char *searchString) +{ + return strcasestr(string, searchString); +} + + +char* +fssh_strdup(const char *string) +{ + return strdup(string); +} + + +char* +fssh_stpcpy(char *dest, const char *source) +{ + return stpcpy(dest, source); +} + + +#if 0 +const char * +fssh_strtcopy(char *dest, const char *source) +{ + return strtcopy(dest, source); +} +#endif // 0 + + +fssh_size_t +fssh_strlcat(char *dest, const char *source, fssh_size_t length) +{ + return strlcat(dest, source, length); +} + + +fssh_size_t +fssh_strlcpy(char *dest, const char *source, fssh_size_t length) +{ + return strlcpy(dest, source, length); +} + + +fssh_size_t +fssh_strnlen(const char *string, fssh_size_t count) +{ + return strnlen(string, count); +} + + +int +fssh_ffs(int i) +{ + return ffs(i); +} + + +char* +fssh_index(const char *s, int c) +{ + return index(s, c); +} + + +char* +fssh_rindex(char const *s, int c) +{ + return rindex(s, c); +} diff --git a/src/tools/fs_shell/thread.cpp b/src/tools/fs_shell/thread.cpp new file mode 100644 index 0000000000..b6b2e39032 --- /dev/null +++ b/src/tools/fs_shell/thread.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "fssh_os.h" + +#include + +#include "fssh_errors.h" + + +fssh_status_t +fssh_kill_thread(fssh_thread_id thread) +{ + return kill_thread(thread); +} + + +fssh_status_t +fssh_resume_thread(fssh_thread_id thread) +{ + return resume_thread(thread); +} + + +fssh_status_t +fssh_suspend_thread(fssh_thread_id thread) +{ + return suspend_thread(thread); +} + + +fssh_thread_id +fssh_find_thread(const char *name) +{ + return find_thread(name); +} + + +fssh_status_t +fssh_snooze(fssh_bigtime_t amount) +{ + return snooze(amount); +} + + +fssh_status_t +fssh_snooze_until(fssh_bigtime_t time, int timeBase) +{ + return snooze_until(time, timeBase); +} diff --git a/src/tools/fs_shell/time.cpp b/src/tools/fs_shell/time.cpp new file mode 100644 index 0000000000..f64b41287f --- /dev/null +++ b/src/tools/fs_shell/time.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "fssh_os.h" +#include "fssh_time.h" + +#include + +#include + + +// #pragma mark - OS.h + +#if 0 + +uint32_t +fssh_real_time_clock(void) +{ +} + + +void +fssh_set_real_time_clock(uint32_t secs_since_jan1_1970) +{ +} + + +fssh_bigtime_t +fssh_real_time_clock_usecs(void) +{ +} + + +fssh_status_t +fssh_set_timezone(char *timezone) +{ +} + +#endif // 0 + +fssh_bigtime_t +fssh_system_time(void) +{ + return system_time(); +} + + +// #pragma mark - time.h + + +fssh_time_t +fssh_time(fssh_time_t *timer) +{ + time_t result = time(NULL); + if (timer) + *timer = result; + return result; +} diff --git a/src/tools/fs_shell/uio.cpp b/src/tools/fs_shell/uio.cpp new file mode 100644 index 0000000000..f90511c12a --- /dev/null +++ b/src/tools/fs_shell/uio.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "fssh_uio.h" + +#include + +#include +#include + + +static const fssh_size_t kMaxIOVecs = 1024; + + +bool +prepare_iovecs(const struct fssh_iovec *vecs, fssh_size_t count, + struct iovec* systemVecs) +{ + if (count > kMaxIOVecs) { + errno = B_BAD_VALUE; + return false; + } + + for (fssh_size_t i = 0; i < count; i++) { + systemVecs[i].iov_base = vecs[i].iov_base; + systemVecs[i].iov_len = vecs[i].iov_len; + } + + return true; +} + + +fssh_ssize_t +fssh_readv(int fd, const struct fssh_iovec *vector, fssh_size_t count) +{ + struct iovec systemVecs[kMaxIOVecs]; + if (!prepare_iovecs(vector, count, systemVecs)) + return -1; + + return readv(fd, systemVecs, count); +} + + +fssh_ssize_t +fssh_readv_pos(int fd, fssh_off_t pos, const struct fssh_iovec *vec, + fssh_size_t count) +{ + struct iovec systemVecs[kMaxIOVecs]; + if (!prepare_iovecs(vec, count, systemVecs)) + return -1; + + return readv_pos(fd, pos, systemVecs, count); +} + + +fssh_ssize_t +fssh_writev(int fd, const struct fssh_iovec *vector, fssh_size_t count) +{ + struct iovec systemVecs[kMaxIOVecs]; + if (!prepare_iovecs(vector, count, systemVecs)) + return -1; + + return writev(fd, systemVecs, count); +} + + +fssh_ssize_t +fssh_writev_pos(int fd, fssh_off_t pos, const struct fssh_iovec *vec, + fssh_size_t count) +{ + struct iovec systemVecs[kMaxIOVecs]; + if (!prepare_iovecs(vec, count, systemVecs)) + return -1; + + return writev_pos(fd, pos, systemVecs, count); +} diff --git a/src/tools/fs_shell/unistd.cpp b/src/tools/fs_shell/unistd.cpp new file mode 100644 index 0000000000..0a37334698 --- /dev/null +++ b/src/tools/fs_shell/unistd.cpp @@ -0,0 +1,226 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include "fssh_unistd.h" + +#include +#include +#include + +#include + +#include "fssh_drivers.h" +#include "fssh_errno.h" + +#ifdef __BEOS__ +# include +#else +# include // the correct place of definition for ioctl() + +# if defined(HAIKU_HOST_PLATFORM_LINUX) +# include +# endif +#endif + + +#ifdef HAIKU_HOST_PLATFORM_LINUX + +static bool +test_size(int fd, off_t size) +{ + char buffer[1]; + + if (size == 0) + return true; + + if (lseek(fd, size - 1, SEEK_SET) < 0) + return false; + + return (read(fd, &buffer, 1) == 1); +} + + +static off_t +get_partition_size(int fd, off_t maxSize) +{ + // binary search + off_t lower = 0; + off_t upper = maxSize; + while (lower < upper) { + off_t mid = (lower + upper + 1) / 2; + if (test_size(fd, mid)) + lower = mid; + else + upper = mid - 1; + } + + return lower; +} + +#endif // HAIKU_HOST_PLATFORM_LINUX + + +int +fssh_close(int fd) +{ + return close(fd); +} + + +int +fssh_ioctl(int fd, unsigned long op, ...) +{ + status_t error = B_BAD_VALUE; + va_list list; + + // count arguments + + va_start(list, op); + + switch (op) { + case FSSH_B_GET_GEOMETRY: + { + fssh_device_geometry *geometry + = va_arg(list, fssh_device_geometry*); + + #ifdef __BEOS__ + if (ioctl(fd, B_GET_GEOMETRY, systemGeometry) == 0) { + geometry->bytes_per_sector + = systemGeometry->bytes_per_sector; + geometry->sectors_per_track + = systemGeometry->sectors_per_track; + geometry->cylinder_count = systemGeometry->cylinder_count; + geometry->head_count = systemGeometry->head_count; + geometry->device_type = systemGeometry->device_type; + geometry->removable = systemGeometry->removable; + geometry->read_only = systemGeometry->read_only; + geometry->write_once = systemGeometry->write_once; + error = B_OK; + } else + error = errno; + + #elif defined(HAIKU_HOST_PLATFORM_LINUX) + struct hd_geometry hdGeometry; + // BLKGETSIZE and BLKGETSIZE64 don't seem to work for + // partitions. So we get the device geometry (there only seems + // to be HDIO_GETGEO, which is kind of obsolete, BTW), and + // get the partition size via binary search. + if (ioctl(fd, HDIO_GETGEO, &hdGeometry) == 0) { + off_t bytesPerCylinder = (off_t)hdGeometry.heads + * hdGeometry.sectors * 512; + off_t deviceSize = bytesPerCylinder * hdGeometry.cylinders; + off_t partitionSize = get_partition_size(fd, deviceSize); + + geometry->head_count = hdGeometry.heads; + geometry->cylinder_count = partitionSize / bytesPerCylinder; + geometry->sectors_per_track = hdGeometry.sectors; + + // TODO: Get the real values... + geometry->bytes_per_sector = 512; + geometry->device_type = FSSH_B_DISK; + geometry->removable = false; + geometry->read_only = false; + geometry->write_once = false; + error = B_OK; + } else + error = errno; + + #else + // Not implemented for this platform, i.e. we won't be able to + // deal with block devices. + #endif + + break; + } + + case FSSH_B_FLUSH_DRIVE_CACHE: + { + #ifdef __BEOS__ + if (ioctl(fd, B_FLUSH_DRIVE_CACHE) == 0) + error = B_OK; + else + error = errno; + #else + error = B_OK; + #endif + + break; + } + + case 10000: // IOCTL_FILE_UNCACHED_IO + { + #ifdef __BEOS__ + if (ioctl(fd, 10000) == 0) + error = B_OK; + else + error = errno; + #else + error = B_OK; + #endif + + break; + } + } + + va_end(list); + + if (error != B_OK) { + fssh_set_errno(error); + return -1; + } + return 0; +} + + +fssh_ssize_t +fssh_read_pos(int fd, fssh_off_t pos, void *buffer, fssh_size_t count) +{ + return read_pos(fd, pos, buffer, count); +} + + +fssh_ssize_t +fssh_write_pos(int fd, fssh_off_t pos, const void *buffer, fssh_size_t count) +{ + return write_pos(fd, pos, buffer, count); +} + + +fssh_gid_t +fssh_getegid(void) +{ + return 0; +} + + +fssh_uid_t +fssh_geteuid(void) +{ + return 0; +} + + +fssh_gid_t +fssh_getgid(void) +{ + return 0; +} + + +#if 0 +int +fssh_getgroups(int groupSize, fssh_gid_t groupList[]) +{ +} +#endif // 0 + + +fssh_uid_t +fssh_getuid(void) +{ + return 0; +} diff --git a/src/tools/fs_shell/vfs.cpp b/src/tools/fs_shell/vfs.cpp new file mode 100644 index 0000000000..fe6e098417 --- /dev/null +++ b/src/tools/fs_shell/vfs.cpp @@ -0,0 +1,5170 @@ +/* + * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + +/* Virtual File System and File System Interface Layer */ + +#include "vfs.h" + +#include + +#include "fd.h" +#include "fssh_atomic.h" +#include "fssh_defs.h" +#include "fssh_dirent.h" +#include "fssh_fcntl.h" +#include "fssh_fs_info.h" +#include "fssh_fs_volume.h" +#include "fssh_kernel_export.h" +#include "fssh_stat.h" +#include "fssh_string.h" +#include "fssh_unistd.h" +#include "hash.h" +#include "KPath.h" + + +//#define TRACE_VFS +#ifdef TRACE_VFS +# define TRACE(x) fssh_dprintf x +# define FUNCTION(x) fssh_dprintf x +#else +# define TRACE(x) ; +# define FUNCTION(x) ; +#endif + +#define ADD_DEBUGGER_COMMANDS + +#define ASSERT_LOCKED_MUTEX(x) +#define ASSERT(x) + +namespace FSShell { + + +const static uint32_t kMaxUnusedVnodes = 8192; + // This is the maximum number of unused vnodes that the system + // will keep around (weak limit, if there is enough memory left, + // they won't get flushed even when hitting that limit). + // It may be chosen with respect to the available memory or enhanced + // by some timestamp/frequency heurism. + +struct vnode { + struct vnode *next; + vm_cache_ref *cache; + fssh_mount_id device; + list_link mount_link; + list_link unused_link; + fssh_vnode_id id; + fssh_fs_vnode private_node; + struct fs_mount *mount; + struct vnode *covered_by; + int32_t ref_count; + uint8_t remove : 1; + uint8_t busy : 1; + uint8_t unpublished : 1; + struct file_descriptor *mandatory_locked_by; +}; + +struct vnode_hash_key { + fssh_mount_id device; + fssh_vnode_id vnode; +}; + +#define FS_CALL(vnode, op) (vnode->mount->fs->op) +#define FS_MOUNT_CALL(mount, op) (mount->fs->op) + +/** \brief Structure to manage a mounted file system + + Note: The root_vnode and covers_vnode fields (what others?) are + initialized in fs_mount() and not changed afterwards. That is as soon + as the mount is mounted and it is made sure it won't be unmounted + (e.g. by holding a reference to a vnode of that mount) (read) access + to those fields is always safe, even without additional locking. Morever + while mounted the mount holds a reference to the covers_vnode, and thus + making the access path vnode->mount->covers_vnode->mount->... safe if a + reference to vnode is held (note that for the root mount covers_vnode + is NULL, though). + */ +struct fs_mount { + struct fs_mount *next; + fssh_file_system_module_info *fs; + fssh_mount_id id; + void *cookie; + char *device_name; + char *fs_name; + recursive_lock rlock; // guards the vnodes list + struct vnode *root_vnode; + struct vnode *covers_vnode; + struct list vnodes; + bool unmounting; + bool owns_file_device; +}; + +static mutex sFileSystemsMutex; + +/** \brief Guards sMountsTable. + * + * The holder is allowed to read/write access the sMountsTable. + * Manipulation of the fs_mount structures themselves + * (and their destruction) requires different locks though. + */ +static mutex sMountMutex; + +/** \brief Guards mount/unmount operations. + * + * The fs_mount() and fs_unmount() hold the lock during their whole operation. + * That is locking the lock ensures that no FS is mounted/unmounted. In + * particular this means that + * - sMountsTable will not be modified, + * - the fields immutable after initialization of the fs_mount structures in + * sMountsTable will not be modified, + * - vnode::covered_by of any vnode in sVnodeTable will not be modified. + * + * The thread trying to lock the lock must not hold sVnodeMutex or + * sMountMutex. + */ +static recursive_lock sMountOpLock; + +/** \brief Guards the vnode::covered_by field of any vnode + * + * The holder is allowed to read access the vnode::covered_by field of any + * vnode. Additionally holding sMountOpLock allows for write access. + * + * The thread trying to lock the must not hold sVnodeMutex. + */ +static mutex sVnodeCoveredByMutex; + +/** \brief Guards sVnodeTable. + * + * The holder is allowed to read/write access sVnodeTable and to + * to any unbusy vnode in that table, save + * to the immutable fields (device, id, private_node, mount) to which + * only read-only access is allowed, and to the field covered_by, which is + * guarded by sMountOpLock and sVnodeCoveredByMutex. + * + * The thread trying to lock the mutex must not hold sMountMutex. + * You must not have this mutex held when calling create_sem(), as this + * might call vfs_free_unused_vnodes(). + */ +static mutex sVnodeMutex; + +#define VNODE_HASH_TABLE_SIZE 1024 +static hash_table *sVnodeTable; +static list sUnusedVnodeList; +static uint32_t sUnusedVnodes = 0; +static struct vnode *sRoot; + +#define MOUNTS_HASH_TABLE_SIZE 16 +static hash_table *sMountsTable; +static fssh_mount_id sNextMountID = 1; + +fssh_mode_t __fssh_gUmask = 022; + +/* function declarations */ + +// file descriptor operation prototypes +static fssh_status_t file_read(struct file_descriptor *, fssh_off_t pos, + void *buffer, fssh_size_t *); +static fssh_status_t file_write(struct file_descriptor *, fssh_off_t pos, + const void *buffer, fssh_size_t *); +static fssh_off_t file_seek(struct file_descriptor *, fssh_off_t pos, + int seek_type); +static void file_free_fd(struct file_descriptor *); +static fssh_status_t file_close(struct file_descriptor *); +static fssh_status_t dir_read(struct file_descriptor *, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_count); +static fssh_status_t dir_read(struct vnode *vnode, fssh_fs_cookie cookie, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_count); +static fssh_status_t dir_rewind(struct file_descriptor *); +static void dir_free_fd(struct file_descriptor *); +static fssh_status_t dir_close(struct file_descriptor *); +static fssh_status_t attr_dir_read(struct file_descriptor *, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_count); +static fssh_status_t attr_dir_rewind(struct file_descriptor *); +static void attr_dir_free_fd(struct file_descriptor *); +static fssh_status_t attr_dir_close(struct file_descriptor *); +static fssh_status_t attr_read(struct file_descriptor *, fssh_off_t pos, + void *buffer, fssh_size_t *); +static fssh_status_t attr_write(struct file_descriptor *, fssh_off_t pos, + const void *buffer, fssh_size_t *); +static fssh_off_t attr_seek(struct file_descriptor *, fssh_off_t pos, + int seek_type); +static void attr_free_fd(struct file_descriptor *); +static fssh_status_t attr_close(struct file_descriptor *); +static fssh_status_t attr_read_stat(struct file_descriptor *, + struct fssh_stat *); +static fssh_status_t attr_write_stat(struct file_descriptor *, + const struct fssh_stat *, int statMask); +static fssh_status_t index_dir_read(struct file_descriptor *, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_count); +static fssh_status_t index_dir_rewind(struct file_descriptor *); +static void index_dir_free_fd(struct file_descriptor *); +static fssh_status_t index_dir_close(struct file_descriptor *); +static fssh_status_t query_read(struct file_descriptor *, + struct fssh_dirent *buffer, fssh_size_t bufferSize, + uint32_t *_count); +static fssh_status_t query_rewind(struct file_descriptor *); +static void query_free_fd(struct file_descriptor *); +static fssh_status_t query_close(struct file_descriptor *); + +static fssh_status_t common_ioctl(struct file_descriptor *, uint32_t, void *buf, + fssh_size_t len); +static fssh_status_t common_read_stat(struct file_descriptor *, + struct fssh_stat *); +static fssh_status_t common_write_stat(struct file_descriptor *, + const struct fssh_stat *, int statMask); + +static fssh_status_t vnode_path_to_vnode(struct vnode *vnode, char *path, + bool traverseLeafLink, int count, struct vnode **_vnode, + fssh_vnode_id *_parentID, int *_type); +static fssh_status_t dir_vnode_to_path(struct vnode *vnode, char *buffer, + fssh_size_t bufferSize); +static fssh_status_t fd_and_path_to_vnode(int fd, char *path, + bool traverseLeafLink, struct vnode **_vnode, + fssh_vnode_id *_parentID, bool kernel); +static void inc_vnode_ref_count(struct vnode *vnode); +static fssh_status_t dec_vnode_ref_count(struct vnode *vnode, bool reenter); +static inline void put_vnode(struct vnode *vnode); + +static struct fd_ops sFileOps = { + file_read, + file_write, + file_seek, + common_ioctl, + NULL, + NULL, + NULL, // read_dir() + NULL, // rewind_dir() + common_read_stat, + common_write_stat, + file_close, + file_free_fd +}; + +static struct fd_ops sDirectoryOps = { + NULL, // read() + NULL, // write() + NULL, // seek() + common_ioctl, + NULL, // select() + NULL, // deselect() + dir_read, + dir_rewind, + common_read_stat, + common_write_stat, + dir_close, + dir_free_fd +}; + +static struct fd_ops sAttributeDirectoryOps = { + NULL, // read() + NULL, // write() + NULL, // seek() + common_ioctl, + NULL, // select() + NULL, // deselect() + attr_dir_read, + attr_dir_rewind, + common_read_stat, + common_write_stat, + attr_dir_close, + attr_dir_free_fd +}; + +static struct fd_ops sAttributeOps = { + attr_read, + attr_write, + attr_seek, + common_ioctl, + NULL, // select() + NULL, // deselect() + NULL, // read_dir() + NULL, // rewind_dir() + attr_read_stat, + attr_write_stat, + attr_close, + attr_free_fd +}; + +static struct fd_ops sIndexDirectoryOps = { + NULL, // read() + NULL, // write() + NULL, // seek() + NULL, // ioctl() + NULL, // select() + NULL, // deselect() + index_dir_read, + index_dir_rewind, + NULL, // read_stat() + NULL, // write_stat() + index_dir_close, + index_dir_free_fd +}; + +#if 0 +static struct fd_ops sIndexOps = { + NULL, // read() + NULL, // write() + NULL, // seek() + NULL, // ioctl() + NULL, // select() + NULL, // deselect() + NULL, // dir_read() + NULL, // dir_rewind() + index_read_stat, // read_stat() + NULL, // write_stat() + NULL, // dir_close() + NULL // free_fd() +}; +#endif + +static struct fd_ops sQueryOps = { + NULL, // read() + NULL, // write() + NULL, // seek() + NULL, // ioctl() + NULL, // select() + NULL, // deselect() + query_read, + query_rewind, + NULL, // read_stat() + NULL, // write_stat() + query_close, + query_free_fd +}; + + +// VNodePutter +class VNodePutter { +public: + VNodePutter(struct vnode *vnode = NULL) : fVNode(vnode) {} + + ~VNodePutter() + { + Put(); + } + + void SetTo(struct vnode *vnode) + { + Put(); + fVNode = vnode; + } + + void Put() + { + if (fVNode) { + put_vnode(fVNode); + fVNode = NULL; + } + } + + struct vnode *Detach() + { + struct vnode *vnode = fVNode; + fVNode = NULL; + return vnode; + } + +private: + struct vnode *fVNode; +}; + + +static int +mount_compare(void *_m, const void *_key) +{ + struct fs_mount *mount = (fs_mount *)_m; + const fssh_mount_id *id = (fssh_mount_id *)_key; + + if (mount->id == *id) + return 0; + + return -1; +} + + +static uint32_t +mount_hash(void *_m, const void *_key, uint32_t range) +{ + struct fs_mount *mount = (fs_mount *)_m; + const fssh_mount_id *id = (fssh_mount_id *)_key; + + if (mount) + return mount->id % range; + + return (uint32_t)*id % range; +} + + +/** Finds the mounted device (the fs_mount structure) with the given ID. + * Note, you must hold the gMountMutex lock when you call this function. + */ + +static struct fs_mount * +find_mount(fssh_mount_id id) +{ + ASSERT_LOCKED_MUTEX(&sMountMutex); + + return (fs_mount *)hash_lookup(sMountsTable, (void *)&id); +} + + +static fssh_status_t +get_mount(fssh_mount_id id, struct fs_mount **_mount) +{ + struct fs_mount *mount; + fssh_status_t status; + + mutex_lock(&sMountMutex); + + mount = find_mount(id); + if (mount) { + // ToDo: the volume is locked (against removal) by locking + // its root node - investigate if that's a good idea + if (mount->root_vnode) + inc_vnode_ref_count(mount->root_vnode); + else { + // might have been called during a mount operation in which + // case the root node may still be NULL + mount = NULL; + } + } else + status = FSSH_B_BAD_VALUE; + + mutex_unlock(&sMountMutex); + + if (mount == NULL) + return FSSH_B_BUSY; + + *_mount = mount; + return FSSH_B_OK; +} + + +static void +put_mount(struct fs_mount *mount) +{ + if (mount) + put_vnode(mount->root_vnode); +} + + +static fssh_status_t +put_file_system(fssh_file_system_module_info *fs) +{ +// return put_module(fs->info.name); + return FSSH_B_ERROR; +} + + +/** Tries to open the specified file system module. + * Accepts a file system name of the form "bfs" or "file_systems/bfs/v1". + * Returns a pointer to file system module interface, or NULL if it + * could not open the module. + */ + +static fssh_file_system_module_info * +get_file_system(const char *fsName) +{ +#if 0 + char name[FSSH_B_FILE_NAME_LENGTH]; + if (strncmp(fsName, "file_systems/", strlen("file_systems/"))) { + // construct module name if we didn't get one + // (we currently support only one API) + snprintf(name, sizeof(name), "file_systems/%s/v1", fsName); + fsName = NULL; + } + + fssh_file_system_module_info *info; + if (get_module(fsName ? fsName : name, (module_info **)&info) != FSSH_B_OK) + return NULL; + + return info; +#endif // 0 +return NULL; +} + + +/** Accepts a file system name of the form "bfs" or "file_systems/bfs/v1" + * and returns a compatible fs_info.fsh_name name ("bfs" in both cases). + * The name is allocated for you, and you have to free() it when you're + * done with it. + * Returns NULL if the required memory is no available. + */ + +static char * +get_file_system_name(const char *fsName) +{ + const fssh_size_t length = fssh_strlen("file_systems/"); + + if (fssh_strncmp(fsName, "file_systems/", length)) { + // the name already seems to be the module's file name + return fssh_strdup(fsName); + } + + fsName += length; + const char *end = fssh_strchr(fsName, '/'); + if (end == NULL) { + // this doesn't seem to be a valid name, but well... + return fssh_strdup(fsName); + } + + // cut off the trailing /v1 + + char *name = (char *)malloc(end + 1 - fsName); + if (name == NULL) + return NULL; + + fssh_strlcpy(name, fsName, end + 1 - fsName); + return name; +} + + +static int +vnode_compare(void *_vnode, const void *_key) +{ + struct vnode *vnode = (struct vnode *)_vnode; + const struct vnode_hash_key *key = (vnode_hash_key *)_key; + + if (vnode->device == key->device && vnode->id == key->vnode) + return 0; + + return -1; +} + + +static uint32_t +vnode_hash(void *_vnode, const void *_key, uint32_t range) +{ + struct vnode *vnode = (struct vnode *)_vnode; + const struct vnode_hash_key *key = (vnode_hash_key *)_key; + +#define VHASH(mountid, vnodeid) (((uint32_t)((vnodeid) >> 32) + (uint32_t)(vnodeid)) ^ (uint32_t)(mountid)) + + if (vnode != NULL) + return VHASH(vnode->device, vnode->id) % range; + + return VHASH(key->device, key->vnode) % range; + +#undef VHASH +} + + +static void +add_vnode_to_mount_list(struct vnode *vnode, struct fs_mount *mount) +{ + recursive_lock_lock(&mount->rlock); + + list_add_link_to_head(&mount->vnodes, &vnode->mount_link); + + recursive_lock_unlock(&mount->rlock); +} + + +static void +remove_vnode_from_mount_list(struct vnode *vnode, struct fs_mount *mount) +{ + recursive_lock_lock(&mount->rlock); + + list_remove_link(&vnode->mount_link); + vnode->mount_link.next = vnode->mount_link.prev = NULL; + + recursive_lock_unlock(&mount->rlock); +} + + +static fssh_status_t +create_new_vnode(struct vnode **_vnode, fssh_mount_id mountID, fssh_vnode_id vnodeID) +{ + FUNCTION(("create_new_vnode()\n")); + + struct vnode *vnode = (struct vnode *)malloc(sizeof(struct vnode)); + if (vnode == NULL) + return FSSH_B_NO_MEMORY; + + // initialize basic values + fssh_memset(vnode, 0, sizeof(struct vnode)); + vnode->device = mountID; + vnode->id = vnodeID; + + // add the vnode to the mount structure + mutex_lock(&sMountMutex); + vnode->mount = find_mount(mountID); + if (!vnode->mount || vnode->mount->unmounting) { + mutex_unlock(&sMountMutex); + free(vnode); + return FSSH_B_ENTRY_NOT_FOUND; + } + + hash_insert(sVnodeTable, vnode); + add_vnode_to_mount_list(vnode, vnode->mount); + + mutex_unlock(&sMountMutex); + + vnode->ref_count = 1; + *_vnode = vnode; + + return FSSH_B_OK; +} + + +/** Frees the vnode and all resources it has acquired, and removes + * it from the vnode hash as well as from its mount structure. + * Will also make sure that any cache modifications are written back. + */ + +static void +free_vnode(struct vnode *vnode, bool reenter) +{ + ASSERT(vnode->ref_count == 0 && vnode->busy); + + // write back any changes in this vnode's cache -- but only + // if the vnode won't be deleted, in which case the changes + // will be discarded + + if (!vnode->remove && FS_CALL(vnode, fsync) != NULL) + FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node); + + if (!vnode->unpublished) { + if (vnode->remove) + FS_CALL(vnode, remove_vnode)(vnode->mount->cookie, vnode->private_node, reenter); + else + FS_CALL(vnode, put_vnode)(vnode->mount->cookie, vnode->private_node, reenter); + } + + // The file system has removed the resources of the vnode now, so we can + // make it available again (and remove the busy vnode from the hash) + mutex_lock(&sVnodeMutex); + hash_remove(sVnodeTable, vnode); + mutex_unlock(&sVnodeMutex); + + remove_vnode_from_mount_list(vnode, vnode->mount); + + free(vnode); +} + + +/** \brief Decrements the reference counter of the given vnode and deletes it, + * if the counter dropped to 0. + * + * The caller must, of course, own a reference to the vnode to call this + * function. + * The caller must not hold the sVnodeMutex or the sMountMutex. + * + * \param vnode the vnode. + * \param reenter \c true, if this function is called (indirectly) from within + * a file system. + * \return \c FSSH_B_OK, if everything went fine, an error code otherwise. + */ + +static fssh_status_t +dec_vnode_ref_count(struct vnode *vnode, bool reenter) +{ + mutex_lock(&sVnodeMutex); + + int32_t oldRefCount = fssh_atomic_add(&vnode->ref_count, -1); + + TRACE(("dec_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count)); + + if (oldRefCount == 1) { + if (vnode->busy) + fssh_panic("dec_vnode_ref_count: called on busy vnode %p\n", vnode); + + bool freeNode = false; + + // Just insert the vnode into an unused list if we don't need + // to delete it + if (vnode->remove) { + vnode->busy = true; + freeNode = true; + } else { + list_add_item(&sUnusedVnodeList, vnode); + if (++sUnusedVnodes > kMaxUnusedVnodes) { + // there are too many unused vnodes so we free the oldest one + // ToDo: evaluate this mechanism + vnode = (struct vnode *)list_remove_head_item(&sUnusedVnodeList); + vnode->busy = true; + freeNode = true; + sUnusedVnodes--; + } + } + + mutex_unlock(&sVnodeMutex); + + if (freeNode) + free_vnode(vnode, reenter); + } else + mutex_unlock(&sVnodeMutex); + + return FSSH_B_OK; +} + + +/** \brief Increments the reference counter of the given vnode. + * + * The caller must either already have a reference to the vnode or hold + * the sVnodeMutex. + * + * \param vnode the vnode. + */ + +static void +inc_vnode_ref_count(struct vnode *vnode) +{ + fssh_atomic_add(&vnode->ref_count, 1); + TRACE(("inc_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count)); +} + + +/** \brief Looks up a vnode by mount and node ID in the sVnodeTable. + * + * The caller must hold the sVnodeMutex. + * + * \param mountID the mount ID. + * \param vnodeID the node ID. + * + * \return The vnode structure, if it was found in the hash table, \c NULL + * otherwise. + */ + +static struct vnode * +lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) +{ + struct vnode_hash_key key; + + key.device = mountID; + key.vnode = vnodeID; + + return (vnode *)hash_lookup(sVnodeTable, &key); +} + + +/** \brief Retrieves a vnode for a given mount ID, node ID pair. + * + * If the node is not yet in memory, it will be loaded. + * + * The caller must not hold the sVnodeMutex or the sMountMutex. + * + * \param mountID the mount ID. + * \param vnodeID the node ID. + * \param _vnode Pointer to a vnode* variable into which the pointer to the + * retrieved vnode structure shall be written. + * \param reenter \c true, if this function is called (indirectly) from within + * a file system. + * \return \c FSSH_B_OK, if everything when fine, an error code otherwise. + */ + +static fssh_status_t +get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, struct vnode **_vnode, int reenter) +{ + FUNCTION(("get_vnode: mountid %ld vnid 0x%Lx %p\n", mountID, vnodeID, _vnode)); + + mutex_lock(&sVnodeMutex); + + int32_t tries = 300; + // try for 3 secs +restart: + struct vnode *vnode = lookup_vnode(mountID, vnodeID); + if (vnode && vnode->busy) { + mutex_unlock(&sVnodeMutex); + if (--tries < 0) { + // vnode doesn't seem to become unbusy + fssh_panic("vnode %d:%lld is not becoming unbusy!\n", (int)mountID, vnodeID); + return FSSH_B_BUSY; + } + fssh_snooze(10000); // 10 ms + mutex_lock(&sVnodeMutex); + goto restart; + } + + TRACE(("get_vnode: tried to lookup vnode, got %p\n", vnode)); + + fssh_status_t status; + + if (vnode) { + if (vnode->ref_count == 0) { + // this vnode has been unused before + list_remove_item(&sUnusedVnodeList, vnode); + sUnusedVnodes--; + } + inc_vnode_ref_count(vnode); + } else { + // we need to create a new vnode and read it in + status = create_new_vnode(&vnode, mountID, vnodeID); + if (status < FSSH_B_OK) + goto err; + + vnode->busy = true; + mutex_unlock(&sVnodeMutex); + + status = FS_CALL(vnode, get_vnode)(vnode->mount->cookie, vnodeID, &vnode->private_node, reenter); + if (status == FSSH_B_OK && vnode->private_node == NULL) + status = FSSH_B_BAD_VALUE; + + mutex_lock(&sVnodeMutex); + + if (status < FSSH_B_OK) + goto err1; + + vnode->busy = false; + } + + mutex_unlock(&sVnodeMutex); + + TRACE(("get_vnode: returning %p\n", vnode)); + + *_vnode = vnode; + return FSSH_B_OK; + +err1: + hash_remove(sVnodeTable, vnode); + remove_vnode_from_mount_list(vnode, vnode->mount); +err: + mutex_unlock(&sVnodeMutex); + if (vnode) + free(vnode); + + return status; +} + + +/** \brief Decrements the reference counter of the given vnode and deletes it, + * if the counter dropped to 0. + * + * The caller must, of course, own a reference to the vnode to call this + * function. + * The caller must not hold the sVnodeMutex or the sMountMutex. + * + * \param vnode the vnode. + */ + +static inline void +put_vnode(struct vnode *vnode) +{ + dec_vnode_ref_count(vnode, false); +} + + +/** Disconnects all file descriptors that are associated with the + * \a vnodeToDisconnect, or if this is NULL, all vnodes of the specified + * \a mount object. + * + * Note, after you've called this function, there might still be ongoing + * accesses - they won't be interrupted if they already happened before. + * However, any subsequent access will fail. + * + * This is not a cheap function and should be used with care and rarely. + * TODO: there is currently no means to stop a blocking read/write! + */ + +void +disconnect_mount_or_vnode_fds(struct fs_mount *mount, + struct vnode *vnodeToDisconnect) +{ +} + + +/** \brief Resolves a mount point vnode to the volume root vnode it is covered + * by. + * + * Given an arbitrary vnode, the function checks, whether the node is covered + * by the root of a volume. If it is the function obtains a reference to the + * volume root node and returns it. + * + * \param vnode The vnode in question. + * \return The volume root vnode the vnode cover is covered by, if it is + * indeed a mount point, or \c NULL otherwise. + */ + +static struct vnode * +resolve_mount_point_to_volume_root(struct vnode *vnode) +{ + if (!vnode) + return NULL; + + struct vnode *volumeRoot = NULL; + + mutex_lock(&sVnodeCoveredByMutex); + if (vnode->covered_by) { + volumeRoot = vnode->covered_by; + inc_vnode_ref_count(volumeRoot); + } + mutex_unlock(&sVnodeCoveredByMutex); + + return volumeRoot; +} + + +/** \brief Resolves a mount point vnode to the volume root vnode it is covered + * by. + * + * Given an arbitrary vnode (identified by mount and node ID), the function + * checks, whether the node is covered by the root of a volume. If it is the + * function returns the mount and node ID of the volume root node. Otherwise + * it simply returns the supplied mount and node ID. + * + * In case of error (e.g. the supplied node could not be found) the variables + * for storing the resolved mount and node ID remain untouched and an error + * code is returned. + * + * \param mountID The mount ID of the vnode in question. + * \param nodeID The node ID of the vnode in question. + * \param resolvedMountID Pointer to storage for the resolved mount ID. + * \param resolvedNodeID Pointer to storage for the resolved node ID. + * \return + * - \c FSSH_B_OK, if everything went fine, + * - another error code, if something went wrong. + */ + +fssh_status_t +resolve_mount_point_to_volume_root(fssh_mount_id mountID, fssh_vnode_id nodeID, + fssh_mount_id *resolvedMountID, fssh_vnode_id *resolvedNodeID) +{ + // get the node + struct vnode *node; + fssh_status_t error = get_vnode(mountID, nodeID, &node, false); + if (error != FSSH_B_OK) + return error; + + // resolve the node + struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node); + if (resolvedNode) { + put_vnode(node); + node = resolvedNode; + } + + // set the return values + *resolvedMountID = node->device; + *resolvedNodeID = node->id; + + put_vnode(node); + + return FSSH_B_OK; +} + + +/** \brief Resolves a volume root vnode to the underlying mount point vnode. + * + * Given an arbitrary vnode, the function checks, whether the node is the + * root of a volume. If it is (and if it is not "/"), the function obtains + * a reference to the underlying mount point node and returns it. + * + * \param vnode The vnode in question (caller must have a reference). + * \return The mount point vnode the vnode covers, if it is indeed a volume + * root and not "/", or \c NULL otherwise. + */ + +static struct vnode * +resolve_volume_root_to_mount_point(struct vnode *vnode) +{ + if (!vnode) + return NULL; + + struct vnode *mountPoint = NULL; + + struct fs_mount *mount = vnode->mount; + if (vnode == mount->root_vnode && mount->covers_vnode) { + mountPoint = mount->covers_vnode; + inc_vnode_ref_count(mountPoint); + } + + return mountPoint; +} + + +/** \brief Gets the directory path and leaf name for a given path. + * + * The supplied \a path is transformed to refer to the directory part of + * the entry identified by the original path, and into the buffer \a filename + * the leaf name of the original entry is written. + * Neither the returned path nor the leaf name can be expected to be + * canonical. + * + * \param path The path to be analyzed. Must be able to store at least one + * additional character. + * \param filename The buffer into which the leaf name will be written. + * Must be of size FSSH_B_FILE_NAME_LENGTH at least. + * \return \c FSSH_B_OK, if everything went fine, \c FSSH_B_NAME_TOO_LONG, if the leaf + * name is longer than \c FSSH_B_FILE_NAME_LENGTH. + */ + +static fssh_status_t +get_dir_path_and_leaf(char *path, char *filename) +{ + char *p = fssh_strrchr(path, '/'); + // '/' are not allowed in file names! + + FUNCTION(("get_dir_path_and_leaf(path = %s)\n", path)); + + if (!p) { + // this path is single segment with no '/' in it + // ex. "foo" + if (fssh_strlcpy(filename, path, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH) + return FSSH_B_NAME_TOO_LONG; + fssh_strcpy(path, "."); + } else { + p++; + if (*p == '\0') { + // special case: the path ends in '/' + fssh_strcpy(filename, "."); + } else { + // normal leaf: replace the leaf portion of the path with a '.' + if (fssh_strlcpy(filename, p, FSSH_B_FILE_NAME_LENGTH) + >= FSSH_B_FILE_NAME_LENGTH) { + return FSSH_B_NAME_TOO_LONG; + } + } + p[0] = '.'; + p[1] = '\0'; + } + return FSSH_B_OK; +} + + +static fssh_status_t +entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, struct vnode **_vnode) +{ + char clonedName[FSSH_B_FILE_NAME_LENGTH + 1]; + if (fssh_strlcpy(clonedName, name, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH) + return FSSH_B_NAME_TOO_LONG; + + // get the directory vnode and let vnode_path_to_vnode() do the rest + struct vnode *directory; + + fssh_status_t status = get_vnode(mountID, directoryID, &directory, false); + if (status < 0) + return status; + + return vnode_path_to_vnode(directory, clonedName, false, 0, _vnode, NULL, NULL); +} + + +/** Returns the vnode for the relative path starting at the specified \a vnode. + * \a path must not be NULL. + * If it returns successfully, \a path contains the name of the last path + * component. + * Note, this reduces the ref_count of the starting \a vnode, no matter if + * it is successful or not! + */ + +static fssh_status_t +vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink, + int count, struct vnode **_vnode, fssh_vnode_id *_parentID, int *_type) +{ + fssh_status_t status = 0; + fssh_vnode_id lastParentID = vnode->id; + int type = 0; + + FUNCTION(("vnode_path_to_vnode(vnode = %p, path = %s)\n", vnode, path)); + + if (path == NULL) { + put_vnode(vnode); + return FSSH_B_BAD_VALUE; + } + + while (true) { + struct vnode *nextVnode; + fssh_vnode_id vnodeID; + char *nextPath; + + TRACE(("vnode_path_to_vnode: top of loop. p = %p, p = '%s'\n", path, path)); + + // done? + if (path[0] == '\0') + break; + + // walk to find the next path component ("path" will point to a single + // path component), and filter out multiple slashes + for (nextPath = path + 1; *nextPath != '\0' && *nextPath != '/'; nextPath++); + + if (*nextPath == '/') { + *nextPath = '\0'; + do + nextPath++; + while (*nextPath == '/'); + } + + // See if the '..' is at the root of a mount and move to the covered + // vnode so we pass the '..' path to the underlying filesystem + if (!fssh_strcmp("..", path) + && vnode->mount->root_vnode == vnode + && vnode->mount->covers_vnode) { + nextVnode = vnode->mount->covers_vnode; + inc_vnode_ref_count(nextVnode); + put_vnode(vnode); + vnode = nextVnode; + } + + // Check if we have the right to search the current directory vnode. + // If a file system doesn't have the access() function, we assume that + // searching a directory is always allowed + if (FS_CALL(vnode, access)) + status = FS_CALL(vnode, access)(vnode->mount->cookie, vnode->private_node, FSSH_X_OK); + + // Tell the filesystem to get the vnode of this path component (if we got the + // permission from the call above) + if (status >= FSSH_B_OK) + status = FS_CALL(vnode, lookup)(vnode->mount->cookie, vnode->private_node, path, &vnodeID, &type); + + if (status < FSSH_B_OK) { + put_vnode(vnode); + return status; + } + + // Lookup the vnode, the call to fs_lookup should have caused a get_vnode to be called + // from inside the filesystem, thus the vnode would have to be in the list and it's + // ref count incremented at this point + mutex_lock(&sVnodeMutex); + nextVnode = lookup_vnode(vnode->device, vnodeID); + mutex_unlock(&sVnodeMutex); + + if (!nextVnode) { + // pretty screwed up here - the file system found the vnode, but the hash + // lookup failed, so our internal structures are messed up + fssh_panic("vnode_path_to_vnode: could not lookup vnode (mountid 0x%x vnid 0x%llx)\n", + (int)vnode->device, vnodeID); + put_vnode(vnode); + return FSSH_B_ENTRY_NOT_FOUND; + } + + // If the new node is a symbolic link, resolve it (if we've been told to do it) + if (FSSH_S_ISLNK(type) && !(!traverseLeafLink && nextPath[0] == '\0')) { + fssh_size_t bufferSize; + char *buffer; + + TRACE(("traverse link\n")); + + // it's not exactly nice style using goto in this way, but hey, it works :-/ + if (count + 1 > FSSH_B_MAX_SYMLINKS) { + status = FSSH_B_LINK_LIMIT; + goto resolve_link_error; + } + + buffer = (char *)malloc(bufferSize = FSSH_B_PATH_NAME_LENGTH); + if (buffer == NULL) { + status = FSSH_B_NO_MEMORY; + goto resolve_link_error; + } + + if (FS_CALL(nextVnode, read_symlink) != NULL) { + status = FS_CALL(nextVnode, read_symlink)( + nextVnode->mount->cookie, nextVnode->private_node, buffer, + &bufferSize); + } else + status = FSSH_B_BAD_VALUE; + + if (status < FSSH_B_OK) { + free(buffer); + + resolve_link_error: + put_vnode(vnode); + put_vnode(nextVnode); + + return status; + } + put_vnode(nextVnode); + + // Check if we start from the root directory or the current + // directory ("vnode" still points to that one). + // Cut off all leading slashes if it's the root directory + path = buffer; + if (path[0] == '/') { + // we don't need the old directory anymore + put_vnode(vnode); + + while (*++path == '/') + ; + vnode = sRoot; + inc_vnode_ref_count(vnode); + } + inc_vnode_ref_count(vnode); + // balance the next recursion - we will decrement the ref_count + // of the vnode, no matter if we succeeded or not + + status = vnode_path_to_vnode(vnode, path, traverseLeafLink, count + 1, + &nextVnode, &lastParentID, _type); + + free(buffer); + + if (status < FSSH_B_OK) { + put_vnode(vnode); + return status; + } + } else + lastParentID = vnode->id; + + // decrease the ref count on the old dir we just looked up into + put_vnode(vnode); + + path = nextPath; + vnode = nextVnode; + + // see if we hit a mount point + struct vnode *mountPoint = resolve_mount_point_to_volume_root(vnode); + if (mountPoint) { + put_vnode(vnode); + vnode = mountPoint; + } + } + + *_vnode = vnode; + if (_type) + *_type = type; + if (_parentID) + *_parentID = lastParentID; + + return FSSH_B_OK; +} + + +static fssh_status_t +path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode, + fssh_vnode_id *_parentID, bool kernel) +{ + struct vnode *start = NULL; + + FUNCTION(("path_to_vnode(path = \"%s\")\n", path)); + + if (!path) + return FSSH_B_BAD_VALUE; + + // figure out if we need to start at root or at cwd + if (*path == '/') { + if (sRoot == NULL) { + // we're a bit early, aren't we? + return FSSH_B_ERROR; + } + + while (*++path == '/') + ; + start = sRoot; + inc_vnode_ref_count(start); + } else { + struct io_context *context = get_current_io_context(kernel); + + mutex_lock(&context->io_mutex); + start = context->cwd; + if (start != NULL) + inc_vnode_ref_count(start); + mutex_unlock(&context->io_mutex); + + if (start == NULL) + return FSSH_B_ERROR; + } + + return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID, NULL); +} + + +/** Returns the vnode in the next to last segment of the path, and returns + * the last portion in filename. + * The path buffer must be able to store at least one additional character. + */ + +static fssh_status_t +path_to_dir_vnode(char *path, struct vnode **_vnode, char *filename, bool kernel) +{ + fssh_status_t status = get_dir_path_and_leaf(path, filename); + if (status != FSSH_B_OK) + return status; + + return path_to_vnode(path, true, _vnode, NULL, kernel); +} + + +/** \brief Retrieves the directory vnode and the leaf name of an entry referred + * to by a FD + path pair. + * + * \a path must be given in either case. \a fd might be omitted, in which + * case \a path is either an absolute path or one relative to the current + * directory. If both a supplied and \a path is relative it is reckoned off + * of the directory referred to by \a fd. If \a path is absolute \a fd is + * ignored. + * + * The caller has the responsibility to call put_vnode() on the returned + * directory vnode. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. Must not be \c NULL. The buffer + * is modified by this function. It must have at least room for a + * string one character longer than the path it contains. + * \param _vnode A pointer to a variable the directory vnode shall be written + * into. + * \param filename A buffer of size FSSH_B_FILE_NAME_LENGTH or larger into which + * the leaf name of the specified entry will be written. + * \param kernel \c true, if invoked from inside the kernel, \c false if + * invoked from userland. + * \return \c FSSH_B_OK, if everything went fine, another error code otherwise. + */ + +static fssh_status_t +fd_and_path_to_dir_vnode(int fd, char *path, struct vnode **_vnode, + char *filename, bool kernel) +{ + if (!path) + return FSSH_B_BAD_VALUE; + if (fd < 0) + return path_to_dir_vnode(path, _vnode, filename, kernel); + + fssh_status_t status = get_dir_path_and_leaf(path, filename); + if (status != FSSH_B_OK) + return status; + + return fd_and_path_to_vnode(fd, path, true, _vnode, NULL, kernel); +} + + +/** Returns a vnode's name in the d_name field of a supplied dirent buffer. + */ + +static fssh_status_t +get_vnode_name(struct vnode *vnode, struct vnode *parent, + struct fssh_dirent *buffer, fssh_size_t bufferSize) +{ + if (bufferSize < sizeof(struct fssh_dirent)) + return FSSH_B_BAD_VALUE; + + // See if vnode is the root of a mount and move to the covered + // vnode so we get the underlying file system + VNodePutter vnodePutter; + if (vnode->mount->root_vnode == vnode && vnode->mount->covers_vnode != NULL) { + vnode = vnode->mount->covers_vnode; + inc_vnode_ref_count(vnode); + vnodePutter.SetTo(vnode); + } + + if (FS_CALL(vnode, get_vnode_name)) { + // The FS supports getting the name of a vnode. + return FS_CALL(vnode, get_vnode_name)(vnode->mount->cookie, + vnode->private_node, buffer->d_name, + (char*)buffer + bufferSize - buffer->d_name); + } + + // The FS doesn't support getting the name of a vnode. So we search the + // parent directory for the vnode, if the caller let us. + + if (parent == NULL) + return FSSH_EOPNOTSUPP; + + fssh_fs_cookie cookie; + + fssh_status_t status = FS_CALL(parent, open_dir)(parent->mount->cookie, + parent->private_node, &cookie); + if (status >= FSSH_B_OK) { + while (true) { + uint32_t num = 1; + status = dir_read(parent, cookie, buffer, bufferSize, &num); + if (status < FSSH_B_OK) + break; + if (num == 0) { + status = FSSH_B_ENTRY_NOT_FOUND; + break; + } + + if (vnode->id == buffer->d_ino) { + // found correct entry! + break; + } + } + + FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, + cookie); + FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, + vnode->private_node, cookie); + } + return status; +} + + +static fssh_status_t +get_vnode_name(struct vnode *vnode, struct vnode *parent, char *name, + fssh_size_t nameSize) +{ + char buffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; + struct fssh_dirent *dirent = (struct fssh_dirent *)buffer; + + fssh_status_t status = get_vnode_name(vnode, parent, buffer, sizeof(buffer)); + if (status != FSSH_B_OK) + return status; + + if (fssh_strlcpy(name, dirent->d_name, nameSize) >= nameSize) + return FSSH_B_BUFFER_OVERFLOW; + + return FSSH_B_OK; +} + + +/** Gets the full path to a given directory vnode. + * It uses the fs_get_vnode_name() call to get the name of a vnode; if a + * file system doesn't support this call, it will fall back to iterating + * through the parent directory to get the name of the child. + * + * To protect against circular loops, it supports a maximum tree depth + * of 256 levels. + * + * Note that the path may not be correct the time this function returns! + * It doesn't use any locking to prevent returning the correct path, as + * paths aren't safe anyway: the path to a file can change at any time. + * + * It might be a good idea, though, to check if the returned path exists + * in the calling function (it's not done here because of efficiency) + */ + +static fssh_status_t +dir_vnode_to_path(struct vnode *vnode, char *buffer, fssh_size_t bufferSize) +{ + FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize)); + + if (vnode == NULL || buffer == NULL) + return FSSH_B_BAD_VALUE; + + /* this implementation is currently bound to FSSH_B_PATH_NAME_LENGTH */ + KPath pathBuffer; + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + char *path = pathBuffer.LockBuffer(); + int32_t insert = pathBuffer.BufferSize(); + int32_t maxLevel = 256; + int32_t length; + fssh_status_t status; + + // we don't use get_vnode() here because this call is more + // efficient and does all we need from get_vnode() + inc_vnode_ref_count(vnode); + + // resolve a volume root to its mount point + struct vnode *mountPoint = resolve_volume_root_to_mount_point(vnode); + if (mountPoint) { + put_vnode(vnode); + vnode = mountPoint; + } + + path[--insert] = '\0'; + + while (true) { + // the name buffer is also used for fs_read_dir() + char nameBuffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; + char *name = &((struct fssh_dirent *)nameBuffer)->d_name[0]; + struct vnode *parentVnode; + fssh_vnode_id parentID; + int type; + + // lookup the parent vnode + status = FS_CALL(vnode, lookup)(vnode->mount->cookie, vnode->private_node, "..", + &parentID, &type); + if (status < FSSH_B_OK) + goto out; + + mutex_lock(&sVnodeMutex); + parentVnode = lookup_vnode(vnode->device, parentID); + mutex_unlock(&sVnodeMutex); + + if (parentVnode == NULL) { + fssh_panic("dir_vnode_to_path: could not lookup vnode (mountid 0x%x vnid 0x%llx)\n", + (int)vnode->device, parentID); + status = FSSH_B_ENTRY_NOT_FOUND; + goto out; + } + + // get the node's name + status = get_vnode_name(vnode, parentVnode, + (struct fssh_dirent*)nameBuffer, sizeof(nameBuffer)); + + // resolve a volume root to its mount point + mountPoint = resolve_volume_root_to_mount_point(parentVnode); + if (mountPoint) { + put_vnode(parentVnode); + parentVnode = mountPoint; + parentID = parentVnode->id; + } + + bool hitRoot = (parentVnode == vnode); + + // release the current vnode, we only need its parent from now on + put_vnode(vnode); + vnode = parentVnode; + + if (status < FSSH_B_OK) + goto out; + + if (hitRoot) { + // we have reached "/", which means we have constructed the full + // path + break; + } + + // ToDo: add an explicit check for loops in about 10 levels to do + // real loop detection + + // don't go deeper as 'maxLevel' to prevent circular loops + if (maxLevel-- < 0) { + status = FSSH_ELOOP; + goto out; + } + + // add the name in front of the current path + name[FSSH_B_FILE_NAME_LENGTH - 1] = '\0'; + length = fssh_strlen(name); + insert -= length; + if (insert <= 0) { + status = FSSH_ENOBUFS; + goto out; + } + fssh_memcpy(path + insert, name, length); + path[--insert] = '/'; + } + + // the root dir will result in an empty path: fix it + if (path[insert] == '\0') + path[--insert] = '/'; + + TRACE((" path is: %s\n", path + insert)); + + // copy the path to the output buffer + length = pathBuffer.BufferSize() - insert; + if (length <= (int)bufferSize) + fssh_memcpy(buffer, path + insert, length); + else + status = FSSH_ENOBUFS; + +out: + put_vnode(vnode); + return status; +} + + +/** Checks the length of every path component, and adds a '.' + * if the path ends in a slash. + * The given path buffer must be able to store at least one + * additional character. + */ + +static fssh_status_t +check_path(char *to) +{ + int32_t length = 0; + + // check length of every path component + + while (*to) { + char *begin; + if (*to == '/') + to++, length++; + + begin = to; + while (*to != '/' && *to) + to++, length++; + + if (to - begin > FSSH_B_FILE_NAME_LENGTH) + return FSSH_B_NAME_TOO_LONG; + } + + if (length == 0) + return FSSH_B_ENTRY_NOT_FOUND; + + // complete path if there is a slash at the end + + if (*(to - 1) == '/') { + if (length > FSSH_B_PATH_NAME_LENGTH - 2) + return FSSH_B_NAME_TOO_LONG; + + to[0] = '.'; + to[1] = '\0'; + } + + return FSSH_B_OK; +} + + +static struct file_descriptor * +get_fd_and_vnode(int fd, struct vnode **_vnode, bool kernel) +{ + struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd); + if (descriptor == NULL) + return NULL; + + if (fd_vnode(descriptor) == NULL) { + put_fd(descriptor); + return NULL; + } + + // ToDo: when we can close a file descriptor at any point, investigate + // if this is still valid to do (accessing the vnode without ref_count + // or locking) + *_vnode = descriptor->u.vnode; + return descriptor; +} + + +static struct vnode * +get_vnode_from_fd(int fd, bool kernel) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + + descriptor = get_fd(get_current_io_context(kernel), fd); + if (descriptor == NULL) + return NULL; + + vnode = fd_vnode(descriptor); + if (vnode != NULL) + inc_vnode_ref_count(vnode); + + put_fd(descriptor); + return vnode; +} + + +/** Gets the vnode from an FD + path combination. If \a fd is lower than zero, + * only the path will be considered. In this case, the \a path must not be + * NULL. + * If \a fd is a valid file descriptor, \a path may be NULL for directories, + * and should be NULL for files. + */ + +static fssh_status_t +fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink, + struct vnode **_vnode, fssh_vnode_id *_parentID, bool kernel) +{ + if (fd < 0 && !path) + return FSSH_B_BAD_VALUE; + + if (fd < 0 || (path != NULL && path[0] == '/')) { + // no FD or absolute path + return path_to_vnode(path, traverseLeafLink, _vnode, _parentID, kernel); + } + + // FD only, or FD + relative path + struct vnode *vnode = get_vnode_from_fd(fd, kernel); + if (!vnode) + return FSSH_B_FILE_ERROR; + + if (path != NULL) { + return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0, + _vnode, _parentID, NULL); + } + + // there is no relative path to take into account + + *_vnode = vnode; + if (_parentID) + *_parentID = -1; + + return FSSH_B_OK; +} + + +static int +get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode, + fssh_fs_cookie cookie, int openMode, bool kernel) +{ + struct file_descriptor *descriptor; + int fd; + + // if the vnode is locked, we don't allow creating a new file descriptor for it + if (vnode && vnode->mandatory_locked_by != NULL) + return FSSH_B_BUSY; + + descriptor = alloc_fd(); + if (!descriptor) + return FSSH_B_NO_MEMORY; + + if (vnode) + descriptor->u.vnode = vnode; + else + descriptor->u.mount = mount; + descriptor->cookie = cookie; + + switch (type) { + // vnode types + case FDTYPE_FILE: + descriptor->ops = &sFileOps; + break; + case FDTYPE_DIR: + descriptor->ops = &sDirectoryOps; + break; + case FDTYPE_ATTR: + descriptor->ops = &sAttributeOps; + break; + case FDTYPE_ATTR_DIR: + descriptor->ops = &sAttributeDirectoryOps; + break; + + // mount types + case FDTYPE_INDEX_DIR: + descriptor->ops = &sIndexDirectoryOps; + break; + case FDTYPE_QUERY: + descriptor->ops = &sQueryOps; + break; + + default: + fssh_panic("get_new_fd() called with unknown type %d\n", type); + break; + } + descriptor->type = type; + descriptor->open_mode = openMode; + + fd = new_fd(get_current_io_context(kernel), descriptor); + if (fd < 0) { + free(descriptor); + return FSSH_B_NO_MORE_FDS; + } + + return fd; +} + + +// #pragma mark - public VFS API + + +extern "C" fssh_status_t +fssh_new_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, + fssh_fs_vnode privateNode) +{ + FUNCTION(("new_vnode(mountID = %ld, vnodeID = %Ld, node = %p)\n", + mountID, vnodeID, privateNode)); + + if (privateNode == NULL) + return FSSH_B_BAD_VALUE; + + mutex_lock(&sVnodeMutex); + + // file system integrity check: + // test if the vnode already exists and bail out if this is the case! + + // ToDo: the R5 implementation obviously checks for a different cookie + // and doesn't panic if they are equal + + struct vnode *vnode = lookup_vnode(mountID, vnodeID); + if (vnode != NULL) + fssh_panic("vnode %d:%lld already exists (node = %p, vnode->node = %p)!", (int)mountID, vnodeID, privateNode, vnode->private_node); + + fssh_status_t status = create_new_vnode(&vnode, mountID, vnodeID); + if (status == FSSH_B_OK) { + vnode->private_node = privateNode; + vnode->busy = true; + vnode->unpublished = true; + } + + TRACE(("returns: %s\n", strerror(status))); + + mutex_unlock(&sVnodeMutex); + return status; +} + + +extern "C" fssh_status_t +fssh_publish_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, + fssh_fs_vnode privateNode) +{ + FUNCTION(("publish_vnode()\n")); + + mutex_lock(&sVnodeMutex); + + struct vnode *vnode = lookup_vnode(mountID, vnodeID); + fssh_status_t status = FSSH_B_OK; + + if (vnode != NULL && vnode->busy && vnode->unpublished + && vnode->private_node == privateNode) { + vnode->busy = false; + vnode->unpublished = false; + } else if (vnode == NULL && privateNode != NULL) { + status = create_new_vnode(&vnode, mountID, vnodeID); + if (status == FSSH_B_OK) + vnode->private_node = privateNode; + } else + status = FSSH_B_BAD_VALUE; + + TRACE(("returns: %s\n", strerror(status))); + + mutex_unlock(&sVnodeMutex); + return status; +} + + +extern "C" fssh_status_t +fssh_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, + fssh_fs_vnode *_fsNode) +{ + struct vnode *vnode; + + fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, true); + if (status < FSSH_B_OK) + return status; + + *_fsNode = vnode->private_node; + return FSSH_B_OK; +} + + +extern "C" fssh_status_t +fssh_put_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) +{ + struct vnode *vnode; + + mutex_lock(&sVnodeMutex); + vnode = lookup_vnode(mountID, vnodeID); + mutex_unlock(&sVnodeMutex); + + if (vnode) + dec_vnode_ref_count(vnode, true); + + return FSSH_B_OK; +} + + +extern "C" fssh_status_t +fssh_remove_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) +{ + struct vnode *vnode; + bool remove = false; + + mutex_lock(&sVnodeMutex); + + vnode = lookup_vnode(mountID, vnodeID); + if (vnode != NULL) { + if (vnode->covered_by != NULL) { + // this vnode is in use + mutex_unlock(&sVnodeMutex); + return FSSH_B_BUSY; + } + + vnode->remove = true; + if (vnode->unpublished) { + // prepare the vnode for deletion + vnode->busy = true; + remove = true; + } + } + + mutex_unlock(&sVnodeMutex); + + if (remove) { + // if the vnode hasn't been published yet, we delete it here + fssh_atomic_add(&vnode->ref_count, -1); + free_vnode(vnode, true); + } + + return FSSH_B_OK; +} + + +extern "C" fssh_status_t +fssh_unremove_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) +{ + struct vnode *vnode; + + mutex_lock(&sVnodeMutex); + + vnode = lookup_vnode(mountID, vnodeID); + if (vnode) + vnode->remove = false; + + mutex_unlock(&sVnodeMutex); + return FSSH_B_OK; +} + + +extern "C" fssh_status_t +fssh_get_vnode_removed(fssh_mount_id mountID, fssh_vnode_id vnodeID, + bool* removed) +{ + mutex_lock(&sVnodeMutex); + + fssh_status_t result; + + if (struct vnode* vnode = lookup_vnode(mountID, vnodeID)) { + if (removed) + *removed = vnode->remove; + result = FSSH_B_OK; + } else + result = FSSH_B_BAD_VALUE; + + mutex_unlock(&sVnodeMutex); + return result; +} + + +// #pragma mark - private VFS API +// Functions the VFS exports for other parts of the kernel + + +/** Acquires another reference to the vnode that has to be released + * by calling vfs_put_vnode(). + */ + +void +vfs_acquire_vnode(void *_vnode) +{ + inc_vnode_ref_count((struct vnode *)_vnode); +} + + +/** This is currently called from file_cache_create() only. + * It's probably a temporary solution as long as devfs requires that + * fs_read_pages()/fs_write_pages() are called with the standard + * open cookie and not with a device cookie. + * If that's done differently, remove this call; it has no other + * purpose. + */ + +fssh_status_t +vfs_get_cookie_from_fd(int fd, void **_cookie) +{ + struct file_descriptor *descriptor; + + descriptor = get_fd(get_current_io_context(true), fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + *_cookie = descriptor->cookie; + return FSSH_B_OK; +} + + +int +vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode) +{ + *vnode = get_vnode_from_fd(fd, kernel); + + if (*vnode == NULL) + return FSSH_B_FILE_ERROR; + + return FSSH_B_NO_ERROR; +} + + +fssh_status_t +vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode) +{ + TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel)); + + KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + char *buffer = pathBuffer.LockBuffer(); + fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); + + struct vnode *vnode; + fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel); + if (status < FSSH_B_OK) + return status; + + *_vnode = vnode; + return FSSH_B_OK; +} + + +fssh_status_t +vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode) +{ + struct vnode *vnode; + + fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false); + if (status < FSSH_B_OK) + return status; + + *_vnode = vnode; + return FSSH_B_OK; +} + + +fssh_status_t +vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, + const char *name, void **_vnode) +{ + return entry_ref_to_vnode(mountID, directoryID, name, (struct vnode **)_vnode); +} + + +void +vfssh_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID) +{ + struct vnode *vnode = (struct vnode *)_vnode; + + *_mountID = vnode->device; + *_vnodeID = vnode->id; +} + + +/** Looks up a vnode with the given mount and vnode ID. + * Must only be used with "in-use" vnodes as it doesn't grab a reference + * to the node. + * It's currently only be used by file_cache_create(). + */ + +fssh_status_t +vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode) +{ + mutex_lock(&sVnodeMutex); + struct vnode *vnode = lookup_vnode(mountID, vnodeID); + mutex_unlock(&sVnodeMutex); + + if (vnode == NULL) + return FSSH_B_ERROR; + + *_vnode = vnode; + return FSSH_B_OK; +} + + +fssh_status_t +vfs_get_fs_node_from_path(fssh_mount_id mountID, const char *path, bool kernel, void **_node) +{ + TRACE(("vfs_get_fs_node_from_path(mountID = %ld, path = \"%s\", kernel %d)\n", + mountID, path, kernel)); + + KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + fs_mount *mount; + fssh_status_t status = get_mount(mountID, &mount); + if (status < FSSH_B_OK) + return status; + + char *buffer = pathBuffer.LockBuffer(); + fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); + + struct vnode *vnode = mount->root_vnode; + + if (buffer[0] == '/') + status = path_to_vnode(buffer, true, &vnode, NULL, true); + else { + inc_vnode_ref_count(vnode); + // vnode_path_to_vnode() releases a reference to the starting vnode + status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL, NULL); + } + + put_mount(mount); + + if (status < FSSH_B_OK) + return status; + + if (vnode->device != mountID) { + // wrong mount ID - must not gain access on foreign file system nodes + put_vnode(vnode); + return FSSH_B_BAD_VALUE; + } + + *_node = vnode->private_node; + return FSSH_B_OK; +} + + +/** Finds the full path to the file that contains the module \a moduleName, + * puts it into \a pathBuffer, and returns FSSH_B_OK for success. + * If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW, + * \c FSSH_B_ENTRY_NOT_FOUNT if no file could be found. + * \a pathBuffer is clobbered in any case and must not be relied on if this + * functions returns unsuccessfully. + */ + +fssh_status_t +vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer, + fssh_size_t bufferSize) +{ + struct vnode *dir, *file; + fssh_status_t status; + fssh_size_t length; + char *path; + + if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize) + return FSSH_B_BUFFER_OVERFLOW; + + status = path_to_vnode(pathBuffer, true, &dir, NULL, true); + if (status < FSSH_B_OK) + return status; + + // the path buffer had been clobbered by the above call + length = fssh_strlcpy(pathBuffer, basePath, bufferSize); + if (pathBuffer[length - 1] != '/') + pathBuffer[length++] = '/'; + + path = pathBuffer + length; + bufferSize -= length; + + while (moduleName) { + int type; + + char *nextPath = fssh_strchr(moduleName, '/'); + if (nextPath == NULL) + length = fssh_strlen(moduleName); + else { + length = nextPath - moduleName; + nextPath++; + } + + if (length + 1 >= bufferSize) { + status = FSSH_B_BUFFER_OVERFLOW; + goto err; + } + + fssh_memcpy(path, moduleName, length); + path[length] = '\0'; + moduleName = nextPath; + + status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL, &type); + if (status < FSSH_B_OK) { + // vnode_path_to_vnode() has already released the reference to dir + return status; + } + + if (FSSH_S_ISDIR(type)) { + // goto the next directory + path[length] = '/'; + path[length + 1] = '\0'; + path += length + 1; + bufferSize -= length + 1; + + dir = file; + } else if (FSSH_S_ISREG(type)) { + // it's a file so it should be what we've searched for + put_vnode(file); + + return FSSH_B_OK; + } else { + TRACE(("vfs_get_module_path(): something is strange here: %d...\n", type)); + status = FSSH_B_ERROR; + dir = file; + goto err; + } + } + + // if we got here, the moduleName just pointed to a directory, not to + // a real module - what should we do in this case? + status = FSSH_B_ENTRY_NOT_FOUND; + +err: + put_vnode(dir); + return status; +} + + +/** \brief Normalizes a given path. + * + * The path must refer to an existing or non-existing entry in an existing + * directory, that is chopping off the leaf component the remaining path must + * refer to an existing directory. + * + * The returned will be canonical in that it will be absolute, will not + * contain any "." or ".." components or duplicate occurrences of '/'s, + * and none of the directory components will by symbolic links. + * + * Any two paths referring to the same entry, will result in the same + * normalized path (well, that is pretty much the definition of `normalized', + * isn't it :-). + * + * \param path The path to be normalized. + * \param buffer The buffer into which the normalized path will be written. + * \param bufferSize The size of \a buffer. + * \param kernel \c true, if the IO context of the kernel shall be used, + * otherwise that of the team this thread belongs to. Only relevant, + * if the path is relative (to get the CWD). + * \return \c FSSH_B_OK if everything went fine, another error code otherwise. + */ + +fssh_status_t +vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize, + bool kernel) +{ + if (!path || !buffer || bufferSize < 1) + return FSSH_B_BAD_VALUE; + + TRACE(("vfs_normalize_path(`%s')\n", path)); + + // copy the supplied path to the stack, so it can be modified + KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); + if (mutablePathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + char *mutablePath = mutablePathBuffer.LockBuffer(); + if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH) + return FSSH_B_NAME_TOO_LONG; + + // get the dir vnode and the leaf name + struct vnode *dirNode; + char leaf[FSSH_B_FILE_NAME_LENGTH]; + fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel); + if (error != FSSH_B_OK) { + TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error))); + return error; + } + + // if the leaf is "." or "..", we directly get the correct directory + // vnode and ignore the leaf later + bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0); + if (isDir) + error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL, NULL); + if (error != FSSH_B_OK) { + TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n", + strerror(error))); + return error; + } + + // get the directory path + error = dir_vnode_to_path(dirNode, buffer, bufferSize); + put_vnode(dirNode); + if (error < FSSH_B_OK) { + TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error))); + return error; + } + + // append the leaf name + if (!isDir) { + // insert a directory separator only if this is not the file system root + if ((fssh_strcmp(buffer, "/") != 0 + && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize) + || fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) { + return FSSH_B_NAME_TOO_LONG; + } + } + + TRACE(("vfs_normalize_path() -> `%s'\n", buffer)); + return FSSH_B_OK; +} + + +void +vfs_put_vnode(void *_vnode) +{ + put_vnode((struct vnode *)_vnode); +} + + +fssh_status_t +vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID) +{ + // Get current working directory from io context + struct io_context *context = get_current_io_context(false); + fssh_status_t status = FSSH_B_OK; + + mutex_lock(&context->io_mutex); + + if (context->cwd != NULL) { + *_mountID = context->cwd->device; + *_vnodeID = context->cwd->id; + } else + status = FSSH_B_ERROR; + + mutex_unlock(&context->io_mutex); + return status; +} + + +fssh_status_t +vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size, + fssh_file_io_vec *vecs, fssh_size_t *_count) +{ + struct vnode *vnode = (struct vnode *)_vnode; + + FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size)); + + return FS_CALL(vnode, get_file_map)(vnode->mount->cookie, vnode->private_node, offset, size, vecs, _count); +} + + +fssh_status_t +vfs_stat_vnode(void *_vnode, struct fssh_stat *stat) +{ + struct vnode *vnode = (struct vnode *)_vnode; + + fssh_status_t status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, + vnode->private_node, stat); + + // fill in the st_dev and st_ino fields + if (status == FSSH_B_OK) { + stat->fssh_st_dev = vnode->device; + stat->fssh_st_ino = vnode->id; + } + + return status; +} + + +fssh_status_t +vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize) +{ + return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize); +} + + +/** If the given descriptor locked its vnode, that lock will be released. + */ + +void +vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor) +{ + struct vnode *vnode = fd_vnode(descriptor); + + if (vnode != NULL && vnode->mandatory_locked_by == descriptor) + vnode->mandatory_locked_by = NULL; +} + + +/** Closes all file descriptors of the specified I/O context that + * don't have the FSSH_O_CLOEXEC flag set. + */ + +void +vfs_exec_io_context(void *_context) +{ + struct io_context *context = (struct io_context *)_context; + uint32_t i; + + for (i = 0; i < context->table_size; i++) { + mutex_lock(&context->io_mutex); + + struct file_descriptor *descriptor = context->fds[i]; + bool remove = false; + + if (descriptor != NULL && fd_close_on_exec(context, i)) { + context->fds[i] = NULL; + context->num_used_fds--; + + remove = true; + } + + mutex_unlock(&context->io_mutex); + + if (remove) { + close_fd(descriptor); + put_fd(descriptor); + } + } +} + + +/** Sets up a new io_control structure, and inherits the properties + * of the parent io_control if it is given. + */ + +void * +vfs_new_io_context(void *_parentContext) +{ + fssh_size_t tableSize; + struct io_context *context; + struct io_context *parentContext; + + context = (io_context *)malloc(sizeof(struct io_context)); + if (context == NULL) + return NULL; + + fssh_memset(context, 0, sizeof(struct io_context)); + + parentContext = (struct io_context *)_parentContext; + if (parentContext) + tableSize = parentContext->table_size; + else + tableSize = DEFAULT_FD_TABLE_SIZE; + + // allocate space for FDs and their close-on-exec flag + context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize + + (tableSize + 7) / 8); + if (context->fds == NULL) { + free(context); + return NULL; + } + + fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize + + (tableSize + 7) / 8); + context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize); + + if (mutex_init(&context->io_mutex, "I/O context") < 0) { + free(context->fds); + free(context); + return NULL; + } + + // Copy all parent files which don't have the FSSH_O_CLOEXEC flag set + + if (parentContext) { + fssh_size_t i; + + mutex_lock(&parentContext->io_mutex); + + context->cwd = parentContext->cwd; + if (context->cwd) + inc_vnode_ref_count(context->cwd); + + for (i = 0; i < tableSize; i++) { + struct file_descriptor *descriptor = parentContext->fds[i]; + + if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) { + context->fds[i] = descriptor; + context->num_used_fds++; + fssh_atomic_add(&descriptor->ref_count, 1); + fssh_atomic_add(&descriptor->open_count, 1); + } + } + + mutex_unlock(&parentContext->io_mutex); + } else { + context->cwd = sRoot; + + if (context->cwd) + inc_vnode_ref_count(context->cwd); + } + + context->table_size = tableSize; + + return context; +} + + +fssh_status_t +vfs_free_io_context(void *_ioContext) +{ + struct io_context *context = (struct io_context *)_ioContext; + uint32_t i; + + if (context->cwd) + dec_vnode_ref_count(context->cwd, false); + + mutex_lock(&context->io_mutex); + + for (i = 0; i < context->table_size; i++) { + if (struct file_descriptor *descriptor = context->fds[i]) { + close_fd(descriptor); + put_fd(descriptor); + } + } + + mutex_destroy(&context->io_mutex); + + free(context->fds); + free(context); + + return FSSH_B_OK; +} + + +fssh_status_t +vfs_init(kernel_args *args) +{ + sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next), + &vnode_compare, &vnode_hash); + if (sVnodeTable == NULL) + fssh_panic("vfs_init: error creating vnode hash table\n"); + + list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link)); + + sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next), + &mount_compare, &mount_hash); + if (sMountsTable == NULL) + fssh_panic("vfs_init: error creating mounts hash table\n"); + + sRoot = NULL; + + if (mutex_init(&sFileSystemsMutex, "vfs_lock") < 0) + fssh_panic("vfs_init: error allocating file systems lock\n"); + + if (recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock") < 0) + fssh_panic("vfs_init: error allocating mount op lock\n"); + + if (mutex_init(&sMountMutex, "vfs_mount_lock") < 0) + fssh_panic("vfs_init: error allocating mount lock\n"); + + if (mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock") < 0) + fssh_panic("vfs_init: error allocating vnode::covered_by lock\n"); + + if (mutex_init(&sVnodeMutex, "vfs_vnode_lock") < 0) + fssh_panic("vfs_init: error allocating vnode lock\n"); + + if (block_cache_init() != FSSH_B_OK) + return FSSH_B_ERROR; + + return file_cache_init(); +} + + +// #pragma mark - +// The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...) + + +/** Calls fs_open() on the given vnode and returns a new + * file descriptor for it + */ + +static int +create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel) +{ + struct vnode *vnode; + fssh_fs_cookie cookie; + fssh_vnode_id newID; + int status; + + if (FS_CALL(directory, create) == NULL) + return FSSH_EROFS; + + status = FS_CALL(directory, create)(directory->mount->cookie, directory->private_node, name, openMode, perms, &cookie, &newID); + if (status < FSSH_B_OK) + return status; + + mutex_lock(&sVnodeMutex); + vnode = lookup_vnode(directory->device, newID); + mutex_unlock(&sVnodeMutex); + + if (vnode == NULL) { + fssh_dprintf("vfs: fs_create() returned success but there is no vnode!"); + return FSSH_EINVAL; + } + + if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0) + return status; + + // something went wrong, clean up + + FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, cookie); + FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, cookie); + put_vnode(vnode); + + FS_CALL(directory, unlink)(directory->mount->cookie, directory->private_node, name); + + return status; +} + + +/** Calls fs_open() on the given vnode and returns a new + * file descriptor for it + */ + +static int +open_vnode(struct vnode *vnode, int openMode, bool kernel) +{ + fssh_fs_cookie cookie; + int status; + + status = FS_CALL(vnode, open)(vnode->mount->cookie, vnode->private_node, openMode, &cookie); + if (status < 0) + return status; + + status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel); + if (status < 0) { + FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, cookie); + FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, cookie); + } + return status; +} + + +/** Calls fs open_dir() on the given vnode and returns a new + * file descriptor for it + */ + +static int +open_dir_vnode(struct vnode *vnode, bool kernel) +{ + fssh_fs_cookie cookie; + int status; + + status = FS_CALL(vnode, open_dir)(vnode->mount->cookie, vnode->private_node, &cookie); + if (status < FSSH_B_OK) + return status; + + // file is opened, create a fd + status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel); + if (status >= 0) + return status; + + FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, cookie); + FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, vnode->private_node, cookie); + + return status; +} + + +/** Calls fs open_attr_dir() on the given vnode and returns a new + * file descriptor for it. + * Used by attr_dir_open(), and attr_dir_open_fd(). + */ + +static int +open_attr_dir_vnode(struct vnode *vnode, bool kernel) +{ + fssh_fs_cookie cookie; + int status; + + if (FS_CALL(vnode, open_attr_dir) == NULL) + return FSSH_EOPNOTSUPP; + + status = FS_CALL(vnode, open_attr_dir)(vnode->mount->cookie, vnode->private_node, &cookie); + if (status < 0) + return status; + + // file is opened, create a fd + status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel); + if (status >= 0) + return status; + + FS_CALL(vnode, close_attr_dir)(vnode->mount->cookie, vnode->private_node, cookie); + FS_CALL(vnode, free_attr_dir_cookie)(vnode->mount->cookie, vnode->private_node, cookie); + + return status; +} + + +static int +file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel) +{ + struct vnode *directory; + int status; + + FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel)); + + // get directory to put the new file in + status = get_vnode(mountID, directoryID, &directory, false); + if (status < FSSH_B_OK) + return status; + + status = create_vnode(directory, name, openMode, perms, kernel); + put_vnode(directory); + + return status; +} + + +static int +file_create(int fd, char *path, int openMode, int perms, bool kernel) +{ + char name[FSSH_B_FILE_NAME_LENGTH]; + struct vnode *directory; + int status; + + FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel)); + + // get directory to put the new file in + status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); + if (status < 0) + return status; + + status = create_vnode(directory, name, openMode, perms, kernel); + + put_vnode(directory); + return status; +} + + +static int +file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel) +{ + struct vnode *vnode; + int status; + + if (name == NULL || *name == '\0') + return FSSH_B_BAD_VALUE; + + FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n", + mountID, directoryID, name, openMode)); + + // get the vnode matching the entry_ref + status = entry_ref_to_vnode(mountID, directoryID, name, &vnode); + if (status < FSSH_B_OK) + return status; + + status = open_vnode(vnode, openMode, kernel); + if (status < FSSH_B_OK) + put_vnode(vnode); + + return status; +} + + +static int +file_open(int fd, char *path, int openMode, bool kernel) +{ + int status = FSSH_B_OK; + bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0); + + FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n", + fd, path, openMode, kernel)); + + // get the vnode matching the vnode + path combination + struct vnode *vnode = NULL; + fssh_vnode_id parentID; + status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel); + if (status != FSSH_B_OK) + return status; + + // open the vnode + status = open_vnode(vnode, openMode, kernel); + // put only on error -- otherwise our reference was transferred to the FD + if (status < FSSH_B_OK) + put_vnode(vnode); + + return status; +} + + +static fssh_status_t +file_close(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + fssh_status_t status = FSSH_B_OK; + + FUNCTION(("file_close(descriptor = %p)\n", descriptor)); + + if (FS_CALL(vnode, close)) + status = FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + + return status; +} + + +static void +file_free_fd(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + if (vnode != NULL) { + FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + put_vnode(vnode); + } +} + + +static fssh_status_t +file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); + return FS_CALL(vnode, read)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); +} + + +static fssh_status_t +file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); + return FS_CALL(vnode, write)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); +} + + +static fssh_off_t +file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) +{ + fssh_off_t offset; + + FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType)); + // ToDo: seek should fail for pipes and FIFOs... + + switch (seekType) { + case FSSH_SEEK_SET: + offset = 0; + break; + case FSSH_SEEK_CUR: + offset = descriptor->pos; + break; + case FSSH_SEEK_END: + { + struct vnode *vnode = descriptor->u.vnode; + struct fssh_stat stat; + fssh_status_t status; + + if (FS_CALL(vnode, read_stat) == NULL) + return FSSH_EOPNOTSUPP; + + status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, &stat); + if (status < FSSH_B_OK) + return status; + + offset = stat.fssh_st_size; + break; + } + default: + return FSSH_B_BAD_VALUE; + } + + // assumes fssh_off_t is 64 bits wide + if (offset > 0 && LLONG_MAX - offset < pos) + return FSSH_EOVERFLOW; + + pos += offset; + if (pos < 0) + return FSSH_B_BAD_VALUE; + + return descriptor->pos = pos; +} + + +static fssh_status_t +dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel) +{ + struct vnode *vnode; + fssh_vnode_id newID; + fssh_status_t status; + + if (name == NULL || *name == '\0') + return FSSH_B_BAD_VALUE; + + FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms)); + + status = get_vnode(mountID, parentID, &vnode, kernel); + if (status < FSSH_B_OK) + return status; + + if (FS_CALL(vnode, create_dir)) + status = FS_CALL(vnode, create_dir)(vnode->mount->cookie, vnode->private_node, name, perms, &newID); + else + status = FSSH_EROFS; + + put_vnode(vnode); + return status; +} + + +static fssh_status_t +dir_create(int fd, char *path, int perms, bool kernel) +{ + char filename[FSSH_B_FILE_NAME_LENGTH]; + struct vnode *vnode; + fssh_vnode_id newID; + fssh_status_t status; + + FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel)); + + status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); + if (status < 0) + return status; + + if (FS_CALL(vnode, create_dir)) + status = FS_CALL(vnode, create_dir)(vnode->mount->cookie, vnode->private_node, filename, perms, &newID); + else + status = FSSH_EROFS; + + put_vnode(vnode); + return status; +} + + +static int +dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel) +{ + struct vnode *vnode; + int status; + + FUNCTION(("dir_open_entry_ref()\n")); + + if (name && *name == '\0') + return FSSH_B_BAD_VALUE; + + // get the vnode matching the entry_ref/node_ref + if (name) + status = entry_ref_to_vnode(mountID, parentID, name, &vnode); + else + status = get_vnode(mountID, parentID, &vnode, false); + if (status < FSSH_B_OK) + return status; + + status = open_dir_vnode(vnode, kernel); + if (status < FSSH_B_OK) + put_vnode(vnode); + + return status; +} + + +static int +dir_open(int fd, char *path, bool kernel) +{ + int status = FSSH_B_OK; + + FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel)); + + // get the vnode matching the vnode + path combination + struct vnode *vnode = NULL; + fssh_vnode_id parentID; + status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel); + if (status != FSSH_B_OK) + return status; + + // open the dir + status = open_dir_vnode(vnode, kernel); + if (status < FSSH_B_OK) + put_vnode(vnode); + + return status; +} + + +static fssh_status_t +dir_close(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("dir_close(descriptor = %p)\n", descriptor)); + + if (FS_CALL(vnode, close_dir)) + return FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + + return FSSH_B_OK; +} + + +static void +dir_free_fd(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + if (vnode != NULL) { + FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + put_vnode(vnode); + } +} + + +static fssh_status_t +dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, + fssh_size_t bufferSize, uint32_t *_count) +{ + return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count); +} + + +static void +fix_dirent(struct vnode *parent, struct fssh_dirent *entry) +{ + // set d_pdev and d_pino + entry->d_pdev = parent->device; + entry->d_pino = parent->id; + + // If this is the ".." entry and the directory is the root of a FS, + // we need to replace d_dev and d_ino with the actual values. + if (fssh_strcmp(entry->d_name, "..") == 0 + && parent->mount->root_vnode == parent + && parent->mount->covers_vnode) { + inc_vnode_ref_count(parent); + // vnode_path_to_vnode() puts the node + + struct vnode *vnode; + fssh_status_t status = vnode_path_to_vnode(parent, "..", false, 0, &vnode, + NULL, NULL); + + if (status == FSSH_B_OK) { + entry->d_dev = vnode->device; + entry->d_ino = vnode->id; + } + } else { + // resolve mount points + struct vnode *vnode = NULL; + fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false); + if (status != FSSH_B_OK) + return; + + mutex_lock(&sVnodeCoveredByMutex); + if (vnode->covered_by) { + entry->d_dev = vnode->covered_by->device; + entry->d_ino = vnode->covered_by->id; + } + mutex_unlock(&sVnodeCoveredByMutex); + + put_vnode(vnode); + } +} + + +static fssh_status_t +dir_read(struct vnode *vnode, fssh_fs_cookie cookie, struct fssh_dirent *buffer, + fssh_size_t bufferSize, uint32_t *_count) +{ + if (!FS_CALL(vnode, read_dir)) + return FSSH_EOPNOTSUPP; + + fssh_status_t error = FS_CALL(vnode, read_dir)(vnode->mount->cookie,vnode->private_node,cookie,buffer,bufferSize,_count); + if (error != FSSH_B_OK) + return error; + + // we need to adjust the read dirents + if (*_count > 0) { + // XXX: Currently reading only one dirent is supported. Make this a loop! + fix_dirent(vnode, buffer); + } + + return error; +} + + +static fssh_status_t +dir_rewind(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + if (FS_CALL(vnode, rewind_dir)) + return FS_CALL(vnode, rewind_dir)(vnode->mount->cookie,vnode->private_node,descriptor->cookie); + + return FSSH_EOPNOTSUPP; +} + + +static fssh_status_t +dir_remove(int fd, char *path, bool kernel) +{ + char name[FSSH_B_FILE_NAME_LENGTH]; + struct vnode *directory; + fssh_status_t status; + + if (path != NULL) { + // we need to make sure our path name doesn't stop with "/", ".", or ".." + char *lastSlash = fssh_strrchr(path, '/'); + if (lastSlash != NULL) { + char *leaf = lastSlash + 1; + if (!fssh_strcmp(leaf, "..")) + return FSSH_B_NOT_ALLOWED; + + // omit multiple slashes + while (lastSlash > path && lastSlash[-1] == '/') { + lastSlash--; + } + + if (!leaf[0] + || !fssh_strcmp(leaf, ".")) { + // "name/" -> "name", or "name/." -> "name" + lastSlash[0] = '\0'; + } + } else if (!fssh_strcmp(path, "..")) + return FSSH_B_NOT_ALLOWED; + } + + status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); + if (status < FSSH_B_OK) + return status; + + if (FS_CALL(directory, remove_dir)) { + status = FS_CALL(directory, remove_dir)(directory->mount->cookie, + directory->private_node, name); + } else + status = FSSH_EROFS; + + put_vnode(directory); + return status; +} + + +static fssh_status_t +common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer, + fssh_size_t length) +{ + struct vnode *vnode = descriptor->u.vnode; + + if (FS_CALL(vnode, ioctl)) { + return FS_CALL(vnode, ioctl)(vnode->mount->cookie, vnode->private_node, + descriptor->cookie, op, buffer, length); + } + + return FSSH_EOPNOTSUPP; +} + + +static fssh_status_t +common_fcntl(int fd, int op, uint32_t argument, bool kernel) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + fssh_status_t status; + + FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n", + fd, op, argument, kernel ? "kernel" : "user")); + + descriptor = get_fd_and_vnode(fd, &vnode, kernel); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + switch (op) { + case FSSH_F_SETFD: + { + struct io_context *context = get_current_io_context(kernel); + // Set file descriptor flags + + // FSSH_O_CLOEXEC is the only flag available at this time + mutex_lock(&context->io_mutex); + fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC); + mutex_unlock(&context->io_mutex); + + status = FSSH_B_OK; + break; + } + + case FSSH_F_GETFD: + { + struct io_context *context = get_current_io_context(kernel); + + // Get file descriptor flags + mutex_lock(&context->io_mutex); + status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0; + mutex_unlock(&context->io_mutex); + break; + } + + case FSSH_F_SETFL: + // Set file descriptor open mode + if (FS_CALL(vnode, set_flags)) { + // we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK + argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK; + + status = FS_CALL(vnode, set_flags)(vnode->mount->cookie, + vnode->private_node, descriptor->cookie, (int)argument); + if (status == FSSH_B_OK) { + // update this descriptor's open_mode field + descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK)) + | argument; + } + } else + status = FSSH_EOPNOTSUPP; + break; + + case FSSH_F_GETFL: + // Get file descriptor open mode + status = descriptor->open_mode; + break; + + case FSSH_F_DUPFD: + { + struct io_context *context = get_current_io_context(kernel); + + status = new_fd_etc(context, descriptor, (int)argument); + if (status >= 0) { + mutex_lock(&context->io_mutex); + fd_set_close_on_exec(context, fd, false); + mutex_unlock(&context->io_mutex); + + fssh_atomic_add(&descriptor->ref_count, 1); + } + break; + } + + case FSSH_F_GETLK: + case FSSH_F_SETLK: + case FSSH_F_SETLKW: + status = FSSH_B_BAD_VALUE; + break; + + // ToDo: add support for more ops? + + default: + status = FSSH_B_BAD_VALUE; + } + + put_fd(descriptor); + return status; +} + + +static fssh_status_t +common_sync(int fd, bool kernel) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + fssh_status_t status; + + FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel)); + + descriptor = get_fd_and_vnode(fd, &vnode, kernel); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + if (FS_CALL(vnode, fsync) != NULL) + status = FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node); + else + status = FSSH_EOPNOTSUPP; + + put_fd(descriptor); + return status; +} + + +static fssh_status_t +common_lock_node(int fd, bool kernel) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + + descriptor = get_fd_and_vnode(fd, &vnode, kernel); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + fssh_status_t status = FSSH_B_OK; + + // We need to set the locking atomically - someone + // else might set one at the same time + if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by, + (fssh_addr_t)descriptor, 0) != 0) + status = FSSH_B_BUSY; + + put_fd(descriptor); + return status; +} + + +static fssh_status_t +common_unlock_node(int fd, bool kernel) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + + descriptor = get_fd_and_vnode(fd, &vnode, kernel); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + fssh_status_t status = FSSH_B_OK; + + // We need to set the locking atomically - someone + // else might set one at the same time + if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by, + 0, (fssh_addr_t)descriptor) != (int32_t)descriptor) + status = FSSH_B_BAD_VALUE; + + put_fd(descriptor); + return status; +} + + +static fssh_status_t +common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize, + bool kernel) +{ + struct vnode *vnode; + fssh_status_t status; + + status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel); + if (status < FSSH_B_OK) + return status; + + if (FS_CALL(vnode, read_symlink) != NULL) { + status = FS_CALL(vnode, read_symlink)(vnode->mount->cookie, + vnode->private_node, buffer, _bufferSize); + } else + status = FSSH_B_BAD_VALUE; + + put_vnode(vnode); + return status; +} + + +static fssh_status_t +common_create_symlink(int fd, char *path, const char *toPath, int mode, + bool kernel) +{ + // path validity checks have to be in the calling function! + char name[FSSH_B_FILE_NAME_LENGTH]; + struct vnode *vnode; + fssh_status_t status; + + FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel)); + + status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel); + if (status < FSSH_B_OK) + return status; + + if (FS_CALL(vnode, create_symlink) != NULL) + status = FS_CALL(vnode, create_symlink)(vnode->mount->cookie, vnode->private_node, name, toPath, mode); + else + status = FSSH_EROFS; + + put_vnode(vnode); + + return status; +} + + +static fssh_status_t +common_create_link(char *path, char *toPath, bool kernel) +{ + // path validity checks have to be in the calling function! + char name[FSSH_B_FILE_NAME_LENGTH]; + struct vnode *directory, *vnode; + fssh_status_t status; + + FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel)); + + status = path_to_dir_vnode(path, &directory, name, kernel); + if (status < FSSH_B_OK) + return status; + + status = path_to_vnode(toPath, true, &vnode, NULL, kernel); + if (status < FSSH_B_OK) + goto err; + + if (directory->mount != vnode->mount) { + status = FSSH_B_CROSS_DEVICE_LINK; + goto err1; + } + + if (FS_CALL(vnode, link) != NULL) + status = FS_CALL(vnode, link)(directory->mount->cookie, directory->private_node, name, vnode->private_node); + else + status = FSSH_EROFS; + +err1: + put_vnode(vnode); +err: + put_vnode(directory); + + return status; +} + + +static fssh_status_t +common_unlink(int fd, char *path, bool kernel) +{ + char filename[FSSH_B_FILE_NAME_LENGTH]; + struct vnode *vnode; + fssh_status_t status; + + FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel)); + + status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); + if (status < 0) + return status; + + if (FS_CALL(vnode, unlink) != NULL) + status = FS_CALL(vnode, unlink)(vnode->mount->cookie, vnode->private_node, filename); + else + status = FSSH_EROFS; + + put_vnode(vnode); + + return status; +} + + +static fssh_status_t +common_access(char *path, int mode, bool kernel) +{ + struct vnode *vnode; + fssh_status_t status; + + status = path_to_vnode(path, true, &vnode, NULL, kernel); + if (status < FSSH_B_OK) + return status; + + if (FS_CALL(vnode, access) != NULL) + status = FS_CALL(vnode, access)(vnode->mount->cookie, vnode->private_node, mode); + else + status = FSSH_B_OK; + + put_vnode(vnode); + + return status; +} + + +static fssh_status_t +common_rename(int fd, char *path, int newFD, char *newPath, bool kernel) +{ + struct vnode *fromVnode, *toVnode; + char fromName[FSSH_B_FILE_NAME_LENGTH]; + char toName[FSSH_B_FILE_NAME_LENGTH]; + fssh_status_t status; + + FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel)); + + status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel); + if (status < 0) + return status; + + status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel); + if (status < 0) + goto err; + + if (fromVnode->device != toVnode->device) { + status = FSSH_B_CROSS_DEVICE_LINK; + goto err1; + } + + if (FS_CALL(fromVnode, rename) != NULL) + status = FS_CALL(fromVnode, rename)(fromVnode->mount->cookie, fromVnode->private_node, fromName, toVnode->private_node, toName); + else + status = FSSH_EROFS; + +err1: + put_vnode(toVnode); +err: + put_vnode(fromVnode); + + return status; +} + + +static fssh_status_t +common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("common_read_stat: stat %p\n", stat)); + + fssh_status_t status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, + vnode->private_node, stat); + + // fill in the st_dev and st_ino fields + if (status == FSSH_B_OK) { + stat->fssh_st_dev = vnode->device; + stat->fssh_st_ino = vnode->id; + } + + return status; +} + + +static fssh_status_t +common_write_stat(struct file_descriptor *descriptor, + const struct fssh_stat *stat, int statMask) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask)); + if (!FS_CALL(vnode, write_stat)) + return FSSH_EROFS; + + return FS_CALL(vnode, write_stat)(vnode->mount->cookie, vnode->private_node, stat, statMask); +} + + +static fssh_status_t +common_path_read_stat(int fd, char *path, bool traverseLeafLink, + struct fssh_stat *stat, bool kernel) +{ + struct vnode *vnode; + fssh_status_t status; + + FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat)); + + status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); + if (status < 0) + return status; + + status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, stat); + + // fill in the st_dev and st_ino fields + if (status == FSSH_B_OK) { + stat->fssh_st_dev = vnode->device; + stat->fssh_st_ino = vnode->id; + } + + put_vnode(vnode); + return status; +} + + +static fssh_status_t +common_path_write_stat(int fd, char *path, bool traverseLeafLink, + const struct fssh_stat *stat, int statMask, bool kernel) +{ + struct vnode *vnode; + fssh_status_t status; + + FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel)); + + status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); + if (status < 0) + return status; + + if (FS_CALL(vnode, write_stat)) + status = FS_CALL(vnode, write_stat)(vnode->mount->cookie, vnode->private_node, stat, statMask); + else + status = FSSH_EROFS; + + put_vnode(vnode); + + return status; +} + + +static int +attr_dir_open(int fd, char *path, bool kernel) +{ + struct vnode *vnode; + int status; + + FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel)); + + status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); + if (status < FSSH_B_OK) + return status; + + status = open_attr_dir_vnode(vnode, kernel); + if (status < 0) + put_vnode(vnode); + + return status; +} + + +static fssh_status_t +attr_dir_close(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor)); + + if (FS_CALL(vnode, close_attr_dir)) + return FS_CALL(vnode, close_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + + return FSSH_B_OK; +} + + +static void +attr_dir_free_fd(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + if (vnode != NULL) { + FS_CALL(vnode, free_attr_dir_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + put_vnode(vnode); + } +} + + +static fssh_status_t +attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, + fssh_size_t bufferSize, uint32_t *_count) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor)); + + if (FS_CALL(vnode, read_attr_dir)) + return FS_CALL(vnode, read_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, buffer, bufferSize, _count); + + return FSSH_EOPNOTSUPP; +} + + +static fssh_status_t +attr_dir_rewind(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor)); + + if (FS_CALL(vnode, rewind_attr_dir)) + return FS_CALL(vnode, rewind_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + + return FSSH_EOPNOTSUPP; +} + + +static int +attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel) +{ + struct vnode *vnode; + fssh_fs_cookie cookie; + int status; + + if (name == NULL || *name == '\0') + return FSSH_B_BAD_VALUE; + + vnode = get_vnode_from_fd(fd, kernel); + if (vnode == NULL) + return FSSH_B_FILE_ERROR; + + if (FS_CALL(vnode, create_attr) == NULL) { + status = FSSH_EROFS; + goto err; + } + + status = FS_CALL(vnode, create_attr)(vnode->mount->cookie, vnode->private_node, name, type, openMode, &cookie); + if (status < FSSH_B_OK) + goto err; + + if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) + return status; + + FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, cookie); + FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, cookie); + + FS_CALL(vnode, remove_attr)(vnode->mount->cookie, vnode->private_node, name); + +err: + put_vnode(vnode); + + return status; +} + + +static int +attr_open(int fd, const char *name, int openMode, bool kernel) +{ + struct vnode *vnode; + fssh_fs_cookie cookie; + int status; + + if (name == NULL || *name == '\0') + return FSSH_B_BAD_VALUE; + + vnode = get_vnode_from_fd(fd, kernel); + if (vnode == NULL) + return FSSH_B_FILE_ERROR; + + if (FS_CALL(vnode, open_attr) == NULL) { + status = FSSH_EOPNOTSUPP; + goto err; + } + + status = FS_CALL(vnode, open_attr)(vnode->mount->cookie, vnode->private_node, name, openMode, &cookie); + if (status < FSSH_B_OK) + goto err; + + // now we only need a file descriptor for this attribute and we're done + if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) + return status; + + FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, cookie); + FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, cookie); + +err: + put_vnode(vnode); + + return status; +} + + +static fssh_status_t +attr_close(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_close(descriptor = %p)\n", descriptor)); + + if (FS_CALL(vnode, close_attr)) + return FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + + return FSSH_B_OK; +} + + +static void +attr_free_fd(struct file_descriptor *descriptor) +{ + struct vnode *vnode = descriptor->u.vnode; + + if (vnode != NULL) { + FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); + put_vnode(vnode); + } +} + + +static fssh_status_t +attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); + if (!FS_CALL(vnode, read_attr)) + return FSSH_EOPNOTSUPP; + + return FS_CALL(vnode, read_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); +} + + +static fssh_status_t +attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); + if (!FS_CALL(vnode, write_attr)) + return FSSH_EOPNOTSUPP; + + return FS_CALL(vnode, write_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); +} + + +static fssh_off_t +attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) +{ + fssh_off_t offset; + + switch (seekType) { + case FSSH_SEEK_SET: + offset = 0; + break; + case FSSH_SEEK_CUR: + offset = descriptor->pos; + break; + case FSSH_SEEK_END: + { + struct vnode *vnode = descriptor->u.vnode; + struct fssh_stat stat; + fssh_status_t status; + + if (FS_CALL(vnode, read_stat) == NULL) + return FSSH_EOPNOTSUPP; + + status = FS_CALL(vnode, read_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, &stat); + if (status < FSSH_B_OK) + return status; + + offset = stat.fssh_st_size; + break; + } + default: + return FSSH_B_BAD_VALUE; + } + + // assumes fssh_off_t is 64 bits wide + if (offset > 0 && LLONG_MAX - offset < pos) + return FSSH_EOVERFLOW; + + pos += offset; + if (pos < 0) + return FSSH_B_BAD_VALUE; + + return descriptor->pos = pos; +} + + +static fssh_status_t +attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_read_stat: stat 0x%p\n", stat)); + + if (!FS_CALL(vnode, read_attr_stat)) + return FSSH_EOPNOTSUPP; + + return FS_CALL(vnode, read_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, stat); +} + + +static fssh_status_t +attr_write_stat(struct file_descriptor *descriptor, + const struct fssh_stat *stat, int statMask) +{ + struct vnode *vnode = descriptor->u.vnode; + + FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask)); + + if (!FS_CALL(vnode, write_attr_stat)) + return FSSH_EROFS; + + return FS_CALL(vnode, write_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, stat, statMask); +} + + +static fssh_status_t +attr_remove(int fd, const char *name, bool kernel) +{ + struct file_descriptor *descriptor; + struct vnode *vnode; + fssh_status_t status; + + if (name == NULL || *name == '\0') + return FSSH_B_BAD_VALUE; + + FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel)); + + descriptor = get_fd_and_vnode(fd, &vnode, kernel); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + if (FS_CALL(vnode, remove_attr)) + status = FS_CALL(vnode, remove_attr)(vnode->mount->cookie, vnode->private_node, name); + else + status = FSSH_EROFS; + + put_fd(descriptor); + + return status; +} + + +static fssh_status_t +attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel) +{ + struct file_descriptor *fromDescriptor, *toDescriptor; + struct vnode *fromVnode, *toVnode; + fssh_status_t status; + + if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0') + return FSSH_B_BAD_VALUE; + + FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel)); + + fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel); + if (fromDescriptor == NULL) + return FSSH_B_FILE_ERROR; + + toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel); + if (toDescriptor == NULL) { + status = FSSH_B_FILE_ERROR; + goto err; + } + + // are the files on the same volume? + if (fromVnode->device != toVnode->device) { + status = FSSH_B_CROSS_DEVICE_LINK; + goto err1; + } + + if (FS_CALL(fromVnode, rename_attr)) + status = FS_CALL(fromVnode, rename_attr)(fromVnode->mount->cookie, fromVnode->private_node, fromName, toVnode->private_node, toName); + else + status = FSSH_EROFS; + +err1: + put_fd(toDescriptor); +err: + put_fd(fromDescriptor); + + return status; +} + + +static fssh_status_t +index_dir_open(fssh_mount_id mountID, bool kernel) +{ + struct fs_mount *mount; + fssh_fs_cookie cookie; + + FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel)); + + fssh_status_t status = get_mount(mountID, &mount); + if (status < FSSH_B_OK) + return status; + + if (FS_MOUNT_CALL(mount, open_index_dir) == NULL) { + status = FSSH_EOPNOTSUPP; + goto out; + } + + status = FS_MOUNT_CALL(mount, open_index_dir)(mount->cookie, &cookie); + if (status < FSSH_B_OK) + goto out; + + // get fd for the index directory + status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel); + if (status >= 0) + goto out; + + // something went wrong + FS_MOUNT_CALL(mount, close_index_dir)(mount->cookie, cookie); + FS_MOUNT_CALL(mount, free_index_dir_cookie)(mount->cookie, cookie); + +out: + put_mount(mount); + return status; +} + + +static fssh_status_t +index_dir_close(struct file_descriptor *descriptor) +{ + struct fs_mount *mount = descriptor->u.mount; + + FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor)); + + if (FS_MOUNT_CALL(mount, close_index_dir)) + return FS_MOUNT_CALL(mount, close_index_dir)(mount->cookie, descriptor->cookie); + + return FSSH_B_OK; +} + + +static void +index_dir_free_fd(struct file_descriptor *descriptor) +{ + struct fs_mount *mount = descriptor->u.mount; + + if (mount != NULL) { + FS_MOUNT_CALL(mount, free_index_dir_cookie)(mount->cookie, descriptor->cookie); + // ToDo: find a replacement ref_count object - perhaps the root dir? + //put_vnode(vnode); + } +} + + +static fssh_status_t +index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, + fssh_size_t bufferSize, uint32_t *_count) +{ + struct fs_mount *mount = descriptor->u.mount; + + if (FS_MOUNT_CALL(mount, read_index_dir)) + return FS_MOUNT_CALL(mount, read_index_dir)(mount->cookie, descriptor->cookie, buffer, bufferSize, _count); + + return FSSH_EOPNOTSUPP; +} + + +static fssh_status_t +index_dir_rewind(struct file_descriptor *descriptor) +{ + struct fs_mount *mount = descriptor->u.mount; + + if (FS_MOUNT_CALL(mount, rewind_index_dir)) + return FS_MOUNT_CALL(mount, rewind_index_dir)(mount->cookie, descriptor->cookie); + + return FSSH_EOPNOTSUPP; +} + + +static fssh_status_t +index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel) +{ + FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); + + struct fs_mount *mount; + fssh_status_t status = get_mount(mountID, &mount); + if (status < FSSH_B_OK) + return status; + + if (FS_MOUNT_CALL(mount, create_index) == NULL) { + status = FSSH_EROFS; + goto out; + } + + status = FS_MOUNT_CALL(mount, create_index)(mount->cookie, name, type, flags); + +out: + put_mount(mount); + return status; +} + + +static fssh_status_t +index_name_read_stat(fssh_mount_id mountID, const char *name, + struct fssh_stat *stat, bool kernel) +{ + FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); + + struct fs_mount *mount; + fssh_status_t status = get_mount(mountID, &mount); + if (status < FSSH_B_OK) + return status; + + if (FS_MOUNT_CALL(mount, read_index_stat) == NULL) { + status = FSSH_EOPNOTSUPP; + goto out; + } + + status = FS_MOUNT_CALL(mount, read_index_stat)(mount->cookie, name, stat); + +out: + put_mount(mount); + return status; +} + + +static fssh_status_t +index_remove(fssh_mount_id mountID, const char *name, bool kernel) +{ + FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); + + struct fs_mount *mount; + fssh_status_t status = get_mount(mountID, &mount); + if (status < FSSH_B_OK) + return status; + + if (FS_MOUNT_CALL(mount, remove_index) == NULL) { + status = FSSH_EROFS; + goto out; + } + + status = FS_MOUNT_CALL(mount, remove_index)(mount->cookie, name); + +out: + put_mount(mount); + return status; +} + + +static fssh_status_t +query_close(struct file_descriptor *descriptor) +{ + struct fs_mount *mount = descriptor->u.mount; + + FUNCTION(("query_close(descriptor = %p)\n", descriptor)); + + if (FS_MOUNT_CALL(mount, close_query)) + return FS_MOUNT_CALL(mount, close_query)(mount->cookie, descriptor->cookie); + + return FSSH_B_OK; +} + + +static void +query_free_fd(struct file_descriptor *descriptor) +{ + struct fs_mount *mount = descriptor->u.mount; + + if (mount != NULL) { + FS_MOUNT_CALL(mount, free_query_cookie)(mount->cookie, descriptor->cookie); + // ToDo: find a replacement ref_count object - perhaps the root dir? + //put_vnode(vnode); + } +} + + +static fssh_status_t +query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, + fssh_size_t bufferSize, uint32_t *_count) +{ + struct fs_mount *mount = descriptor->u.mount; + + if (FS_MOUNT_CALL(mount, read_query)) + return FS_MOUNT_CALL(mount, read_query)(mount->cookie, descriptor->cookie, buffer, bufferSize, _count); + + return FSSH_EOPNOTSUPP; +} + + +static fssh_status_t +query_rewind(struct file_descriptor *descriptor) +{ + struct fs_mount *mount = descriptor->u.mount; + + if (FS_MOUNT_CALL(mount, rewind_query)) + return FS_MOUNT_CALL(mount, rewind_query)(mount->cookie, descriptor->cookie); + + return FSSH_EOPNOTSUPP; +} + + +// #pragma mark - +// General File System functions + + +static fssh_dev_t +fs_mount(char *path, const char *device, const char *fsName, uint32_t flags, + const char *args, bool kernel) +{ + struct fs_mount *mount; + fssh_status_t status = 0; + + FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName)); + + // The path is always safe, we just have to make sure that fsName is + // almost valid - we can't make any assumptions about args, though. + // A NULL fsName is OK, if a device was given and the FS is not virtual. + // We'll get it from the DDM later. + if (fsName == NULL) { + if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) + return FSSH_B_BAD_VALUE; + } else if (fsName[0] == '\0') + return FSSH_B_BAD_VALUE; + + RecursiveLocker mountOpLocker(sMountOpLock); + + // If the file system is not a "virtual" one, the device argument should + // point to a real file/device (if given at all). + // get the partition + KPath normalizedDevice; + + if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) { + // normalize the device path + status = normalizedDevice.SetTo(device, true); + if (status != FSSH_B_OK) + return status; + + device = normalizedDevice.Path(); + // correct path to file device + } + + mount = (struct fs_mount *)malloc(sizeof(struct fs_mount)); + if (mount == NULL) + return FSSH_B_NO_MEMORY; + + list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link)); + + mount->fs_name = get_file_system_name(fsName); + if (mount->fs_name == NULL) { + status = FSSH_B_NO_MEMORY; + goto err1; + } + + mount->device_name = fssh_strdup(device); + // "device" can be NULL + + mount->fs = get_file_system(fsName); + if (mount->fs == NULL) { + status = FSSH_ENODEV; + goto err3; + } + + status = recursive_lock_init(&mount->rlock, "mount rlock"); + if (status < FSSH_B_OK) + goto err4; + + // initialize structure + mount->id = sNextMountID++; + mount->root_vnode = NULL; + mount->covers_vnode = NULL; + mount->cookie = NULL; + mount->unmounting = false; + mount->owns_file_device = false; + + // insert mount struct into list before we call FS's mount() function + // so that vnodes can be created for this mount + mutex_lock(&sMountMutex); + hash_insert(sMountsTable, mount); + mutex_unlock(&sMountMutex); + + fssh_vnode_id rootID; + + if (!sRoot) { + // we haven't mounted anything yet + if (fssh_strcmp(path, "/") != 0) { + status = FSSH_B_ERROR; + goto err5; + } + + status = FS_MOUNT_CALL(mount, mount)(mount->id, device, flags, args, &mount->cookie, &rootID); + if (status < 0) { + // ToDo: why should we hide the error code from the file system here? + //status = ERR_VFS_GENERAL; + goto err5; + } + } else { + struct vnode *coveredVnode; + status = path_to_vnode(path, true, &coveredVnode, NULL, kernel); + if (status < FSSH_B_OK) + goto err5; + + // make sure covered_vnode is a DIR + struct fssh_stat coveredNodeStat; + status = FS_CALL(coveredVnode, read_stat)(coveredVnode->mount->cookie, + coveredVnode->private_node, &coveredNodeStat); + if (status < FSSH_B_OK) + goto err5; + + if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) { + status = FSSH_B_NOT_A_DIRECTORY; + goto err5; + } + + if (coveredVnode->mount->root_vnode == coveredVnode) { + // this is already a mount point + status = FSSH_B_BUSY; + goto err5; + } + + mount->covers_vnode = coveredVnode; + + // mount it + status = FS_MOUNT_CALL(mount, mount)(mount->id, device, flags, args, &mount->cookie, &rootID); + if (status < FSSH_B_OK) + goto err6; + } + + // the root node is supposed to be owned by the file system - it must + // exist at this point + mount->root_vnode = lookup_vnode(mount->id, rootID); + if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) { + fssh_panic("fs_mount: file system does not own its root node!\n"); + status = FSSH_B_ERROR; + goto err7; + } + + // No race here, since fs_mount() is the only function changing + // covers_vnode (and holds sMountOpLock at that time). + mutex_lock(&sVnodeCoveredByMutex); + if (mount->covers_vnode) + mount->covers_vnode->covered_by = mount->root_vnode; + mutex_unlock(&sVnodeCoveredByMutex); + + if (!sRoot) + sRoot = mount->root_vnode; + + return mount->id; + +err7: + FS_MOUNT_CALL(mount, unmount)(mount->cookie); +err6: + if (mount->covers_vnode) + put_vnode(mount->covers_vnode); +err5: + mutex_lock(&sMountMutex); + hash_remove(sMountsTable, mount); + mutex_unlock(&sMountMutex); + + recursive_lock_destroy(&mount->rlock); +err4: + put_file_system(mount->fs); + free(mount->device_name); +err3: + free(mount->fs_name); +err1: + free(mount); + + return status; +} + + +static fssh_status_t +fs_unmount(char *path, uint32_t flags, bool kernel) +{ + struct fs_mount *mount; + struct vnode *vnode; + fssh_status_t err; + + FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel)); + + err = path_to_vnode(path, true, &vnode, NULL, kernel); + if (err < 0) + return FSSH_B_ENTRY_NOT_FOUND; + + RecursiveLocker mountOpLocker(sMountOpLock); + + mount = find_mount(vnode->device); + if (!mount) + fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode); + + if (mount->root_vnode != vnode) { + // not mountpoint + put_vnode(vnode); + return FSSH_B_BAD_VALUE; + } + + // grab the vnode master mutex to keep someone from creating + // a vnode while we're figuring out if we can continue + mutex_lock(&sVnodeMutex); + + bool disconnectedDescriptors = false; + + while (true) { + bool busy = false; + + // cycle through the list of vnodes associated with this mount and + // make sure all of them are not busy or have refs on them + vnode = NULL; + while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { + // The root vnode ref_count needs to be 2 here: one for the file + // system, one from the path_to_vnode() call above + if (vnode->busy + || ((vnode->ref_count != 0 && mount->root_vnode != vnode) + || (vnode->ref_count != 2 && mount->root_vnode == vnode))) { + // there are still vnodes in use on this mount, so we cannot + // unmount yet + busy = true; + break; + } + } + + if (!busy) + break; + + if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) { + mutex_unlock(&sVnodeMutex); + put_vnode(mount->root_vnode); + + return FSSH_B_BUSY; + } + + if (disconnectedDescriptors) { + // wait a bit until the last access is finished, and then try again + mutex_unlock(&sVnodeMutex); + fssh_snooze(100000); + // TODO: if there is some kind of bug that prevents the ref counts + // from getting back to zero, this will fall into an endless loop... + mutex_lock(&sVnodeMutex); + continue; + } + + // the file system is still busy - but we're forced to unmount it, + // so let's disconnect all open file descriptors + + mount->unmounting = true; + // prevent new vnodes from being created + + mutex_unlock(&sVnodeMutex); + + disconnect_mount_or_vnode_fds(mount, NULL); + disconnectedDescriptors = true; + + mutex_lock(&sVnodeMutex); + } + + // we can safely continue, mark all of the vnodes busy and this mount + // structure in unmounting state + mount->unmounting = true; + + while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { + vnode->busy = true; + + if (vnode->ref_count == 0) { + // this vnode has been unused before + list_remove_item(&sUnusedVnodeList, vnode); + sUnusedVnodes--; + } + } + + // The ref_count of the root node is 2 at this point, see above why this is + mount->root_vnode->ref_count -= 2; + + mutex_unlock(&sVnodeMutex); + + mutex_lock(&sVnodeCoveredByMutex); + mount->covers_vnode->covered_by = NULL; + mutex_unlock(&sVnodeCoveredByMutex); + put_vnode(mount->covers_vnode); + + // Free all vnodes associated with this mount. + // They will be removed from the mount list by free_vnode(), so + // we don't have to do this. + while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) { + free_vnode(vnode, false); + } + + // remove the mount structure from the hash table + mutex_lock(&sMountMutex); + hash_remove(sMountsTable, mount); + mutex_unlock(&sMountMutex); + + mountOpLocker.Unlock(); + + FS_MOUNT_CALL(mount, unmount)(mount->cookie); + + // release the file system + put_file_system(mount->fs); + + free(mount->device_name); + free(mount->fs_name); + free(mount); + + return FSSH_B_OK; +} + + +static fssh_status_t +fs_sync(fssh_dev_t device) +{ + struct fs_mount *mount; + fssh_status_t status = get_mount(device, &mount); + if (status < FSSH_B_OK) + return status; + + mutex_lock(&sMountMutex); + + if (FS_MOUNT_CALL(mount, sync)) + status = FS_MOUNT_CALL(mount, sync)(mount->cookie); + + mutex_unlock(&sMountMutex); + + struct vnode *previousVnode = NULL; + while (true) { + // synchronize access to vnode list + recursive_lock_lock(&mount->rlock); + + struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes, + previousVnode); + + fssh_vnode_id id = -1; + if (vnode != NULL) + id = vnode->id; + + recursive_lock_unlock(&mount->rlock); + + if (vnode == NULL) + break; + + // acquire a reference to the vnode + + if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) { + if (previousVnode != NULL) + put_vnode(previousVnode); + + if (FS_CALL(vnode, fsync) != NULL) + FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node); + + // the next vnode might change until we lock the vnode list again, + // but this vnode won't go away since we keep a reference to it. + previousVnode = vnode; + } else { + fssh_dprintf("syncing of mount %d stopped due to vnode %lld.\n", (int)mount->id, id); + break; + } + } + + if (previousVnode != NULL) + put_vnode(previousVnode); + + put_mount(mount); + return status; +} + + +static fssh_status_t +fs_read_info(fssh_dev_t device, struct fssh_fs_info *info) +{ + struct fs_mount *mount; + fssh_status_t status = get_mount(device, &mount); + if (status < FSSH_B_OK) + return status; + + fssh_memset(info, 0, sizeof(struct fssh_fs_info)); + + if (FS_MOUNT_CALL(mount, read_fs_info)) + status = FS_MOUNT_CALL(mount, read_fs_info)(mount->cookie, info); + + // fill in info the file system doesn't (have to) know about + if (status == FSSH_B_OK) { + info->dev = mount->id; + info->root = mount->root_vnode->id; + fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name)); + if (mount->device_name != NULL) { + fssh_strlcpy(info->device_name, mount->device_name, + sizeof(info->device_name)); + } + } + + // if the call is not supported by the file system, there are still + // the parts that we filled out ourselves + + put_mount(mount); + return status; +} + + +static fssh_status_t +fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) +{ + struct fs_mount *mount; + fssh_status_t status = get_mount(device, &mount); + if (status < FSSH_B_OK) + return status; + + if (FS_MOUNT_CALL(mount, write_fs_info)) + status = FS_MOUNT_CALL(mount, write_fs_info)(mount->cookie, info, mask); + else + status = FSSH_EROFS; + + put_mount(mount); + return status; +} + + +static fssh_dev_t +fs_next_device(int32_t *_cookie) +{ + struct fs_mount *mount = NULL; + fssh_dev_t device = *_cookie; + + mutex_lock(&sMountMutex); + + // Since device IDs are assigned sequentially, this algorithm + // does work good enough. It makes sure that the device list + // returned is sorted, and that no device is skipped when an + // already visited device got unmounted. + + while (device < sNextMountID) { + mount = find_mount(device++); + if (mount != NULL && mount->cookie != NULL) + break; + } + + *_cookie = device; + + if (mount != NULL) + device = mount->id; + else + device = FSSH_B_BAD_VALUE; + + mutex_unlock(&sMountMutex); + + return device; +} + + +static fssh_status_t +get_cwd(char *buffer, fssh_size_t size, bool kernel) +{ + // Get current working directory from io context + struct io_context *context = get_current_io_context(kernel); + fssh_status_t status; + + FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size)); + + mutex_lock(&context->io_mutex); + + if (context->cwd) + status = dir_vnode_to_path(context->cwd, buffer, size); + else + status = FSSH_B_ERROR; + + mutex_unlock(&context->io_mutex); + return status; +} + + +static fssh_status_t +set_cwd(int fd, char *path, bool kernel) +{ + struct io_context *context; + struct vnode *vnode = NULL; + struct vnode *oldDirectory; + struct fssh_stat stat; + fssh_status_t status; + + FUNCTION(("set_cwd: path = \'%s\'\n", path)); + + // Get vnode for passed path, and bail if it failed + status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); + if (status < 0) + return status; + + status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, &stat); + if (status < 0) + goto err; + + if (!FSSH_S_ISDIR(stat.fssh_st_mode)) { + // nope, can't cwd to here + status = FSSH_B_NOT_A_DIRECTORY; + goto err; + } + + // Get current io context and lock + context = get_current_io_context(kernel); + mutex_lock(&context->io_mutex); + + // save the old current working directory first + oldDirectory = context->cwd; + context->cwd = vnode; + + mutex_unlock(&context->io_mutex); + + if (oldDirectory) + put_vnode(oldDirectory); + + return FSSH_B_NO_ERROR; + +err: + put_vnode(vnode); + return status; +} + + +} // namespace FSShell + + +// #pragma mark - +// Calls from within the kernel + + +using namespace FSShell; + + +fssh_dev_t +_kern_mount(const char *path, const char *device, const char *fsName, + uint32_t flags, const char *args, fssh_size_t argsLength) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true); +} + + +fssh_status_t +_kern_unmount(const char *path, uint32_t flags) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return fs_unmount(pathBuffer.LockBuffer(), flags, true); +} + + +fssh_status_t +_kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info) +{ + if (info == NULL) + return FSSH_B_BAD_VALUE; + + return fs_read_info(device, info); +} + + +fssh_status_t +_kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) +{ + if (info == NULL) + return FSSH_B_BAD_VALUE; + + return fs_write_info(device, info, mask); +} + + +fssh_status_t +_kern_sync(void) +{ + // Note: _kern_sync() is also called from _user_sync() + int32_t cookie = 0; + fssh_dev_t device; + while ((device = fs_next_device(&cookie)) >= 0) { + fssh_status_t status = fs_sync(device); + if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE) + fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status)); + } + + return FSSH_B_OK; +} + + +fssh_dev_t +_kern_next_device(int32_t *_cookie) +{ + return fs_next_device(_cookie); +} + + +int +_kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms) +{ + if (openMode & FSSH_O_CREAT) + return file_create_entry_ref(device, inode, name, openMode, perms, true); + + return file_open_entry_ref(device, inode, name, openMode, true); +} + + +/** \brief Opens a node specified by a FD + path pair. + * + * At least one of \a fd and \a path must be specified. + * If only \a fd is given, the function opens the node identified by this + * FD. If only a path is given, this path is opened. If both are given and + * the path is absolute, \a fd is ignored; a relative path is reckoned off + * of the directory (!) identified by \a fd. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. May be \c NULL. + * \param openMode The open mode. + * \return A FD referring to the newly opened node, or an error code, + * if an error occurs. + */ + +int +_kern_open(int fd, const char *path, int openMode, int perms) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + if (openMode & FSSH_O_CREAT) + return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true); + + return file_open(fd, pathBuffer.LockBuffer(), openMode, true); +} + + +/** \brief Opens a directory specified by entry_ref or node_ref. + * + * The supplied name may be \c NULL, in which case directory identified + * by \a device and \a inode will be opened. Otherwise \a device and + * \a inode identify the parent directory of the directory to be opened + * and \a name its entry name. + * + * \param device If \a name is specified the ID of the device the parent + * directory of the directory to be opened resides on, otherwise + * the device of the directory itself. + * \param inode If \a name is specified the node ID of the parent + * directory of the directory to be opened, otherwise node ID of the + * directory itself. + * \param name The entry name of the directory to be opened. If \c NULL, + * the \a device + \a inode pair identify the node to be opened. + * \return The FD of the newly opened directory or an error code, if + * something went wrong. + */ + +int +_kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name) +{ + return dir_open_entry_ref(device, inode, name, true); +} + + +/** \brief Opens a directory specified by a FD + path pair. + * + * At least one of \a fd and \a path must be specified. + * If only \a fd is given, the function opens the directory identified by this + * FD. If only a path is given, this path is opened. If both are given and + * the path is absolute, \a fd is ignored; a relative path is reckoned off + * of the directory (!) identified by \a fd. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. May be \c NULL. + * \return A FD referring to the newly opened directory, or an error code, + * if an error occurs. + */ + +int +_kern_open_dir(int fd, const char *path) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return dir_open(fd, pathBuffer.LockBuffer(), true); +} + + +fssh_status_t +_kern_fcntl(int fd, int op, uint32_t argument) +{ + return common_fcntl(fd, op, argument, true); +} + + +fssh_status_t +_kern_fsync(int fd) +{ + return common_sync(fd, true); +} + + +fssh_status_t +_kern_lock_node(int fd) +{ + return common_lock_node(fd, true); +} + + +fssh_status_t +_kern_unlock_node(int fd) +{ + return common_unlock_node(fd, true); +} + + +fssh_status_t +_kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms) +{ + return dir_create_entry_ref(device, inode, name, perms, true); +} + + +/** \brief Creates a directory specified by a FD + path pair. + * + * \a path must always be specified (it contains the name of the new directory + * at least). If only a path is given, this path identifies the location at + * which the directory shall be created. If both \a fd and \a path are given and + * the path is absolute, \a fd is ignored; a relative path is reckoned off + * of the directory (!) identified by \a fd. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. Must not be \c NULL. + * \param perms The access permissions the new directory shall have. + * \return \c FSSH_B_OK, if the directory has been created successfully, another + * error code otherwise. + */ + +fssh_status_t +_kern_create_dir(int fd, const char *path, int perms) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return dir_create(fd, pathBuffer.LockBuffer(), perms, true); +} + + +fssh_status_t +_kern_remove_dir(int fd, const char *path) +{ + if (path) { + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return dir_remove(fd, pathBuffer.LockBuffer(), true); + } + + return dir_remove(fd, NULL, true); +} + + +/** \brief Reads the contents of a symlink referred to by a FD + path pair. + * + * At least one of \a fd and \a path must be specified. + * If only \a fd is given, the function the symlink to be read is the node + * identified by this FD. If only a path is given, this path identifies the + * symlink to be read. If both are given and the path is absolute, \a fd is + * ignored; a relative path is reckoned off of the directory (!) identified + * by \a fd. + * If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer + * will still be updated to reflect the required buffer size. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. May be \c NULL. + * \param buffer The buffer into which the contents of the symlink shall be + * written. + * \param _bufferSize A pointer to the size of the supplied buffer. + * \return The length of the link on success or an appropriate error code + */ + +fssh_status_t +_kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize) +{ + if (path) { + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return common_read_link(fd, pathBuffer.LockBuffer(), + buffer, _bufferSize, true); + } + + return common_read_link(fd, NULL, buffer, _bufferSize, true); +} + + +/** \brief Creates a symlink specified by a FD + path pair. + * + * \a path must always be specified (it contains the name of the new symlink + * at least). If only a path is given, this path identifies the location at + * which the symlink shall be created. If both \a fd and \a path are given and + * the path is absolute, \a fd is ignored; a relative path is reckoned off + * of the directory (!) identified by \a fd. + * + * \param fd The FD. May be < 0. + * \param toPath The absolute or relative path. Must not be \c NULL. + * \param mode The access permissions the new symlink shall have. + * \return \c FSSH_B_OK, if the symlink has been created successfully, another + * error code otherwise. + */ + +fssh_status_t +_kern_create_symlink(int fd, const char *path, const char *toPath, int mode) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + char *toBuffer = toPathBuffer.LockBuffer(); + + fssh_status_t status = check_path(toBuffer); + if (status < FSSH_B_OK) + return status; + + return common_create_symlink(fd, pathBuffer.LockBuffer(), + toBuffer, mode, true); +} + + +fssh_status_t +_kern_create_link(const char *path, const char *toPath) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return common_create_link(pathBuffer.LockBuffer(), + toPathBuffer.LockBuffer(), true); +} + + +/** \brief Removes an entry specified by a FD + path pair from its directory. + * + * \a path must always be specified (it contains at least the name of the entry + * to be deleted). If only a path is given, this path identifies the entry + * directly. If both \a fd and \a path are given and the path is absolute, + * \a fd is ignored; a relative path is reckoned off of the directory (!) + * identified by \a fd. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. Must not be \c NULL. + * \return \c FSSH_B_OK, if the entry has been removed successfully, another + * error code otherwise. + */ + +fssh_status_t +_kern_unlink(int fd, const char *path) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return common_unlink(fd, pathBuffer.LockBuffer(), true); +} + + +/** \brief Moves an entry specified by a FD + path pair to a an entry specified + * by another FD + path pair. + * + * \a oldPath and \a newPath must always be specified (they contain at least + * the name of the entry). If only a path is given, this path identifies the + * entry directly. If both a FD and a path are given and the path is absolute, + * the FD is ignored; a relative path is reckoned off of the directory (!) + * identified by the respective FD. + * + * \param oldFD The FD of the old location. May be < 0. + * \param oldPath The absolute or relative path of the old location. Must not + * be \c NULL. + * \param newFD The FD of the new location. May be < 0. + * \param newPath The absolute or relative path of the new location. Must not + * be \c NULL. + * \return \c FSSH_B_OK, if the entry has been moved successfully, another + * error code otherwise. + */ + +fssh_status_t +_kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath) +{ + KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1); + KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return common_rename(oldFD, oldPathBuffer.LockBuffer(), + newFD, newPathBuffer.LockBuffer(), true); +} + + +fssh_status_t +_kern_access(const char *path, int mode) +{ + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + return common_access(pathBuffer.LockBuffer(), mode, true); +} + + +/** \brief Reads stat data of an entity specified by a FD + path pair. + * + * If only \a fd is given, the stat operation associated with the type + * of the FD (node, attr, attr dir etc.) is performed. If only \a path is + * given, this path identifies the entry for whose node to retrieve the + * stat data. If both \a fd and \a path are given and the path is absolute, + * \a fd is ignored; a relative path is reckoned off of the directory (!) + * identified by \a fd and specifies the entry whose stat data shall be + * retrieved. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. Must not be \c NULL. + * \param traverseLeafLink If \a path is given, \c true specifies that the + * function shall not stick to symlinks, but traverse them. + * \param stat The buffer the stat data shall be written into. + * \param statSize The size of the supplied stat buffer. + * \return \c FSSH_B_OK, if the the stat data have been read successfully, another + * error code otherwise. + */ + +fssh_status_t +_kern_read_stat(int fd, const char *path, bool traverseLeafLink, + struct fssh_stat *stat, fssh_size_t statSize) +{ + struct fssh_stat completeStat; + struct fssh_stat *originalStat = NULL; + fssh_status_t status; + + if (statSize > sizeof(struct fssh_stat)) + return FSSH_B_BAD_VALUE; + + // this supports different stat extensions + if (statSize < sizeof(struct fssh_stat)) { + originalStat = stat; + stat = &completeStat; + } + + if (path) { + // path given: get the stat of the node referred to by (fd, path) + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + status = common_path_read_stat(fd, pathBuffer.LockBuffer(), + traverseLeafLink, stat, true); + } else { + // no path given: get the FD and use the FD operation + struct file_descriptor *descriptor + = get_fd(get_current_io_context(true), fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + if (descriptor->ops->fd_read_stat) + status = descriptor->ops->fd_read_stat(descriptor, stat); + else + status = FSSH_EOPNOTSUPP; + + put_fd(descriptor); + } + + if (status == FSSH_B_OK && originalStat != NULL) + fssh_memcpy(originalStat, stat, statSize); + + return status; +} + + +/** \brief Writes stat data of an entity specified by a FD + path pair. + * + * If only \a fd is given, the stat operation associated with the type + * of the FD (node, attr, attr dir etc.) is performed. If only \a path is + * given, this path identifies the entry for whose node to write the + * stat data. If both \a fd and \a path are given and the path is absolute, + * \a fd is ignored; a relative path is reckoned off of the directory (!) + * identified by \a fd and specifies the entry whose stat data shall be + * written. + * + * \param fd The FD. May be < 0. + * \param path The absolute or relative path. Must not be \c NULL. + * \param traverseLeafLink If \a path is given, \c true specifies that the + * function shall not stick to symlinks, but traverse them. + * \param stat The buffer containing the stat data to be written. + * \param statSize The size of the supplied stat buffer. + * \param statMask A mask specifying which parts of the stat data shall be + * written. + * \return \c FSSH_B_OK, if the the stat data have been written successfully, + * another error code otherwise. + */ + +fssh_status_t +_kern_write_stat(int fd, const char *path, bool traverseLeafLink, + const struct fssh_stat *stat, fssh_size_t statSize, int statMask) +{ + struct fssh_stat completeStat; + + if (statSize > sizeof(struct fssh_stat)) + return FSSH_B_BAD_VALUE; + + // this supports different stat extensions + if (statSize < sizeof(struct fssh_stat)) { + fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(struct fssh_stat) - statSize); + fssh_memcpy(&completeStat, stat, statSize); + stat = &completeStat; + } + + fssh_status_t status; + + if (path) { + // path given: write the stat of the node referred to by (fd, path) + KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + status = common_path_write_stat(fd, pathBuffer.LockBuffer(), + traverseLeafLink, stat, statMask, true); + } else { + // no path given: get the FD and use the FD operation + struct file_descriptor *descriptor + = get_fd(get_current_io_context(true), fd); + if (descriptor == NULL) + return FSSH_B_FILE_ERROR; + + if (descriptor->ops->fd_write_stat) + status = descriptor->ops->fd_write_stat(descriptor, stat, statMask); + else + status = FSSH_EOPNOTSUPP; + + put_fd(descriptor); + } + + return status; +} + + +int +_kern_open_attr_dir(int fd, const char *path) +{ + KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + if (path != NULL) + pathBuffer.SetTo(path); + + return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true); +} + + +int +_kern_create_attr(int fd, const char *name, uint32_t type, int openMode) +{ + return attr_create(fd, name, type, openMode, true); +} + + +int +_kern_open_attr(int fd, const char *name, int openMode) +{ + return attr_open(fd, name, openMode, true); +} + + +fssh_status_t +_kern_remove_attr(int fd, const char *name) +{ + return attr_remove(fd, name, true); +} + + +fssh_status_t +_kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName) +{ + return attr_rename(fromFile, fromName, toFile, toName, true); +} + + +int +_kern_open_index_dir(fssh_dev_t device) +{ + return index_dir_open(device, true); +} + + +fssh_status_t +_kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags) +{ + return index_create(device, name, type, flags, true); +} + + +fssh_status_t +_kern_read_index_stat(fssh_dev_t device, const char *name, struct fssh_stat *stat) +{ + return index_name_read_stat(device, name, stat, true); +} + + +fssh_status_t +_kern_remove_index(fssh_dev_t device, const char *name) +{ + return index_remove(device, name, true); +} + + +fssh_status_t +_kern_getcwd(char *buffer, fssh_size_t size) +{ + TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size)); + + // Call vfs to get current working directory + return get_cwd(buffer, size, true); +} + + +fssh_status_t +_kern_setcwd(int fd, const char *path) +{ + KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); + if (pathBuffer.InitCheck() != FSSH_B_OK) + return FSSH_B_NO_MEMORY; + + if (path != NULL) + pathBuffer.SetTo(path); + + return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true); +} + diff --git a/src/tools/fs_shell/vfs.h b/src/tools/fs_shell/vfs.h new file mode 100644 index 0000000000..ff6229d8c8 --- /dev/null +++ b/src/tools/fs_shell/vfs.h @@ -0,0 +1,111 @@ +/* + * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ +#ifndef _FSSH_VFS_H +#define _FSSH_VFS_H + + +#include "fssh_fs_interface.h" +#include "list.h" +#include "lock.h" + + +namespace FSShell { + + +/* R5 figures, but we don't use a table for monitors anyway */ +#define DEFAULT_FD_TABLE_SIZE 128 +#define MAX_FD_TABLE_SIZE 8192 +#define DEFAULT_NODE_MONITORS 4096 +#define MAX_NODE_MONITORS 65536 + +struct kernel_args; +struct vm_cache_ref; +struct file_descriptor; + + +/** The I/O context of a process/team, holds the fd array among others */ +typedef struct io_context { + struct vnode *cwd; + mutex io_mutex; + uint32_t table_size; + uint32_t num_used_fds; + struct file_descriptor **fds; + uint8_t *fds_close_on_exec; +} io_context; + +struct fd_info { + int number; + int32_t open_mode; + fssh_dev_t device; + fssh_ino_t node; +}; + +/* macro to allocate a iovec array on the stack */ +#define IOVECS(name, size) \ + uint8_t _##name[sizeof(fssh_iovecs) + (size)*sizeof(fssh_iovec)]; \ + fssh_iovecs *name = (fssh_iovecs *)_##name + + +fssh_status_t vfs_init(struct kernel_args *args); +fssh_status_t vfs_bootstrap_file_systems(void); +void vfs_mount_boot_file_system(struct kernel_args *args); +void vfs_exec_io_context(void *context); +void* vfs_new_io_context(void *parentContext); +fssh_status_t vfs_free_io_context(void *context); + +/* calls needed by the VM for paging and by the file cache */ +int vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode); +fssh_status_t vfs_get_vnode_from_path(const char *path, bool kernel, void **vnode); +fssh_status_t vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, + void **_vnode); +fssh_status_t vfs_entry_ref_to_vnode(fssh_mount_id mountID, + fssh_vnode_id directoryID, const char *name, void **_vnode); +void vfs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, + fssh_vnode_id *_vnodeID); + +fssh_status_t vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, + void **_vnode); +void vfs_put_vnode(void *vnode); +void vfs_acquire_vnode(void *vnode); +fssh_status_t vfs_get_cookie_from_fd(int fd, void **_cookie); +fssh_status_t vfs_get_file_map(void *_vnode, fssh_off_t offset, + fssh_size_t size, fssh_file_io_vec *vecs, + fssh_size_t *_count); +fssh_status_t vfs_get_fs_node_from_path(fssh_mount_id mountID, + const char *path, bool kernel, void **_node); +fssh_status_t vfs_stat_vnode(void *_vnode, struct fssh_stat *stat); +fssh_status_t vfs_get_vnode_name(void *vnode, char *name, + fssh_size_t nameSize); +fssh_status_t vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID); +void vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor); +fssh_status_t vfs_disconnect_vnode(fssh_mount_id mountID, + fssh_vnode_id vnodeID); +void vfs_free_unused_vnodes(int32_t level); + +/* special module convenience call */ +fssh_status_t vfs_get_module_path(const char *basePath, + const char *moduleName, char *pathBuffer, + fssh_size_t bufferSize); + +/* service call for whoever needs a normalized path */ +fssh_status_t vfs_normalize_path(const char *path, char *buffer, + fssh_size_t bufferSize, bool kernel); + +/* service call for the node monitor */ +fssh_status_t resolve_mount_point_to_volume_root(fssh_mount_id mountID, + fssh_vnode_id nodeID, fssh_mount_id *resolvedMountID, + fssh_vnode_id *resolvedNodeID); + +// cache initialization functions defined in the respective cache implementation +extern fssh_status_t block_cache_init(); +extern fssh_status_t file_cache_init(); + +} // namespace FSShell + + +#endif /* _FSSH_VFS_H */