summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/haiku/SharedFolders
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Additions/haiku/SharedFolders
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Additions/haiku/SharedFolders')
-rw-r--r--src/VBox/Additions/haiku/SharedFolders/Makefile.kmk89
-rw-r--r--src/VBox/Additions/haiku/SharedFolders/OpenHashTable.h515
-rw-r--r--src/VBox/Additions/haiku/SharedFolders/kernel_cpp.h122
-rw-r--r--src/VBox/Additions/haiku/SharedFolders/lock.h315
-rw-r--r--src/VBox/Additions/haiku/SharedFolders/vboxsf.c1055
-rw-r--r--src/VBox/Additions/haiku/SharedFolders/vboxsf.h120
-rw-r--r--src/VBox/Additions/haiku/SharedFolders/vnode_cache.cpp144
7 files changed, 2360 insertions, 0 deletions
diff --git a/src/VBox/Additions/haiku/SharedFolders/Makefile.kmk b/src/VBox/Additions/haiku/SharedFolders/Makefile.kmk
new file mode 100644
index 00000000..daa5d002
--- /dev/null
+++ b/src/VBox/Additions/haiku/SharedFolders/Makefile.kmk
@@ -0,0 +1,89 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for shared folders, Haiku Guest Additions.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+#
+# This code is based on:
+#
+# VirtualBox Guest Additions for Haiku.
+# Copyright (c) 2011 Mike Smith <mike@scgtrp.net>
+# François Revol <revol@free.fr>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+ifdef VBOX_WITH_ADDITION_DRIVERS
+ SYSMODS += vboxsf
+endif
+
+#
+# Populate FILES_VBOXSF_NOBIN
+#
+#include $(PATH_SUB_CURRENT)/files_vboxsf
+
+#
+# The module (for syntax checking).
+# The DEBUG_HASH* stuff is for CONFIG_DYNAMIC_DEBUG-enabled kernels
+#
+vboxsf_TEMPLATE = VBoxGuestR0Drv
+vboxsf_DEFS = \
+ MODULE IN_RT_R0 VBOXGUEST VBOX_WITH_HGCM \
+ KBUILD_MODNAME=KBUILD_STR\(vboxsf\) \
+ KBUILD_BASENAME=KBUILD_STR\(vboxsf\)
+vboxsf_INCS = \
+ $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest \
+ $(PATH_ROOT)/src/VBox/Runtime/r0drv/haiku
+vboxsf_SOURCES = \
+ vboxsf.c \
+ vnode_cache.cpp \
+ $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c
+vboxsf_LIBS = \
+ $(VBOX_LIB_VBGL_R0)
+
+include $(KBUILD_PATH)/subfooter.kmk
+
diff --git a/src/VBox/Additions/haiku/SharedFolders/OpenHashTable.h b/src/VBox/Additions/haiku/SharedFolders/OpenHashTable.h
new file mode 100644
index 00000000..ac01704d
--- /dev/null
+++ b/src/VBox/Additions/haiku/SharedFolders/OpenHashTable.h
@@ -0,0 +1,515 @@
+/* $Id: OpenHashTable.h $ */
+/** @file
+ * OpenHashTable, Haiku Guest Additions.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * VirtualBox Guest Additions for Haiku.
+ *
+ * Copyright 2007, Hugo Santos. All Rights Reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+#ifndef GA_INCLUDED_SRC_haiku_SharedFolders_OpenHashTable_h
+#define GA_INCLUDED_SRC_haiku_SharedFolders_OpenHashTable_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+#include <OS.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _KERNEL_MODE
+# include <KernelExport.h>
+# include "kernel_cpp.h"
+#endif
+
+/*!
+ The Definition template must have four methods: `HashKey', `Hash',
+ `Compare' and `GetLink;. It must also define several types as shown in the
+ following example:
+
+ struct Foo {
+ int bar;
+
+ Foo* fNext;
+ };
+
+ struct HashTableDefinition {
+ typedef int KeyType;
+ typedef Foo ValueType;
+
+ size_t HashKey(int key) const
+ {
+ return key >> 1;
+ }
+
+ size_t Hash(Foo* value) const
+ {
+ return HashKey(value->bar);
+ }
+
+ bool Compare(int key, Foo* value) const
+ {
+ return value->bar == key;
+ }
+
+ Foo*& GetLink(Foo* value) const
+ {
+ return value->fNext;
+ }
+ };
+*/
+
+
+struct MallocAllocator
+{
+ void* Allocate(size_t size) const
+ {
+ return malloc(size);
+ }
+
+ void Free(void* memory) const
+ {
+ free(memory);
+ }
+};
+
+
+template<typename Definition, bool AutoExpand = true,
+ bool CheckDuplicates = false, typename Allocator = MallocAllocator>
+class BOpenHashTable
+{
+public:
+ typedef BOpenHashTable<Definition, AutoExpand, CheckDuplicates> HashTable;
+ typedef typename Definition::KeyType KeyType;
+ typedef typename Definition::ValueType ValueType;
+
+ static const size_t kMinimumSize = 8;
+
+ // All allocations are of power of 2 lengths.
+
+ // regrowth factor: 200 / 256 = 78.125%
+ // 50 / 256 = 19.53125%
+
+ BOpenHashTable()
+ :
+ fTableSize(0),
+ fItemCount(0),
+ fTable(NULL)
+ {
+ }
+
+ BOpenHashTable(const Definition& definition)
+ :
+ fDefinition(definition),
+ fTableSize(0),
+ fItemCount(0),
+ fTable(NULL)
+ {
+ }
+
+ BOpenHashTable(const Definition& definition, const Allocator& allocator)
+ :
+ fDefinition(definition),
+ fAllocator(allocator),
+ fTableSize(0),
+ fItemCount(0),
+ fTable(NULL)
+ {
+ }
+
+ ~BOpenHashTable()
+ {
+ fAllocator.Free(fTable);
+ }
+
+ status_t Init(size_t initialSize = kMinimumSize)
+ {
+ if (initialSize > 0 && !_Resize(initialSize))
+ return B_NO_MEMORY;
+ return B_OK;
+ }
+
+ size_t TableSize() const
+ {
+ return fTableSize;
+ }
+
+ size_t CountElements() const
+ {
+ return fItemCount;
+ }
+
+ ValueType* Lookup(const KeyType& key) const
+ {
+ if (fTableSize == 0)
+ return NULL;
+
+ size_t index = fDefinition.HashKey(key) & (fTableSize - 1);
+ ValueType* slot = fTable[index];
+
+ while (slot)
+ {
+ if (fDefinition.Compare(key, slot))
+ break;
+ slot = _Link(slot);
+ }
+
+ return slot;
+ }
+
+ status_t Insert(ValueType* value)
+ {
+ if (fTableSize == 0)
+ {
+ if (!_Resize(kMinimumSize))
+ return B_NO_MEMORY;
+ }
+ else if (AutoExpand && fItemCount >= (fTableSize * 200 / 256))
+ _Resize(fTableSize * 2);
+
+ InsertUnchecked(value);
+ return B_OK;
+ }
+
+ void InsertUnchecked(ValueType* value)
+ {
+ if (CheckDuplicates && _ExhaustiveSearch(value))
+ {
+#ifdef _KERNEL_MODE
+ panic("Hash Table: value already in table.");
+#else
+ debugger("Hash Table: value already in table.");
+#endif
+ }
+
+ _Insert(fTable, fTableSize, value);
+ fItemCount++;
+ }
+
+ // TODO: a ValueType* Remove(const KeyType& key) method is missing
+
+ bool Remove(ValueType* value)
+ {
+ if (!RemoveUnchecked(value))
+ return false;
+
+ if (AutoExpand && fTableSize > kMinimumSize
+ && fItemCount < (fTableSize * 50 / 256))
+ _Resize(fTableSize / 2);
+
+ return true;
+ }
+
+ bool RemoveUnchecked(ValueType* value)
+ {
+ size_t index = fDefinition.Hash(value) & (fTableSize - 1);
+ ValueType* previous = NULL;
+ ValueType* slot = fTable[index];
+
+ while (slot)
+ {
+ ValueType* next = _Link(slot);
+
+ if (value == slot)
+ {
+ if (previous)
+ _Link(previous) = next;
+ else
+ fTable[index] = next;
+ break;
+ }
+
+ previous = slot;
+ slot = next;
+ }
+
+ if (slot == NULL)
+ return false;
+
+ if (CheckDuplicates && _ExhaustiveSearch(value))
+ {
+#ifdef _KERNEL_MODE
+ panic("Hash Table: duplicate detected.");
+#else
+ debugger("Hash Table: duplicate detected.");
+#endif
+ }
+
+ fItemCount--;
+ return true;
+ }
+
+ /*! Removes all elements from the hash table. No resizing happens. The
+ elements are not deleted. If \a returnElements is \c true, the method
+ returns all elements chained via their hash table link.
+ */
+ ValueType* Clear(bool returnElements = false)
+ {
+ if (this->fItemCount == 0)
+ return NULL;
+
+ ValueType* result = NULL;
+
+ if (returnElements)
+ {
+ ValueType** nextPointer = &result;
+
+ // iterate through all buckets
+ for (size_t i = 0; i < fTableSize; i++)
+ {
+ ValueType* element = fTable[i];
+ if (element != NULL)
+ {
+ // add the bucket to the list
+ *nextPointer = element;
+
+ // update nextPointer to point to the fNext of the last
+ // element in the bucket
+ while (element != NULL)
+ {
+ nextPointer = &_Link(element);
+ element = *nextPointer;
+ }
+ }
+ }
+ }
+
+ memset(this->fTable, 0, sizeof(ValueType*) * this->fTableSize);
+
+ return result;
+ }
+
+ /*! If the table needs resizing, the number of bytes for the required
+ allocation is returned. If no resizing is needed, 0 is returned.
+ */
+ size_t ResizeNeeded() const
+ {
+ size_t size = fTableSize;
+ if (size == 0 || fItemCount >= (size * 200 / 256))
+ {
+ // grow table
+ if (size == 0)
+ size = kMinimumSize;
+ while (fItemCount >= size * 200 / 256)
+ size <<= 1;
+ }
+ else if (size > kMinimumSize && fItemCount < size * 50 / 256)
+ {
+ // shrink table
+ while (fItemCount < size * 50 / 256)
+ size >>= 1;
+ if (size < kMinimumSize)
+ size = kMinimumSize;
+ }
+
+ if (size == fTableSize)
+ return 0;
+
+ return size * sizeof(ValueType*);
+ }
+
+ /*! Resizes the table using the given allocation. The allocation must not
+ be \c NULL. It must be of size \a size, which must a value returned
+ earlier by ResizeNeeded(). If the size requirements have changed in the
+ meantime, the method free()s the given allocation and returns \c false,
+ unless \a force is \c true, in which case the supplied allocation is
+ used in any event.
+ Otherwise \c true is returned.
+ If \a oldTable is non-null and resizing is successful, the old table
+ will not be freed, but will be returned via this parameter instead.
+ */
+ bool Resize(void* allocation, size_t size, bool force = false,
+ void** oldTable = NULL)
+ {
+ if (!force && size != ResizeNeeded())
+ {
+ fAllocator.Free(allocation);
+ return false;
+ }
+
+ _Resize((ValueType**)allocation, size / sizeof(ValueType*), oldTable);
+ return true;
+ }
+
+ class Iterator
+ {
+ public:
+ Iterator(const HashTable* table)
+ : fTable(table)
+ {
+ Rewind();
+ }
+
+ Iterator(const HashTable* table, size_t index, ValueType* value)
+ : fTable(table), fIndex(index), fNext(value) {}
+
+ bool HasNext() const { return fNext != NULL; }
+
+ ValueType* Next()
+ {
+ ValueType* current = fNext;
+ _GetNext();
+ return current;
+ }
+
+ void Rewind()
+ {
+ // get the first one
+ fIndex = 0;
+ fNext = NULL;
+ _GetNext();
+ }
+
+ protected:
+ Iterator() {}
+
+ void _GetNext()
+ {
+ if (fNext)
+ fNext = fTable->_Link(fNext);
+
+ while (fNext == NULL && fIndex < fTable->fTableSize)
+ fNext = fTable->fTable[fIndex++];
+ }
+
+ const HashTable* fTable;
+ size_t fIndex;
+ ValueType* fNext;
+ };
+
+ Iterator GetIterator() const
+ {
+ return Iterator(this);
+ }
+
+ Iterator GetIterator(const KeyType& key) const
+ {
+ if (fTableSize == 0)
+ return Iterator(this, fTableSize, NULL);
+
+ size_t index = fDefinition.HashKey(key) & (fTableSize - 1);
+ ValueType* slot = fTable[index];
+
+ while (slot)
+ {
+ if (fDefinition.Compare(key, slot))
+ break;
+ slot = _Link(slot);
+ }
+
+ if (slot == NULL)
+ return Iterator(this, fTableSize, NULL);
+
+ return Iterator(this, index + 1, slot);
+ }
+
+protected:
+ // for g++ 2.95
+ friend class Iterator;
+
+ void _Insert(ValueType** table, size_t tableSize, ValueType* value)
+ {
+ size_t index = fDefinition.Hash(value) & (tableSize - 1);
+
+ _Link(value) = table[index];
+ table[index] = value;
+ }
+
+ bool _Resize(size_t newSize)
+ {
+ ValueType** newTable
+ = (ValueType**)fAllocator.Allocate(sizeof(ValueType*) * newSize);
+ if (newTable == NULL)
+ return false;
+
+ _Resize(newTable, newSize);
+ return true;
+ }
+
+ void _Resize(ValueType** newTable, size_t newSize, void** _oldTable = NULL)
+ {
+ for (size_t i = 0; i < newSize; i++)
+ newTable[i] = NULL;
+
+ if (fTable)
+ {
+ for (size_t i = 0; i < fTableSize; i++)
+ {
+ ValueType* bucket = fTable[i];
+ while (bucket)
+ {
+ ValueType* next = _Link(bucket);
+ _Insert(newTable, newSize, bucket);
+ bucket = next;
+ }
+ }
+
+ if (_oldTable != NULL)
+ *_oldTable = fTable;
+ else
+ fAllocator.Free(fTable);
+ }
+ else if (_oldTable != NULL)
+ *_oldTable = NULL;
+
+ fTableSize = newSize;
+ fTable = newTable;
+ }
+
+ ValueType*& _Link(ValueType* bucket) const
+ {
+ return fDefinition.GetLink(bucket);
+ }
+
+ bool _ExhaustiveSearch(ValueType* value) const
+ {
+ for (size_t i = 0; i < fTableSize; i++)
+ {
+ ValueType* bucket = fTable[i];
+ while (bucket) {
+ if (bucket == value)
+ return true;
+ bucket = _Link(bucket);
+ }
+ }
+
+ return false;
+ }
+
+ Definition fDefinition;
+ Allocator fAllocator;
+ size_t fTableSize;
+ size_t fItemCount;
+ ValueType** fTable;
+};
+
+#endif /* !GA_INCLUDED_SRC_haiku_SharedFolders_OpenHashTable_h */
+
diff --git a/src/VBox/Additions/haiku/SharedFolders/kernel_cpp.h b/src/VBox/Additions/haiku/SharedFolders/kernel_cpp.h
new file mode 100644
index 00000000..7cc3093f
--- /dev/null
+++ b/src/VBox/Additions/haiku/SharedFolders/kernel_cpp.h
@@ -0,0 +1,122 @@
+/* $Id: kernel_cpp.h $ */
+/** @file
+ * Kernel C++, Haiku private.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * VirtualBox Guest Additions for Haiku. C++ in the kernel.
+ *
+ * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
+ * Distributed under the terms of the MIT License.
+ */
+
+#ifndef GA_INCLUDED_SRC_haiku_SharedFolders_kernel_cpp_h
+#define GA_INCLUDED_SRC_haiku_SharedFolders_kernel_cpp_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef __cplusplus
+
+#include <new>
+#include <stdlib.h>
+
+#if _KERNEL_MODE || _LOADER_MODE
+
+using namespace std;
+extern const nothrow_t std::nothrow;
+
+// We need new() versions we can use when also linking against libgcc.
+// std::nothrow can't be used since it's defined in both libgcc and
+// kernel_cpp.cpp.
+typedef struct {} mynothrow_t;
+extern const mynothrow_t mynothrow;
+
+
+inline void *
+operator new(size_t size) throw (std::bad_alloc)
+{
+ // we don't actually throw any exceptions, but we have to
+ // keep the prototype as specified in <new>, or else GCC 3
+ // won't like us
+ return malloc(size);
+}
+
+
+inline void *
+operator new[](size_t size) throw (std::bad_alloc)
+{
+ return malloc(size);
+}
+
+
+inline void *
+operator new(size_t size, const std::nothrow_t &) throw ()
+{
+ return malloc(size);
+}
+
+
+inline void *
+operator new[](size_t size, const std::nothrow_t &) throw ()
+{
+ return malloc(size);
+}
+
+
+inline void *
+operator new(size_t size, const mynothrow_t &) throw ()
+{
+ return malloc(size);
+}
+
+
+inline void *
+operator new[](size_t size, const mynothrow_t &) throw ()
+{
+ return malloc(size);
+}
+
+
+inline void
+operator delete(void *ptr) throw ()
+{
+ free(ptr);
+}
+
+
+inline void
+operator delete[](void *ptr) throw ()
+{
+ free(ptr);
+}
+
+#endif // #if _KERNEL_MODE
+
+#endif // __cplusplus
+
+#endif /* !GA_INCLUDED_SRC_haiku_SharedFolders_kernel_cpp_h */
diff --git a/src/VBox/Additions/haiku/SharedFolders/lock.h b/src/VBox/Additions/haiku/SharedFolders/lock.h
new file mode 100644
index 00000000..4bca6a3f
--- /dev/null
+++ b/src/VBox/Additions/haiku/SharedFolders/lock.h
@@ -0,0 +1,315 @@
+/* $Id: lock.h $ */
+/** @file
+ * Lock.h - Haiku, private locking internals.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * VirtualBox Guest Additions for Haiku.
+ *
+ * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
+ * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
+ * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+#ifndef GA_INCLUDED_SRC_haiku_SharedFolders_lock_h
+#define GA_INCLUDED_SRC_haiku_SharedFolders_lock_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <OS.h>
+
+
+struct mutex_waiter;
+
+typedef struct mutex {
+ const char* name;
+ struct mutex_waiter* waiters;
+#if KDEBUG
+ thread_id holder;
+#else
+ int32 count;
+ uint16 ignore_unlock_count;
+#endif
+ uint8 flags;
+} mutex;
+
+#define MUTEX_FLAG_CLONE_NAME 0x1
+
+
+typedef struct recursive_lock {
+ mutex lock;
+#if !KDEBUG
+ thread_id holder;
+#endif
+ int recursion;
+} recursive_lock;
+
+
+struct rw_lock_waiter;
+
+typedef struct rw_lock {
+ const char* name;
+ struct rw_lock_waiter* waiters;
+ thread_id holder;
+ vint32 count;
+ int32 owner_count;
+ int16 active_readers;
+ // Only > 0 while a writer is waiting: number
+ // of active readers when the first waiting
+ // writer started waiting.
+ int16 pending_readers;
+ // Number of readers that have already
+ // incremented "count", but have not yet started
+ // to wait at the time the last writer unlocked.
+ uint32 flags;
+} rw_lock;
+
+#define RW_LOCK_WRITER_COUNT_BASE 0x10000
+
+#define RW_LOCK_FLAG_CLONE_NAME 0x1
+
+
+#if KDEBUG
+# define KDEBUG_RW_LOCK_DEBUG 0
+ // Define to 1 if you want to use ASSERT_READ_LOCKED_RW_LOCK().
+ // The rw_lock will just behave like a recursive locker then.
+# define ASSERT_LOCKED_RECURSIVE(r) \
+ { ASSERT(find_thread(NULL) == (r)->lock.holder); }
+# define ASSERT_LOCKED_MUTEX(m) { ASSERT(find_thread(NULL) == (m)->holder); }
+# define ASSERT_WRITE_LOCKED_RW_LOCK(l) \
+ { ASSERT(find_thread(NULL) == (l)->holder); }
+# if KDEBUG_RW_LOCK_DEBUG
+# define ASSERT_READ_LOCKED_RW_LOCK(l) \
+ { ASSERT(find_thread(NULL) == (l)->holder); }
+# else
+# define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
+# endif
+#else
+# define ASSERT_LOCKED_RECURSIVE(r) do {} while (false)
+# define ASSERT_LOCKED_MUTEX(m) do {} while (false)
+# define ASSERT_WRITE_LOCKED_RW_LOCK(m) do {} while (false)
+# define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
+#endif
+
+
+// static initializers
+#if KDEBUG
+# define MUTEX_INITIALIZER(name) { name, NULL, -1, 0 }
+# define RECURSIVE_LOCK_INITIALIZER(name) { MUTEX_INITIALIZER(name), 0 }
+#else
+# define MUTEX_INITIALIZER(name) { name, NULL, 0, 0, 0 }
+# define RECURSIVE_LOCK_INITIALIZER(name) { MUTEX_INITIALIZER(name), -1, 0 }
+#endif
+
+#define RW_LOCK_INITIALIZER(name) { name, NULL, -1, 0, 0, 0 }
+
+
+#if KDEBUG
+# define RECURSIVE_LOCK_HOLDER(recursiveLock) ((recursiveLock)->lock.holder)
+#else
+# define RECURSIVE_LOCK_HOLDER(recursiveLock) ((recursiveLock)->holder)
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void recursive_lock_init(recursive_lock *lock, const char *name);
+ // name is *not* cloned nor freed in recursive_lock_destroy()
+extern void recursive_lock_init_etc(recursive_lock *lock, const char *name,
+ uint32 flags);
+extern void recursive_lock_destroy(recursive_lock *lock);
+extern status_t recursive_lock_lock(recursive_lock *lock);
+extern status_t recursive_lock_trylock(recursive_lock *lock);
+extern void recursive_lock_unlock(recursive_lock *lock);
+extern int32 recursive_lock_get_recursion(recursive_lock *lock);
+
+extern void rw_lock_init(rw_lock* lock, const char* name);
+ // name is *not* cloned nor freed in rw_lock_destroy()
+extern void rw_lock_init_etc(rw_lock* lock, const char* name, uint32 flags);
+extern void rw_lock_destroy(rw_lock* lock);
+extern status_t rw_lock_write_lock(rw_lock* lock);
+
+extern void mutex_init(mutex* lock, const char* name);
+ // name is *not* cloned nor freed in mutex_destroy()
+extern void mutex_init_etc(mutex* lock, const char* name, uint32 flags);
+extern void mutex_destroy(mutex* lock);
+extern status_t mutex_switch_lock(mutex* from, mutex* to);
+ // Unlocks "from" and locks "to" such that unlocking and starting to wait
+ // for the lock is atomically. I.e. if "from" guards the object "to" belongs
+ // to, the operation is safe as long as "from" is held while destroying
+ // "to".
+extern status_t mutex_switch_from_read_lock(rw_lock* from, mutex* to);
+ // Like mutex_switch_lock(), just for a switching from a read-locked
+ // rw_lock.
+
+
+// implementation private:
+
+extern status_t _rw_lock_read_lock(rw_lock* lock);
+extern status_t _rw_lock_read_lock_with_timeout(rw_lock* lock,
+ uint32 timeoutFlags, bigtime_t timeout);
+extern void _rw_lock_read_unlock(rw_lock* lock, bool threadsLocked);
+extern void _rw_lock_write_unlock(rw_lock* lock, bool threadsLocked);
+
+extern status_t _mutex_lock(mutex* lock, bool threadsLocked);
+extern void _mutex_unlock(mutex* lock, bool threadsLocked);
+extern status_t _mutex_trylock(mutex* lock);
+extern status_t _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags,
+ bigtime_t timeout);
+
+
+static inline status_t
+rw_lock_read_lock(rw_lock* lock)
+{
+#if KDEBUG_RW_LOCK_DEBUG
+ return rw_lock_write_lock(lock);
+#else
+ int32 oldCount = atomic_add(&lock->count, 1);
+ if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
+ return _rw_lock_read_lock(lock);
+ return B_OK;
+#endif
+}
+
+
+static inline status_t
+rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
+ bigtime_t timeout)
+{
+#if KDEBUG_RW_LOCK_DEBUG
+ return mutex_lock_with_timeout(lock, timeoutFlags, timeout);
+#else
+ int32 oldCount = atomic_add(&lock->count, 1);
+ if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
+ return _rw_lock_read_lock_with_timeout(lock, timeoutFlags, timeout);
+ return B_OK;
+#endif
+}
+
+
+static inline void
+rw_lock_read_unlock(rw_lock* lock)
+{
+#if KDEBUG_RW_LOCK_DEBUG
+ rw_lock_write_unlock(lock);
+#else
+ int32 oldCount = atomic_add(&lock->count, -1);
+ if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
+ _rw_lock_read_unlock(lock, false);
+#endif
+}
+
+
+static inline void
+rw_lock_write_unlock(rw_lock* lock)
+{
+ _rw_lock_write_unlock(lock, false);
+}
+
+
+static inline status_t
+mutex_lock(mutex* lock)
+{
+#if KDEBUG
+ return _mutex_lock(lock, false);
+#else
+ if (atomic_add(&lock->count, -1) < 0)
+ return _mutex_lock(lock, false);
+ return B_OK;
+#endif
+}
+
+
+static inline status_t
+mutex_lock_threads_locked(mutex* lock)
+{
+#if KDEBUG
+ return _mutex_lock(lock, true);
+#else
+ if (atomic_add(&lock->count, -1) < 0)
+ return _mutex_lock(lock, true);
+ return B_OK;
+#endif
+}
+
+
+static inline status_t
+mutex_trylock(mutex* lock)
+{
+#if KDEBUG
+ return _mutex_trylock(lock);
+#else
+ if (atomic_test_and_set(&lock->count, -1, 0) != 0)
+ return B_WOULD_BLOCK;
+ return B_OK;
+#endif
+}
+
+
+static inline status_t
+mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout)
+{
+#if KDEBUG
+ return _mutex_lock_with_timeout(lock, timeoutFlags, timeout);
+#else
+ if (atomic_add(&lock->count, -1) < 0)
+ return _mutex_lock_with_timeout(lock, timeoutFlags, timeout);
+ return B_OK;
+#endif
+}
+
+
+static inline void
+mutex_unlock(mutex* lock)
+{
+#if !KDEBUG
+ if (atomic_add(&lock->count, 1) < -1)
+#endif
+ _mutex_unlock(lock, false);
+}
+
+
+static inline void
+mutex_transfer_lock(mutex* lock, thread_id thread)
+{
+#if KDEBUG
+ lock->holder = thread;
+#endif
+}
+
+
+extern void lock_debug_init();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GA_INCLUDED_SRC_haiku_SharedFolders_lock_h */
diff --git a/src/VBox/Additions/haiku/SharedFolders/vboxsf.c b/src/VBox/Additions/haiku/SharedFolders/vboxsf.c
new file mode 100644
index 00000000..26182d42
--- /dev/null
+++ b/src/VBox/Additions/haiku/SharedFolders/vboxsf.c
@@ -0,0 +1,1055 @@
+/* $Id: vboxsf.c $ */
+/** @file
+ * Shared folders - Haiku Guest Additions, implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * VirtualBox Guest Additions for Haiku.
+ * Copyright (c) 2011 Mike Smith <mike@scgtrp.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "vboxsf.h"
+
+#define MODULE_NAME "file_systems/vboxsf"
+#define FS_NAME "vboxsf"
+#define FS_PRETTY_NAME "VirtualBox Shared Folders"
+
+VBGLSFCLIENT g_clientHandle;
+static fs_volume_ops vboxsf_volume_ops;
+static fs_vnode_ops vboxsf_vnode_ops;
+
+status_t init_module(void)
+{
+#if 0
+ /** @todo enable this soon */
+ int rc = get_module(VBOXGUEST_MODULE_NAME, (module_info **)&g_VBoxGuest);
+ if (RT_LIKELY(rc == B_OK)
+ {
+ rc = VbglR0SfInit();
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfConnect(&g_clientHandle);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfSetUtf8(&g_clientHandle);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfSetSymlinks(&g_clientHandle);
+ if (RT_FAILURE(rc))
+ LogRel((FS_NAME ": Warning! VbglR0SfSetSymlinks failed (rc=%d) - symlink will appear as copies.\n", rc));
+
+ mutex_init(&g_vnodeCacheLock, "vboxsf vnode cache lock");
+ Log((FS_NAME ": init_module succeeded.\n");
+ return B_OK;
+ }
+ else
+ LogRel((FS_NAME ": VbglR0SfSetUtf8 failed. rc=%d\n", rc));
+ }
+ else
+ LogRel((FS_NAME ": VbglR0SfConnect failed. rc=%d\n", rc));
+ }
+ else
+ LogRel((FS_NAME ": VbglR0SfInit failed. rc=%d\n", rc));
+ }
+ else
+ LogRel((FS_NAME ": get_module failed for '%s'. rc=%d\n", VBOXGUEST_MODULE_NAME, rc));
+
+ return B_ERROR;
+#endif
+
+ if (get_module(VBOXGUEST_MODULE_NAME, (module_info **)&g_VBoxGuest) != B_OK)
+ {
+ dprintf("get_module(%s) failed\n", VBOXGUEST_MODULE_NAME);
+ return B_ERROR;
+ }
+
+ if (RT_FAILURE(VbglR0SfInit()))
+ {
+ dprintf("VbglR0SfInit failed\n");
+ return B_ERROR;
+ }
+
+ if (RT_FAILURE(VbglR0SfConnect(&g_clientHandle)))
+ {
+ dprintf("VbglR0SfConnect failed\n");
+ return B_ERROR;
+ }
+
+ if (RT_FAILURE(VbglR0SfSetUtf8(&g_clientHandle)))
+ {
+ dprintf("VbglR0SfSetUtf8 failed\n");
+ return B_ERROR;
+ }
+
+ if (RT_FAILURE(VbglR0SfSetSymlinks(&g_clientHandle)))
+ {
+ dprintf("warning: VbglR0SfSetSymlinks failed (old vbox?) - symlinks will appear as copies\n");
+ }
+
+ mutex_init(&g_vnodeCacheLock, "vboxsf vnode cache lock");
+
+ dprintf(FS_NAME ": inited successfully\n");
+ return B_OK;
+}
+
+void uninit_module(void)
+{
+ mutex_destroy(&g_vnodeCacheLock);
+ put_module(VBOXGUEST_MODULE_NAME);
+}
+
+
+PSHFLSTRING make_shflstring(const char* const s)
+{
+ int len = strlen(s);
+ if (len > 0xFFFE)
+ {
+ dprintf(FS_NAME ": make_shflstring: string too long\n");
+ return NULL;
+ }
+
+ PSHFLSTRING rv = malloc(sizeof(SHFLSTRING) + len);
+ if (!rv)
+ return NULL;
+
+ rv->u16Length = len;
+ rv->u16Size = len + 1;
+ strcpy(rv->String.utf8, s);
+ return rv;
+}
+
+
+PSHFLSTRING clone_shflstring(PSHFLSTRING s)
+{
+ PSHFLSTRING rv = malloc(sizeof(SHFLSTRING) + s->u16Length);
+ if (rv)
+ memcpy(rv, s, sizeof(SHFLSTRING) + s->u16Length);
+ return rv;
+}
+
+PSHFLSTRING concat_shflstring_cstr(PSHFLSTRING s1, const char* const s2)
+{
+ size_t s2len = strlen(s2);
+ PSHFLSTRING rv = malloc(sizeof(SHFLSTRING) + s1->u16Length + s2len);
+ if (rv)
+ {
+ memcpy(rv, s1, sizeof(SHFLSTRING) + s1->u16Length);
+ strcat(rv->String.utf8, s2);
+ rv->u16Length += s2len;
+ rv->u16Size += s2len;
+ }
+ return rv;
+}
+
+
+PSHFLSTRING concat_cstr_shflstring(const char* const s1, PSHFLSTRING s2)
+{
+ size_t s1len = strlen(s1);
+ PSHFLSTRING rv = malloc(sizeof(SHFLSTRING) + s1len + s2->u16Length);
+ if (rv)
+ {
+ strcpy(rv->String.utf8, s1);
+ strcat(rv->String.utf8, s2->String.utf8);
+ rv->u16Length = s1len + s2->u16Length;
+ rv->u16Size = rv->u16Length + 1;
+ }
+ return rv;
+}
+
+
+PSHFLSTRING build_path(vboxsf_vnode* dir, const char* const name)
+{
+ dprintf("*** build_path(%p, %p)\n", dir, name);
+ if (!dir || !name)
+ return NULL;
+
+ size_t len = dir->path->u16Length + strlen(name) + 1;
+ PSHFLSTRING rv = malloc(sizeof(SHFLSTRING) + len);
+ if (rv)
+ {
+ strcpy(rv->String.utf8, dir->path->String.utf8);
+ strcat(rv->String.utf8, "/");
+ strcat(rv->String.utf8, name);
+ rv->u16Length = len;
+ rv->u16Size = rv->u16Length + 1;
+ }
+ return rv;
+}
+
+
+status_t mount(fs_volume *volume, const char *device, uint32 flags, const char *args, ino_t *_rootVnodeID)
+{
+ if (device)
+ {
+ dprintf(FS_NAME ": trying to mount a real device as a vbox share is silly\n");
+ return B_BAD_TYPE;
+ }
+
+ dprintf(FS_NAME ": mount(%s)\n", args);
+
+ PSHFLSTRING sharename = make_shflstring(args);
+
+ vboxsf_volume* vbsfvolume = malloc(sizeof(vboxsf_volume));
+ volume->private_volume = vbsfvolume;
+ int rv = VbglR0SfMapFolder(&g_clientHandle, sharename, &(vbsfvolume->map));
+ free(sharename);
+
+ if (rv == 0)
+ {
+ vboxsf_vnode* root_vnode;
+
+ PSHFLSTRING name = make_shflstring("");
+ if (!name)
+ {
+ dprintf(FS_NAME ": make_shflstring() failed\n");
+ return B_NO_MEMORY;
+ }
+
+ status_t rs = vboxsf_new_vnode(&vbsfvolume->map, name, name, &root_vnode);
+ dprintf(FS_NAME ": allocated %p (path=%p name=%p)\n", root_vnode, root_vnode->path, root_vnode->name);
+
+ if (rs != B_OK)
+ {
+ dprintf(FS_NAME ": vboxsf_new_vnode() failed (%d)\n", (int)rs);
+ return rs;
+ }
+
+ rs = publish_vnode(volume, root_vnode->vnode, root_vnode, &vboxsf_vnode_ops, S_IFDIR, 0);
+ dprintf(FS_NAME ": publish_vnode(): %d\n", (int)rs);
+ *_rootVnodeID = root_vnode->vnode;
+ volume->ops = &vboxsf_volume_ops;
+ return B_OK;
+ }
+ else
+ {
+ dprintf(FS_NAME ": VbglR0SfMapFolder failed (%d)\n", rv);
+ free(volume->private_volume);
+ return vbox_err_to_haiku_err(rv);
+ }
+}
+
+
+status_t unmount(fs_volume *volume)
+{
+ dprintf(FS_NAME ": unmount\n");
+ VbglR0SfUnmapFolder(&g_clientHandle, volume->private_volume);
+ return B_OK;
+}
+
+
+status_t vboxsf_read_stat(fs_volume* _volume, fs_vnode* _vnode, struct stat* st)
+{
+ vboxsf_vnode* vnode = _vnode->private_node;
+ vboxsf_volume* volume = _volume->private_volume;
+ SHFLCREATEPARMS params;
+ int rc;
+
+ dprintf("vboxsf_read_stat (_vnode=%p, vnode=%p, path=%p (%s))\n", _vnode, vnode, vnode->path->String.utf8, vnode->path->String.utf8);
+
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
+ dprintf("sf_stat: calling VbglR0SfCreate, file %s, flags %x\n", vnode->path->String.utf8, params.CreateFlags);
+ rc = VbglR0SfCreate(&g_clientHandle, &volume->map, vnode->path, &params);
+ if (rc == VERR_INVALID_NAME)
+ {
+ /* this can happen for names like 'foo*' on a Windows host */
+ return B_ENTRY_NOT_FOUND;
+ }
+ if (RT_FAILURE(rc))
+ {
+ dprintf("VbglR0SfCreate: %d\n", params.Result);
+ return vbox_err_to_haiku_err(params.Result);
+ }
+ if (params.Result != SHFL_FILE_EXISTS)
+ {
+ dprintf("VbglR0SfCreate: %d\n", params.Result);
+ return B_ENTRY_NOT_FOUND;
+ }
+
+ st->st_dev = 0;
+ st->st_ino = vnode->vnode;
+ st->st_mode = mode_from_fmode(params.Info.Attr.fMode);
+ st->st_nlink = 1;
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+ st->st_size = params.Info.cbObject;
+ st->st_blksize = 1;
+ st->st_blocks = params.Info.cbAllocated;
+ st->st_atime = RTTimeSpecGetSeconds(&params.Info.AccessTime);
+ st->st_mtime = RTTimeSpecGetSeconds(&params.Info.ModificationTime);
+ st->st_ctime = RTTimeSpecGetSeconds(&params.Info.BirthTime);
+ return B_OK;
+}
+
+
+status_t vboxsf_open_dir(fs_volume* _volume, fs_vnode* _vnode, void** _cookie)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_vnode* vnode = _vnode->private_node;
+ SHFLCREATEPARMS params;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;
+
+ int rc = VbglR0SfCreate(&g_clientHandle, &volume->map, vnode->path, &params);
+ if (RT_SUCCESS(rc))
+ {
+ if (params.Result == SHFL_FILE_EXISTS && params.Handle != SHFL_HANDLE_NIL)
+ {
+ vboxsf_dir_cookie* cookie = malloc(sizeof(vboxsf_dir_cookie));
+ *_cookie = cookie;
+ cookie->index = 0;
+ cookie->path = build_path(vnode, "*");
+ cookie->handle = params.Handle;
+ cookie->has_more_files = true;
+ cookie->buffer_start = cookie->buffer = NULL;
+ cookie->buffer_length = cookie->num_files = 0;
+ return B_OK;
+ }
+ else
+ return B_ENTRY_NOT_FOUND;
+ }
+ else
+ {
+ dprintf(FS_NAME ": VbglR0SfCreate: %d\n", rc);
+ return vbox_err_to_haiku_err(rc);
+ }
+}
+
+
+/** read a single entry from a dir */
+status_t vboxsf_read_dir_1(vboxsf_volume* volume, vboxsf_vnode* vnode, vboxsf_dir_cookie* cookie,
+ struct dirent* buffer, size_t bufferSize)
+{
+ dprintf("%p, %d, %p\n", cookie, cookie->has_more_files, cookie->buffer);
+ if (!cookie->has_more_files)
+ return B_ENTRY_NOT_FOUND;
+
+ if (!cookie->buffer)
+ {
+ cookie->buffer_length = 16384;
+ cookie->buffer_start = cookie->buffer = malloc(cookie->buffer_length);
+
+ int rc = VbglR0SfDirInfo(&g_clientHandle, &volume->map, cookie->handle, cookie->path, 0, cookie->index,
+ &cookie->buffer_length, cookie->buffer, &cookie->num_files);
+
+ if (rc != 0 && rc != VERR_NO_MORE_FILES)
+ {
+ dprintf(FS_NAME ": VbglR0SfDirInfo failed: %d\n", rc);
+ free(cookie->buffer_start);
+ cookie->buffer_start = NULL;
+ return vbox_err_to_haiku_err(rc);
+ }
+
+ if (rc == VERR_NO_MORE_FILES)
+ {
+ free(cookie->buffer_start);
+ cookie->buffer_start = NULL;
+ cookie->has_more_files = false;
+ return B_ENTRY_NOT_FOUND;
+ }
+ }
+
+ if (bufferSize <= sizeof(struct dirent) + cookie->buffer->name.u16Length)
+ {
+ dprintf("hit end of buffer\n");
+ return B_BUFFER_OVERFLOW;
+ }
+
+ PSHFLSTRING name1 = clone_shflstring(&cookie->buffer->name);
+ if (!name1)
+ {
+ dprintf(FS_NAME ": make_shflstring() failed\n");
+ return B_NO_MEMORY;
+ }
+
+ vboxsf_vnode* new_vnode;
+ int rv = vboxsf_new_vnode(&volume->map, build_path(vnode, name1->String.utf8), name1, &new_vnode);
+ if (rv != B_OK)
+ {
+ dprintf(FS_NAME ": vboxsf_new_vnode() failed\n");
+ return rv;
+ }
+
+ buffer->d_dev = 0;
+ buffer->d_pdev = 0;
+ buffer->d_ino = new_vnode->vnode;
+ buffer->d_pino = vnode->vnode;
+ buffer->d_reclen = sizeof(struct dirent) + cookie->buffer->name.u16Length;
+ strncpy(buffer->d_name, cookie->buffer->name.String.utf8, NAME_MAX);
+
+ size_t size = offsetof(SHFLDIRINFO, name.String) + cookie->buffer->name.u16Size;
+ cookie->buffer = ((void*)cookie->buffer + size);
+ cookie->index++;
+
+ if (cookie->index >= cookie->num_files)
+ {
+ // hit end of this buffer, next call will reallocate a new one
+ free(cookie->buffer_start);
+ cookie->buffer_start = cookie->buffer = NULL;
+ }
+ return B_OK;
+}
+
+
+status_t vboxsf_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
+ struct dirent* buffer, size_t bufferSize, uint32* _num)
+{
+ vboxsf_dir_cookie* cookie = _cookie;
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_vnode* vnode = _vnode->private_node;
+ uint32 num_read = 0;
+ status_t rv = B_OK;
+
+ for (num_read = 0; num_read < *_num && cookie->has_more_files; num_read++)
+ {
+ rv = vboxsf_read_dir_1(volume, vnode, cookie, buffer, bufferSize);
+ if (rv == B_BUFFER_OVERFLOW || rv == B_ENTRY_NOT_FOUND)
+ {
+ // hit end of at least one of the buffers - not really an error
+ rv = B_OK;
+ break;
+ }
+ bufferSize -= buffer->d_reclen;
+ buffer = ((void*)(buffer)) + buffer->d_reclen;
+ }
+
+ *_num = num_read;
+ return rv;
+}
+
+
+status_t vboxsf_free_dir_cookie(fs_volume* _volume, fs_vnode* vnode, void* _cookie)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_dir_cookie* cookie = _cookie;
+
+ VbglR0SfClose(&g_clientHandle, &volume->map, cookie->handle);
+ free(cookie->path);
+ free(cookie);
+
+ return B_OK;
+}
+
+
+status_t vboxsf_read_fs_info(fs_volume* _volume, struct fs_info* info)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+
+ SHFLVOLINFO volume_info;
+ uint32_t bytes = sizeof(SHFLVOLINFO);
+
+ int rc = VbglR0SfFsInfo(&g_clientHandle, &volume->map, 0, SHFL_INFO_GET | SHFL_INFO_VOLUME,
+ &bytes, (PSHFLDIRINFO)&volume_info);
+ if (RT_FAILURE(rc))
+ {
+ dprintf(FS_NAME ": VbglR0SfFsInfo failed (%d)\n", rc);
+ return vbox_err_to_haiku_err(rc);
+ }
+
+ info->flags = B_FS_IS_PERSISTENT;
+ if (volume_info.fsProperties.fReadOnly)
+ info->flags |= B_FS_IS_READONLY;
+
+ info->dev = 0;
+ info->root = 1;
+ info->block_size = volume_info.ulBytesPerAllocationUnit;
+ info->io_size = volume_info.ulBytesPerAllocationUnit;
+ info->total_blocks = volume_info.ullTotalAllocationBytes / info->block_size;
+ info->free_blocks = volume_info.ullAvailableAllocationBytes / info->block_size;
+ info->total_nodes = LONGLONG_MAX;
+ info->free_nodes = LONGLONG_MAX;
+ strcpy(info->volume_name, "VBox share");
+ return B_OK;
+}
+
+
+status_t vboxsf_lookup(fs_volume* _volume, fs_vnode* dir, const char* name, ino_t* _id)
+{
+ dprintf(FS_NAME ": lookup %s\n", name);
+ vboxsf_volume* volume = _volume->private_volume;
+ SHFLCREATEPARMS params;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
+
+ PSHFLSTRING path = build_path(dir->private_node, name);
+ if (!path)
+ {
+ dprintf(FS_NAME ": make_shflstring() failed\n");
+ return B_NO_MEMORY;
+ }
+
+ int rc = VbglR0SfCreate(&g_clientHandle, &volume->map, path, &params);
+ if (RT_SUCCESS(rc))
+ {
+ if (params.Result == SHFL_FILE_EXISTS)
+ {
+ vboxsf_vnode* vn;
+ status_t rv = vboxsf_new_vnode(&volume->map, path, path, &vn);
+ if (rv == B_OK)
+ {
+ *_id = vn->vnode;
+ rv = publish_vnode(_volume, vn->vnode, vn, &vboxsf_vnode_ops, mode_from_fmode(params.Info.Attr.fMode), 0);
+ }
+ return rv;
+ }
+ else
+ {
+ free(path);
+ return B_ENTRY_NOT_FOUND;
+ }
+ }
+ else
+ {
+ free(path);
+ dprintf(FS_NAME ": VbglR0SfCreate: %d\n", rc);
+ return vbox_err_to_haiku_err(rc);
+ }
+}
+
+
+mode_t mode_from_fmode(RTFMODE fMode)
+{
+ mode_t m = 0;
+
+ if (RTFS_IS_DIRECTORY(fMode))
+ m |= S_IFDIR;
+ else if (RTFS_IS_FILE(fMode))
+ m |= S_IFREG;
+ else if (RTFS_IS_FIFO(fMode))
+ m |= S_IFIFO;
+ else if (RTFS_IS_DEV_CHAR(fMode))
+ m |= S_IFCHR;
+ else if (RTFS_IS_DEV_BLOCK(fMode))
+ m |= S_IFBLK;
+ else if (RTFS_IS_SYMLINK(fMode))
+ m |= S_IFLNK;
+ else if (RTFS_IS_SOCKET(fMode))
+ m |= S_IFSOCK;
+
+ if (fMode & RTFS_UNIX_IRUSR)
+ m |= S_IRUSR;
+ if (fMode & RTFS_UNIX_IWUSR)
+ m |= S_IWUSR;
+ if (fMode & RTFS_UNIX_IXUSR)
+ m |= S_IXUSR;
+ if (fMode & RTFS_UNIX_IRGRP)
+ m |= S_IRGRP;
+ if (fMode & RTFS_UNIX_IWGRP)
+ m |= S_IWGRP;
+ if (fMode & RTFS_UNIX_IXGRP)
+ m |= S_IXGRP;
+ if (fMode & RTFS_UNIX_IROTH)
+ m |= S_IROTH;
+ if (fMode & RTFS_UNIX_IWOTH)
+ m |= S_IWOTH;
+ if (fMode & RTFS_UNIX_IXOTH)
+ m |= S_IXOTH;
+ if (fMode & RTFS_UNIX_ISUID)
+ m |= S_ISUID;
+ if (fMode & RTFS_UNIX_ISGID)
+ m |= S_ISGID;
+ if (fMode & RTFS_UNIX_ISTXT)
+ m |= S_ISVTX;
+
+ return m;
+}
+
+
+status_t vboxsf_open(fs_volume* _volume, fs_vnode* _vnode, int openMode, void** _cookie)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_vnode* vnode = _vnode->private_node;
+
+ dprintf(FS_NAME ": open %s (mode=%x)\n", vnode->path->String.utf8, openMode);
+
+ SHFLCREATEPARMS params;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+
+ if (openMode & O_RDWR)
+ params.CreateFlags |= SHFL_CF_ACCESS_READWRITE;
+ else if (openMode & O_RDONLY)
+ params.CreateFlags |= SHFL_CF_ACCESS_READ;
+ else if (openMode & O_WRONLY)
+ params.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+
+ if (openMode & O_APPEND)
+ params.CreateFlags |= SHFL_CF_ACCESS_APPEND;
+
+ if (openMode & O_CREAT)
+ {
+ params.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
+ if (openMode & O_EXCL)
+ params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_EXISTS;
+ else if (openMode & O_TRUNC)
+ params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
+ else
+ params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ }
+ else
+ {
+ params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+ if (openMode & O_TRUNC)
+ params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
+ else
+ params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ }
+
+ int rc = VbglR0SfCreate(&g_clientHandle, &volume->map, vnode->path, &params);
+ if (!RT_SUCCESS(rc))
+ {
+ dprintf("VbglR0SfCreate returned %d\n", rc);
+ return vbox_err_to_haiku_err(rc);
+ }
+
+ vboxsf_file_cookie* cookie = malloc(sizeof(vboxsf_file_cookie));
+ if (!cookie)
+ {
+ dprintf("couldn't allocate file cookie\n");
+ return B_NO_MEMORY;
+ }
+
+ cookie->handle = params.Handle;
+ cookie->path = vnode->path;
+
+ *_cookie = cookie;
+
+ return B_OK;
+}
+
+
+status_t vboxsf_create(fs_volume* _volume, fs_vnode* _dir, const char *name, int openMode, int perms, void **_cookie, ino_t *_newVnodeID)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+
+ SHFLCREATEPARMS params;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+
+ if (openMode & O_RDWR)
+ params.CreateFlags |= SHFL_CF_ACCESS_READWRITE;
+ else if (openMode & O_RDONLY)
+ params.CreateFlags |= SHFL_CF_ACCESS_READ;
+ else if (openMode & O_WRONLY)
+ params.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+
+ if (openMode & O_APPEND)
+ params.CreateFlags |= SHFL_CF_ACCESS_APPEND;
+
+ if (openMode & O_CREAT)
+ {
+ params.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
+ if (openMode & O_EXCL)
+ params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_EXISTS;
+ else if (openMode & O_TRUNC)
+ params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
+ else
+ params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ }
+ else
+ {
+ params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+ if (openMode & O_TRUNC)
+ params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
+ else
+ params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ }
+
+ PSHFLSTRING path = build_path(_dir->private_node, name);
+ int rc = VbglR0SfCreate(&g_clientHandle, &volume->map, path, &params);
+
+ if (!RT_SUCCESS(rc))
+ {
+ dprintf("VbglR0SfCreate returned %d\n", rc);
+ free(path);
+ return vbox_err_to_haiku_err(rc);
+ }
+
+ vboxsf_file_cookie* cookie = malloc(sizeof(vboxsf_file_cookie));
+ if (!cookie)
+ {
+ dprintf("couldn't allocate file cookie\n");
+ free(path);
+ return B_NO_MEMORY;
+ }
+
+ cookie->handle = params.Handle;
+ cookie->path = path;
+
+ *_cookie = cookie;
+ return vboxsf_lookup(_volume, _dir, name, _newVnodeID);
+}
+
+
+status_t vboxsf_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_file_cookie* cookie = _cookie;
+
+ int rc = VbglR0SfClose(&g_clientHandle, &volume->map, cookie->handle);
+ dprintf("VbglR0SfClose returned %d\n", rc);
+ return vbox_err_to_haiku_err(rc);
+}
+
+
+status_t vboxsf_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
+{
+ vboxsf_dir_cookie* cookie = _cookie;
+ cookie->index = 0;
+ return B_OK;
+}
+
+
+status_t vboxsf_close_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
+{
+ return B_OK;
+}
+
+
+status_t vboxsf_free_cookie(fs_volume *volume, fs_vnode *vnode, void *_cookie)
+{
+ vboxsf_dir_cookie* cookie = _cookie;
+ free(cookie);
+ return B_OK;
+}
+
+status_t vboxsf_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, void *buffer, size_t *length)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_vnode* vnode = _vnode->private_node;
+ vboxsf_file_cookie* cookie = _cookie;
+
+ if (*length > 0xFFFFFFFF)
+ *length = 0xFFFFFFFF;
+
+ uint32_t l = *length;
+ void* other_buffer = malloc(l); /** @todo map the user memory into kernel space here for efficiency */
+ int rc = VbglR0SfRead(&g_clientHandle, &volume->map, cookie->handle, pos, &l, other_buffer, false /*fLocked*/);
+ memcpy(buffer, other_buffer, l);
+ free(other_buffer);
+
+ dprintf("VbglR0SfRead returned %d\n", rc);
+ *length = l;
+ return vbox_err_to_haiku_err(rc);
+}
+
+
+status_t vboxsf_write(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, off_t pos, const void *buffer, size_t *length)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_vnode* vnode = _vnode->private_node;
+ vboxsf_file_cookie* cookie = _cookie;
+
+ if (*length > 0xFFFFFFFF)
+ *length = 0xFFFFFFFF;
+
+ uint32_t l = *length;
+ void* other_buffer = malloc(l); /** @todo map the user memory into kernel space here for efficiency */
+ memcpy(other_buffer, buffer, l);
+ int rc = VbglR0SfWrite(&g_clientHandle, &volume->map, cookie->handle, pos, &l, other_buffer, false /*fLocked*/);
+ free(other_buffer);
+
+ *length = l;
+ return vbox_err_to_haiku_err(rc);
+}
+
+
+status_t vboxsf_write_stat(fs_volume *volume, fs_vnode *vnode, const struct stat *stat, uint32 statMask)
+{
+ /* The host handles updating the stat info - in the guest, this is a no-op */
+ return B_OK;
+}
+
+
+status_t vboxsf_create_dir(fs_volume *_volume, fs_vnode *parent, const char *name, int perms)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+
+ SHFLCREATEPARMS params;
+ params.Handle = 0;
+ params.Info.cbObject = 0;
+ params.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_CREATE_IF_NEW |
+ SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACCESS_READ;
+
+ PSHFLSTRING path = build_path(parent->private_node, name);
+ int rc = VbglR0SfCreate(&g_clientHandle, &volume->map, path, &params);
+ free(path);
+ /** @todo r=ramshankar: we should perhaps also check rc here and change
+ * Handle initialization from 0 to SHFL_HANDLE_NIL. */
+ if (params.Handle == SHFL_HANDLE_NIL)
+ return vbox_err_to_haiku_err(rc);
+ VbglR0SfClose(&g_clientHandle, &volume->map, params.Handle);
+ return B_OK;
+}
+
+
+status_t vboxsf_remove_dir(fs_volume *_volume, fs_vnode *parent, const char *name)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+
+ PSHFLSTRING path = build_path(parent->private_node, name);
+ int rc = VbglR0SfRemove(&g_clientHandle, &volume->map, path, SHFL_REMOVE_DIR);
+ free(path);
+
+ return vbox_err_to_haiku_err(rc);
+}
+
+
+status_t vboxsf_unlink(fs_volume *_volume, fs_vnode *parent, const char *name)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+
+ PSHFLSTRING path = build_path(parent->private_node, name);
+ int rc = VbglR0SfRemove(&g_clientHandle, &volume->map, path, SHFL_REMOVE_FILE);
+ free(path);
+
+ return vbox_err_to_haiku_err(rc);
+}
+
+status_t vboxsf_link(fs_volume *volume, fs_vnode *dir, const char *name, fs_vnode *vnode)
+{
+ return B_UNSUPPORTED;
+}
+
+
+status_t vboxsf_rename(fs_volume* _volume, fs_vnode* fromDir, const char* fromName, fs_vnode* toDir, const char* toName)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+
+ PSHFLSTRING oldpath = build_path(fromDir->private_node, fromName);
+ PSHFLSTRING newpath = build_path(toDir->private_node, toName);
+ int rc = VbglR0SfRename(&g_clientHandle, &volume->map, oldpath, newpath, SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS);
+ free(oldpath);
+ free(newpath);
+
+ return vbox_err_to_haiku_err(rc);
+}
+
+
+status_t vboxsf_create_symlink(fs_volume* _volume, fs_vnode* dir, const char* name, const char* path, int mode)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+
+ PSHFLSTRING target = make_shflstring(path);
+ PSHFLSTRING linkpath = build_path(dir->private_node, name);
+ SHFLFSOBJINFO stuff;
+ RT_ZERO(stuff);
+
+ int rc = VbglR0SfSymlink(&g_clientHandle, &volume->map, linkpath, target, &stuff);
+
+ free(target);
+ free(linkpath);
+
+ return vbox_err_to_haiku_err(rc);
+}
+
+
+status_t vboxsf_read_symlink(fs_volume* _volume, fs_vnode* link, char* buffer, size_t* _bufferSize)
+{
+ vboxsf_volume* volume = _volume->private_volume;
+ vboxsf_vnode* vnode = link->private_node;
+
+ int rc = VbglR0SfReadLink(&g_clientHandle, &volume->map, vnode->path, *_bufferSize, buffer);
+ *_bufferSize = strlen(buffer);
+
+ return vbox_err_to_haiku_err(rc);
+}
+
+
+/** @todo move this into the runtime */
+status_t vbox_err_to_haiku_err(int rc)
+{
+ switch (rc)
+ {
+ case VINF_SUCCESS: return B_OK;
+ case VERR_INVALID_POINTER: return B_BAD_ADDRESS;
+ case VERR_INVALID_PARAMETER: return B_BAD_VALUE;
+ case VERR_PERMISSION_DENIED: return B_PERMISSION_DENIED;
+ case VERR_NOT_IMPLEMENTED: return B_UNSUPPORTED;
+ case VERR_FILE_NOT_FOUND: return B_ENTRY_NOT_FOUND;
+
+ case SHFL_PATH_NOT_FOUND:
+ case SHFL_FILE_NOT_FOUND: return B_ENTRY_NOT_FOUND;
+ case SHFL_FILE_EXISTS: return B_FILE_EXISTS;
+
+ default:
+ return B_ERROR;
+ }
+}
+
+
+static status_t std_ops(int32 op, ...)
+{
+ switch(op)
+ {
+ case B_MODULE_INIT:
+ dprintf(MODULE_NAME ": B_MODULE_INIT\n");
+ return init_module();
+ case B_MODULE_UNINIT:
+ dprintf(MODULE_NAME ": B_MODULE_UNINIT\n");
+ uninit_module();
+ return B_OK;
+ default:
+ return B_ERROR;
+ }
+}
+
+
+static fs_volume_ops vboxsf_volume_ops =
+{
+ unmount,
+ vboxsf_read_fs_info,
+ NULL, /* write_fs_info */
+ NULL, /* sync */
+ vboxsf_get_vnode,
+ NULL, /* open_index_dir */
+ NULL, /* close_index_dir */
+ NULL, /* free_index_dir_cookie */
+ NULL, /* read_index_dir */
+ NULL, /* rewind_index_dir */
+ NULL, /* create_index */
+ NULL, /* remove_index */
+ NULL, /* read_index_stat */
+ NULL, /* open_query */
+ NULL, /* close_query */
+ NULL, /* free_query_cookie */
+ NULL, /* read_query */
+ NULL, /* rewind_query */
+ NULL, /* all_layers_mounted */
+ NULL, /* create_sub_vnode */
+ NULL, /* delete_sub_vnode */
+};
+
+static fs_vnode_ops vboxsf_vnode_ops =
+{
+ vboxsf_lookup,
+ NULL, /* get_vnode_name */
+ vboxsf_put_vnode,
+ NULL, /* remove_vnode */
+ NULL, /* can_page */
+ NULL, /* read_pages */
+ NULL, /* write_pages */
+ NULL, /* io */
+ NULL, /* cancel_io */
+ NULL, /* get_file_map */
+ NULL, /* ioctl */
+ NULL, /* set_flags */
+ NULL, /* select */
+ NULL, /* deselect */
+ NULL, /* fsync */
+ vboxsf_read_symlink,
+ vboxsf_create_symlink,
+ vboxsf_link,
+ vboxsf_unlink,
+ vboxsf_rename,
+ NULL, /* access */
+ vboxsf_read_stat,
+ vboxsf_write_stat,
+ NULL, /* preallocate */
+ vboxsf_create,
+ vboxsf_open,
+ vboxsf_close,
+ vboxsf_free_cookie,
+ vboxsf_read,
+ vboxsf_write,
+ vboxsf_create_dir,
+ vboxsf_remove_dir,
+ vboxsf_open_dir,
+ vboxsf_close_dir,
+ vboxsf_free_dir_cookie,
+ vboxsf_read_dir,
+ vboxsf_rewind_dir,
+ NULL, /* open_attr_dir */
+ NULL, /* close_attr_dir */
+ NULL, /* free_attr_dir_cookie */
+ NULL, /* read_attr_dir */
+ NULL, /* rewind_attr_dir */
+ NULL, /* create_attr */
+ NULL, /* open_attr */
+ NULL, /* close_attr */
+ NULL, /* free_attr_cookie */
+ NULL, /* read_attr */
+ NULL, /* write_attr */
+ NULL, /* read_attr_stat */
+ NULL, /* write_attr_stat */
+ NULL, /* rename_attr */
+ NULL, /* remove_attr */
+ NULL, /* create_special_node */
+ NULL, /* get_super_vnode */
+};
+
+static file_system_module_info sVBoxSharedFileSystem =
+{
+ {
+ MODULE_NAME B_CURRENT_FS_API_VERSION,
+ 0,
+ std_ops,
+ },
+ FS_NAME,
+ FS_PRETTY_NAME,
+ 0, /* DDM flags */
+ NULL, /* identify_partition */
+ NULL, /* scan_partition */
+ NULL, /* free_identify_partition_cookie */
+ NULL, /* free_partition_content_cookie */
+ mount,
+};
+
+module_info *modules[] =
+{
+ (module_info *)&sVBoxSharedFileSystem,
+ NULL,
+};
+
diff --git a/src/VBox/Additions/haiku/SharedFolders/vboxsf.h b/src/VBox/Additions/haiku/SharedFolders/vboxsf.h
new file mode 100644
index 00000000..d8449987
--- /dev/null
+++ b/src/VBox/Additions/haiku/SharedFolders/vboxsf.h
@@ -0,0 +1,120 @@
+/* $Id: vboxsf.h $ */
+/** @file
+ * Shared folders - Haiku Guest Additions, header.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * VirtualBox Guest Additions for Haiku.
+ * Copyright (c) 2011 Mike Smith <mike@scgtrp.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef GA_INCLUDED_SRC_haiku_SharedFolders_vboxsf_h
+#define GA_INCLUDED_SRC_haiku_SharedFolders_vboxsf_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <malloc.h>
+#include <dirent.h>
+#include <fs_info.h>
+#include <sys/stat.h>
+#include <fs_interface.h>
+#include <KernelExport.h>
+#include <VBoxGuest-haiku.h>
+#include <VBox/VBoxGuestLibSharedFolders.h>
+#include "lock.h"
+
+typedef struct vboxsf_volume
+{
+ VBGLSFMAP map;
+ ino_t rootid;
+} vboxsf_volume;
+
+typedef struct vboxsf_vnode
+{
+ PVBGLSFMAP map;
+ PSHFLSTRING name;
+ PSHFLSTRING path;
+ ino_t vnode;
+ struct vboxsf_vnode* next;
+} vboxsf_vnode;
+
+typedef struct vboxsf_dir_cookie
+{
+ SHFLHANDLE handle;
+ PSHFLSTRING path;
+ uint32_t index;
+ bool has_more_files;
+ PSHFLDIRINFO buffer_start, buffer;
+ uint32_t buffer_length, num_files;
+} vboxsf_dir_cookie;
+
+typedef struct vboxsf_file_cookie
+{
+ SHFLHANDLE handle;
+ PSHFLSTRING path;
+} vboxsf_file_cookie;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+status_t vboxsf_new_vnode(PVBGLSFMAP map, PSHFLSTRING path, PSHFLSTRING name, vboxsf_vnode** p);
+status_t vboxsf_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type, uint32* _flags, bool reenter);
+status_t vboxsf_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter);
+PSHFLSTRING make_shflstring(const char* const s);
+mode_t mode_from_fmode(RTFMODE fMode);
+status_t vbox_err_to_haiku_err(int rc);
+extern mutex g_vnodeCacheLock;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GA_INCLUDED_SRC_haiku_SharedFolders_vboxsf_h */
+
diff --git a/src/VBox/Additions/haiku/SharedFolders/vnode_cache.cpp b/src/VBox/Additions/haiku/SharedFolders/vnode_cache.cpp
new file mode 100644
index 00000000..05221606
--- /dev/null
+++ b/src/VBox/Additions/haiku/SharedFolders/vnode_cache.cpp
@@ -0,0 +1,144 @@
+/* $Id: vnode_cache.cpp $ */
+/** @file
+ * Shared folders - Haiku Guest Additions, vnode cache header.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * VirtualBox Guest Additions for Haiku.
+ * Copyright (c) 2011 Mike Smith <mike@scgtrp.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "vboxsf.h"
+#include "OpenHashTable.h"
+
+struct HashTableDefinition
+{
+ typedef uint32 KeyType;
+ typedef vboxsf_vnode ValueType;
+
+ size_t HashKey(uint32 key) const
+ {
+ return key;
+ }
+
+ size_t Hash(vboxsf_vnode* value) const
+ {
+ return HashKey(value->vnode);
+ }
+
+ bool Compare(uint32 key, vboxsf_vnode* value) const
+ {
+ return value->vnode == key;
+ }
+
+ vboxsf_vnode*& GetLink(vboxsf_vnode* value) const
+ {
+ return value->next;
+ }
+};
+
+static BOpenHashTable<HashTableDefinition> g_cache;
+static ino_t g_nextVnid = 1;
+mutex g_vnodeCacheLock;
+
+
+extern "C" status_t vboxsf_new_vnode(PVBSFMAP map, PSHFLSTRING path, PSHFLSTRING name, vboxsf_vnode** p)
+{
+ vboxsf_vnode* vn = (vboxsf_vnode*)malloc(sizeof(vboxsf_vnode));
+ if (vn == NULL)
+ return B_NO_MEMORY;
+
+ dprintf("creating new vnode at %p with path=%p (%s)\n", vn, path->String.utf8, path->String.utf8);
+ vn->map = map;
+ vn->path = path;
+ if (name)
+ vn->name = name;
+ else
+ {
+ const char* cname = strrchr((char*)path->String.utf8, '/');
+ if (!cname)
+ vn->name = path; // no slash, assume this *is* the filename
+ else
+ vn->name = make_shflstring(cname);
+ }
+
+ if (mutex_lock(&g_vnodeCacheLock) < B_OK)
+ {
+ free(vn);
+ return B_ERROR;
+ }
+
+ vn->vnode = g_nextVnid++;
+ *p = vn;
+ dprintf("vboxsf: allocated %p (path=%p name=%p)\n", vn, vn->path, vn->name);
+ status_t rv = g_cache.Insert(vn);
+
+ mutex_unlock(&g_vnodeCacheLock);
+
+ return rv;
+}
+
+
+extern "C" status_t vboxsf_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode,
+ int* _type, uint32* _flags, bool reenter)
+{
+ vboxsf_vnode* vn = g_cache.Lookup(id);
+ if (vn)
+ {
+ vnode->private_node = vn;
+ return B_OK;
+ }
+ return B_ERROR;
+}
+
+
+extern "C" status_t vboxsf_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
+{
+ g_cache.Remove((vboxsf_vnode*)vnode->private_node);
+}
+